* Re: [PATCH 1/2] serial/imx: add DMA support
From: Sascha Hauer @ 2012-04-26 12:00 UTC (permalink / raw)
To: Huang Shijie
Cc: alan, linux-serial, gregkh, shawn.guo, linux-arm-kernel, B20223,
r58066, B20596
In-Reply-To: <1335436632-29499-2-git-send-email-b32955@freescale.com>
On Thu, Apr 26, 2012 at 06:37:11PM +0800, Huang Shijie wrote:
> Add the DMA support for uart RX and TX.
>
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
> .../bindings/tty/serial/fsl-imx-uart.txt | 7 +
> drivers/tty/serial/imx.c | 386 +++++++++++++++++++-
> 2 files changed, 389 insertions(+), 4 deletions(-)
>
> + enum dma_status status;
> + unsigned long flags;
> + int ret;
> +
> + status = chan->device->device_tx_status(chan, (dma_cookie_t)NULL, NULL);
> + if (DMA_IN_PROGRESS == status)
> + return;
> +
> + spin_lock_irqsave(&sport->port.lock, flags);
> + sport->tx_bytes = uart_circ_chars_pending(xmit);
> + if (sport->tx_bytes > 0) {
Instead of putting nearly the whole body of this function inside a 'if'
you should return here.
> + if (xmit->tail > xmit->head) {
> + sport->dma_tx_nents = 2;
> + sg_init_table(sgl, 2);
> + sg_set_buf(sgl, xmit->buf + xmit->tail,
> + UART_XMIT_SIZE - xmit->tail);
> + sg_set_buf(&sgl[1], xmit->buf, xmit->head);
> + } else {
> + sport->dma_tx_nents = 1;
> + sg_init_one(sgl, xmit->buf + xmit->tail,
> + sport->tx_bytes);
> + }
> + spin_unlock_irqrestore(&sport->port.lock, flags);
> +
> + ret = dma_map_sg(sport->port.dev, sgl,
> + sport->dma_tx_nents, DMA_TO_DEVICE);
> + if (ret == 0) {
> + pr_err("DMA mapping error for TX.\n");
Use dev_* functions. Whoever reads the above in the logs won't have a
clue that it comes from the serial driver.
> + return;
> + }
> + desc = dmaengine_prep_slave_sg(chan, sgl,
> + sport->dma_tx_nents, DMA_MEM_TO_DEV, 0);
> + if (!desc) {
> + pr_err("We cannot prepare for the TX slave dma!\n");
> + return;
> + }
> + desc->callback = dma_tx_callback;
> + desc->callback_param = sport;
> +
> + /* fire it */
> + dmaengine_submit(desc);
> + dma_async_issue_pending(chan);
> + return;
> + }
> + spin_unlock_irqrestore(&sport->port.lock, flags);
> + return;
> +}
> +
> +/* see the "i.MX61 SDMA Scripts User Manual.doc" for the parameters */
I can't see how the manual helps here.
Please test this patch at least on one more SoC. There should be nothing
i.MX6 specific in here, the fact that the i.MX6 is mentioned several
times in the comments make me suspicious.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* Re: [PATCH 1/2] serial/imx: add DMA support
From: Russell King - ARM Linux @ 2012-04-26 11:11 UTC (permalink / raw)
To: Huang Shijie
Cc: alan, B20596, B20223, gregkh, r58066, linux-serial, shawn.guo,
s.hauer, linux-arm-kernel
In-Reply-To: <1335436632-29499-2-git-send-email-b32955@freescale.com>
On Thu, Apr 26, 2012 at 06:37:11PM +0800, Huang Shijie wrote:
> Add the DMA support for uart RX and TX.
>
> Signed-off-by: Huang Shijie <b32955@freescale.com>
> ---
Apart from the comments below,
1. How do you deal with transmitting the high-priority XON/XOFF
characters (port->x_char) which occur with s/w flow control and
the tty buffers fill up?
2. How do you deal with flow control in general? IOW, what happens
when the remote end deasserts your CTS with h/w flow control enabled.
How does your end deal with sending RTS according to flow control
conditions?
> .../bindings/tty/serial/fsl-imx-uart.txt | 7 +
> drivers/tty/serial/imx.c | 386 +++++++++++++++++++-
> 2 files changed, 389 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
> index a9c0406..f27489d 100644
> --- a/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
> +++ b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
> @@ -8,6 +8,10 @@ Required properties:
> Optional properties:
> - fsl,uart-has-rtscts : Indicate the uart has rts and cts
> - fsl,irda-mode : Indicate the uart supports irda mode
> +- fsl,enable-dma : Indicate the uart supports DMA
> +- fsl,uart-dma-events : contains the DMA events for RX and TX,
> + The first is the RX event, while the other is TX.
> +- fsl,enable-dte: Indicate the uart works in DTE mode
>
> Example:
>
> @@ -16,4 +20,7 @@ uart@73fbc000 {
> reg = <0x73fbc000 0x4000>;
> interrupts = <31>;
> fsl,uart-has-rtscts;
> + fsl,enable-dma;
> + fsl,uart-dma-events = <xx xx>;
> + fsl,enable-dte;
> };
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index e7fecee..65ba24d 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -47,9 +47,11 @@
> #include <linux/slab.h>
> #include <linux/of.h>
> #include <linux/of_device.h>
> +#include <linux/dma-mapping.h>
>
> #include <asm/io.h>
> #include <asm/irq.h>
> +#include <mach/dma.h>
> #include <mach/imx-uart.h>
>
> /* Register definitions */
> @@ -82,6 +84,7 @@
> #define UCR1_ADBR (1<<14) /* Auto detect baud rate */
> #define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */
> #define UCR1_IDEN (1<<12) /* Idle condition interrupt */
> +#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
> #define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
> #define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
> #define UCR1_IREN (1<<7) /* Infrared interface enable */
> @@ -125,6 +128,7 @@
> #define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */
> #define UCR4_WKEN (1<<7) /* Wake interrupt enable */
> #define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */
> +#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */
> #define UCR4_IRSC (1<<5) /* IR special case */
> #define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
> #define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
> @@ -134,6 +138,7 @@
> #define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
> #define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
> #define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
> +#define UFCR_DCEDTE (1<<6)
> #define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
> #define USR1_RTSS (1<<14) /* RTS pin status */
> #define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */
> @@ -200,12 +205,27 @@ struct imx_port {
> unsigned int old_status;
> int txirq,rxirq,rtsirq;
> unsigned int have_rtscts:1;
> + unsigned int enable_dte:1;
> + unsigned int enable_dma:1;
> unsigned int use_irda:1;
> unsigned int irda_inv_rx:1;
> unsigned int irda_inv_tx:1;
> unsigned short trcv_delay; /* transceiver delay */
> struct clk *clk;
> struct imx_uart_data *devdata;
> +
> + /* DMA fields */
> + unsigned int dma_req_rx;
> + unsigned int dma_req_tx;
> + struct imx_dma_data dma_data;
> + struct dma_chan *dma_chan_rx, *dma_chan_tx;
> + struct scatterlist rx_sgl, tx_sgl[2];
> + void *rx_buf;
> + unsigned int rx_bytes, tx_bytes;
> + struct work_struct tsk_dma_rx, tsk_dma_tx;
Why do you need a work struct to deal with DMA?
> + unsigned int dma_tx_nents;
> + bool dma_is_rxing;
> + wait_queue_head_t dma_wait;
> };
>
> struct imx_port_ucrs {
> @@ -394,6 +414,13 @@ static void imx_stop_rx(struct uart_port *port)
> struct imx_port *sport = (struct imx_port *)port;
> unsigned long temp;
>
> + /*
> + * We are maybe in the SMP now, so if the DMA RX thread is running,
> + * we have to wait for it to finish.
> + */
> + if (sport->enable_dma && sport->dma_is_rxing)
> + return;
> +
> temp = readl(sport->port.membase + UCR2);
> writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2);
> }
> @@ -429,6 +456,80 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
> imx_stop_tx(&sport->port);
> }
>
> +static void dma_tx_callback(void *data)
> +{
> + struct imx_port *sport = data;
> + struct scatterlist *sgl = &sport->tx_sgl[0];
struct scatterlist *sgl = sport->tx_sgl;
is equivalent, and less typing.
> + struct circ_buf *xmit = &sport->port.state->xmit;
> +
> + dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
> +
> + /* update the stat */
> + spin_lock(&sport->port.lock);
> + xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
> + sport->port.icount.tx += sport->tx_bytes;
> + spin_unlock(&sport->port.lock);
Callbacks are called from tasklet context, and will have IRQs enabled.
As the port lock is taken from IRQ context, this is waiting for a deadlock
to happen. Have you run this with lockdep enabled?
> +
> + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
> + uart_write_wakeup(&sport->port);
> + schedule_work(&sport->tsk_dma_tx);
> +}
> +
> +static void dma_tx_work(struct work_struct *w)
> +{
> + struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx);
> + struct circ_buf *xmit = &sport->port.state->xmit;
> + struct scatterlist *sgl = &sport->tx_sgl[0];
> + struct dma_async_tx_descriptor *desc;
> + struct dma_chan *chan = sport->dma_chan_tx;
> + enum dma_status status;
> + unsigned long flags;
> + int ret;
> +
> + status = chan->device->device_tx_status(chan, (dma_cookie_t)NULL, NULL);
Cookies aren't pointers. NULL is inappropriate here.
> + if (DMA_IN_PROGRESS == status)
> + return;
> +
> + spin_lock_irqsave(&sport->port.lock, flags);
> + sport->tx_bytes = uart_circ_chars_pending(xmit);
> + if (sport->tx_bytes > 0) {
> + if (xmit->tail > xmit->head) {
> + sport->dma_tx_nents = 2;
> + sg_init_table(sgl, 2);
> + sg_set_buf(sgl, xmit->buf + xmit->tail,
> + UART_XMIT_SIZE - xmit->tail);
> + sg_set_buf(&sgl[1], xmit->buf, xmit->head);
> + } else {
> + sport->dma_tx_nents = 1;
> + sg_init_one(sgl, xmit->buf + xmit->tail,
> + sport->tx_bytes);
> + }
> + spin_unlock_irqrestore(&sport->port.lock, flags);
> +
> + ret = dma_map_sg(sport->port.dev, sgl,
> + sport->dma_tx_nents, DMA_TO_DEVICE);
> + if (ret == 0) {
> + pr_err("DMA mapping error for TX.\n");
> + return;
> + }
> + desc = dmaengine_prep_slave_sg(chan, sgl,
> + sport->dma_tx_nents, DMA_MEM_TO_DEV, 0);
If you're setting a callback, you should set DMA_PREP_INTERRUPT in the
flags too.
> + if (!desc) {
> + pr_err("We cannot prepare for the TX slave dma!\n");
> + return;
> + }
> + desc->callback = dma_tx_callback;
> + desc->callback_param = sport;
> +
> + /* fire it */
> + dmaengine_submit(desc);
> + dma_async_issue_pending(chan);
> + return;
> + }
> + spin_unlock_irqrestore(&sport->port.lock, flags);
> + return;
> +}
> +
> /*
> * interrupts disabled on entry
> */
> @@ -448,8 +549,10 @@ static void imx_start_tx(struct uart_port *port)
> writel(temp, sport->port.membase + UCR1);
> }
>
> - temp = readl(sport->port.membase + UCR1);
> - writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
> + if (!sport->enable_dma) {
> + temp = readl(sport->port.membase + UCR1);
> + writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
> + }
>
> if (USE_IRDA(sport)) {
> temp = readl(sport->port.membase + UCR1);
> @@ -461,6 +564,11 @@ static void imx_start_tx(struct uart_port *port)
> writel(temp, sport->port.membase + UCR4);
> }
>
> + if (sport->enable_dma) {
> + schedule_work(&sport->tsk_dma_tx);
> + return;
> + }
> +
> if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
> imx_transmit_buffer(sport);
> }
> @@ -577,6 +685,28 @@ out:
> return IRQ_HANDLED;
> }
>
> +/*
> + * We wait for the RXFIFO is filled with some data, and then
> + * arise a DMA operation to receive the data.
> + */
> +static void imx_dma_rxint(struct imx_port *sport)
> +{
> + unsigned long temp;
> +
> + temp = readl(sport->port.membase + USR2);
> + if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
> + sport->dma_is_rxing = true;
> +
> + /* disable the `Recerver Ready Interrrupt` */
> + temp = readl(sport->port.membase + UCR1);
> + temp &= ~(UCR1_RRDYEN);
> + writel(temp, sport->port.membase + UCR1);
> +
> + /* tell the DMA to receive the data. */
> + schedule_work(&sport->tsk_dma_rx);
> + }
> +}
> +
> static irqreturn_t imx_int(int irq, void *dev_id)
> {
> struct imx_port *sport = dev_id;
> @@ -584,8 +714,12 @@ static irqreturn_t imx_int(int irq, void *dev_id)
>
> sts = readl(sport->port.membase + USR1);
>
> - if (sts & USR1_RRDY)
> - imx_rxint(irq, dev_id);
> + if (sts & USR1_RRDY) {
> + if (sport->enable_dma)
> + imx_dma_rxint(sport);
> + else
> + imx_rxint(irq, dev_id);
> + }
>
> if (sts & USR1_TRDY &&
> readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
> @@ -685,6 +819,195 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
> return 0;
> }
>
> +static bool imx_uart_filter(struct dma_chan *chan, void *param)
> +{
> + struct imx_port *sport = param;
> +
> + if (!imx_dma_is_general_purpose(chan))
> + return false;
> + chan->private = &sport->dma_data;
> + return true;
> +}
> +
> +#define RX_BUF_SIZE (PAGE_SIZE)
> +static int start_rx_dma(struct imx_port *sport);
> +static void dma_rx_work(struct work_struct *w)
> +{
> + struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx);
> + struct tty_struct *tty = sport->port.state->port.tty;
> +
> + if (sport->rx_bytes) {
> + tty_insert_flip_string(tty, sport->rx_buf, sport->rx_bytes);
> + tty_flip_buffer_push(tty);
> + sport->rx_bytes = 0;
> + }
> +
> + if (sport->dma_is_rxing)
> + start_rx_dma(sport);
> +}
> +
> +static void imx_finish_dma(struct imx_port *sport)
> +{
> + unsigned long temp;
> +
> + /* Enable the interrupt when the RXFIFO is not empty. */
> + temp = readl(sport->port.membase + UCR1);
> + temp |= UCR1_RRDYEN;
> + writel(temp, sport->port.membase + UCR1);
> +
> + sport->dma_is_rxing = false;
> + if (waitqueue_active(&sport->dma_wait))
> + wake_up(&sport->dma_wait);
> +}
> +
> +/*
> + * There are three kinds of RX DMA interrupts in the MX6Q:
> + * [1] the RX DMA buffer is full.
> + * [2] the Aging timer expires(wait for 8 bytes long)
> + * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
> + *
> + * The [2] and [3] are similar, but [3] is better.
> + * [3] can wait for 32 bytes long, so we do not use [2].
> + */
> +static void dma_rx_callback(void *data)
> +{
> + struct imx_port *sport = data;
> + struct dma_chan *chan = sport->dma_chan_rx;
> + unsigned int count;
> + struct tty_struct *tty;
> + struct scatterlist *sgl;
> + struct dma_tx_state state;
> + enum dma_status status;
> +
> + tty = sport->port.state->port.tty;
> + sgl = &sport->rx_sgl;
> +
> + /* unmap it first */
> + dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
> +
> + /* If we have finish the reading. we will not accept any more data. */
> + if (tty->closing) {
> + imx_finish_dma(sport);
> + return;
> + }
> +
> + status = chan->device->device_tx_status(chan,
> + (dma_cookie_t)NULL, &state);
> + count = RX_BUF_SIZE - state.residue;
> + if (count) {
> + sport->rx_bytes = count;
> + schedule_work(&sport->tsk_dma_rx);
> + } else
> + imx_finish_dma(sport);
> +}
> +
> +static int start_rx_dma(struct imx_port *sport)
> +{
> + struct scatterlist *sgl = &sport->rx_sgl;
> + struct dma_chan *chan = sport->dma_chan_rx;
> + struct dma_async_tx_descriptor *desc;
> + int ret;
> +
> + sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
> + ret = dma_map_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
> + if (ret == 0) {
> + pr_err("DMA mapping error for RX.\n");
> + return -EINVAL;
> + }
> + desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM, 0);
> + if (!desc) {
> + pr_err("We cannot prepare for the RX slave dma!\n");
> + return -EINVAL;
> + }
> + desc->callback = dma_rx_callback;
> + desc->callback_param = sport;
> +
> + dmaengine_submit(desc);
> + dma_async_issue_pending(chan);
> + return 0;
> +}
> +
> +static void imx_uart_dma_exit(struct imx_port *sport)
> +{
> + if (sport->dma_chan_rx) {
> + dma_release_channel(sport->dma_chan_rx);
> + sport->dma_chan_rx = NULL;
> +
> + kfree(sport->rx_buf);
> + sport->rx_buf = NULL;
> + }
> +
> + if (sport->dma_chan_tx) {
> + dma_release_channel(sport->dma_chan_tx);
> + sport->dma_chan_tx = NULL;
> + }
> +}
> +
> +/* see the "i.MX61 SDMA Scripts User Manual.doc" for the parameters */
> +static int imx_uart_dma_init(struct imx_port *sport)
> +{
> + struct dma_slave_config slave_config;
> + dma_cap_mask_t mask;
> + int ret;
> +
> + /* prepare for RX : */
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_SLAVE, mask);
> +
> + sport->dma_data.priority = DMA_PRIO_HIGH;
> + sport->dma_data.dma_request = sport->dma_req_rx;
> + sport->dma_data.peripheral_type = IMX_DMATYPE_UART;
> +
> + sport->dma_chan_rx = dma_request_channel(mask, imx_uart_filter, sport);
> + if (!sport->dma_chan_rx) {
> + pr_err("cannot get the DMA channel.\n");
> + ret = -EINVAL;
> + goto err;
> + }
> +
> + slave_config.direction = DMA_DEV_TO_MEM;
> + slave_config.src_addr = sport->port.mapbase + URXD0;
> + slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> + slave_config.src_maxburst = RXTL; /* fix me */
> + ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
> + if (ret) {
> + pr_err("error in RX dma configuration.\n");
> + goto err;
> + }
> +
> + sport->rx_buf = kzalloc(PAGE_SIZE, GFP_DMA);
> + if (!sport->rx_buf) {
> + pr_err("cannot alloc DMA buffer.\n");
> + ret = -ENOMEM;
> + goto err;
> + }
> + sport->rx_bytes = 0;
> +
> + /* prepare for TX : */
> + sport->dma_data.dma_request = sport->dma_req_tx;
> + sport->dma_chan_tx = dma_request_channel(mask, imx_uart_filter, sport);
> + if (!sport->dma_chan_tx) {
> + pr_err("cannot get the TX DMA channel!\n");
> + ret = -EINVAL;
> + goto err;
> + }
> +
> + slave_config.direction = DMA_MEM_TO_DEV;
> + slave_config.dst_addr = sport->port.mapbase + URTX0;
> + slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
> + slave_config.dst_maxburst = TXTL; /* fix me */
> + ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
> + if (ret) {
> + pr_err("error in TX dma configuration.");
> + goto err;
> + }
> +
> + return 0;
> +err:
> + imx_uart_dma_exit(sport);
> + return ret;
> +}
> +
> /* half the RX buffer size */
> #define CTSTL 16
>
> @@ -756,6 +1079,19 @@ static int imx_startup(struct uart_port *port)
> }
> }
>
> + /* Enable the SDMA for uart. */
> + if (sport->enable_dma) {
> + int ret;
> + ret = imx_uart_dma_init(sport);
> + if (ret)
> + goto error_out3;
> +
> + sport->port.flags |= UPF_LOW_LATENCY;
> + INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
> + INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
> + init_waitqueue_head(&sport->dma_wait);
> + }
> +
> /*
> * Finally, clear and enable interrupts
> */
> @@ -763,6 +1099,11 @@ static int imx_startup(struct uart_port *port)
>
> temp = readl(sport->port.membase + UCR1);
> temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
> + if (sport->enable_dma) {
> + temp |= UCR1_RDMAEN | UCR1_TDMAEN;
> + /* ICD, wait for more than 32 frames, but it still to short. */
> + temp |= UCR1_ICD_REG(3);
> + }
>
> if (USE_IRDA(sport)) {
> temp |= UCR1_IREN;
> @@ -806,6 +1147,12 @@ static int imx_startup(struct uart_port *port)
> writel(temp, sport->port.membase + UCR3);
> }
>
> + if (sport->enable_dma) {
> + temp = readl(sport->port.membase + UCR4);
> + temp |= UCR4_IDDMAEN;
> + writel(temp, sport->port.membase + UCR4);
> + }
> +
> /*
> * Enable modem status interrupts
> */
> @@ -840,6 +1187,13 @@ static void imx_shutdown(struct uart_port *port)
> struct imx_port *sport = (struct imx_port *)port;
> unsigned long temp;
>
> + if (sport->enable_dma) {
> + /* We have to wait for the DMA to finish. */
> + wait_event(sport->dma_wait, !sport->dma_is_rxing);
> + imx_stop_rx(port);
> + imx_uart_dma_exit(sport);
> + }
> +
> temp = readl(sport->port.membase + UCR2);
> temp &= ~(UCR2_TXEN);
> writel(temp, sport->port.membase + UCR2);
> @@ -875,8 +1229,16 @@ static void imx_shutdown(struct uart_port *port)
> temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
> if (USE_IRDA(sport))
> temp &= ~(UCR1_IREN);
> + if (sport->enable_dma)
> + temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN);
>
> writel(temp, sport->port.membase + UCR1);
> +
> + if (sport->enable_dma) {
> + temp = readl(sport->port.membase + UCR4);
> + temp &= ~UCR4_IDDMAEN;
> + writel(temp, sport->port.membase + UCR4);
> + }
> }
>
> static void
> @@ -1013,6 +1375,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>
> ufcr = readl(sport->port.membase + UFCR);
> ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
> + /* set the DTE mode */
> + if (sport->enable_dte)
> + ufcr |= UFCR_DCEDTE;
> writel(ufcr, sport->port.membase + UFCR);
>
> writel(num, sport->port.membase + UBIR);
> @@ -1409,6 +1774,7 @@ static int serial_imx_probe_dt(struct imx_port *sport,
> const struct of_device_id *of_id =
> of_match_device(imx_uart_dt_ids, &pdev->dev);
> int ret;
> + u32 dma_req[2];
>
> if (!np)
> /* no device tree device */
> @@ -1427,6 +1793,18 @@ static int serial_imx_probe_dt(struct imx_port *sport,
> if (of_get_property(np, "fsl,irda-mode", NULL))
> sport->use_irda = 1;
>
> + if (of_get_property(np, "fsl,enable-dma", NULL))
> + sport->enable_dma = 1;
> +
> + if (of_get_property(np, "fsl,enable-dte", NULL))
> + sport->enable_dte = 1;
> +
> + if (of_property_read_u32_array(np, "fsl,uart-dma-events", dma_req,
> + ARRAY_SIZE(dma_req)) == 0) {
> + sport->dma_req_rx = dma_req[0];
> + sport->dma_req_tx = dma_req[1];
> + }
> +
> sport->devdata = of_id->data;
>
> return 0;
> --
> 1.7.0.4
>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 2/2] ARM: MX6q: enable DMA support for UART2
From: Huang Shijie @ 2012-04-26 10:37 UTC (permalink / raw)
To: alan
Cc: linux-serial, gregkh, shawn.guo, linux-arm-kernel, B20223, r58066,
B20596, s.hauer, Huang Shijie
In-Reply-To: <1335436632-29499-1-git-send-email-b32955@freescale.com>
add uart2 for mx6q-arm2 board, and enable the DMA support for it.
Signed-off-by: Huang Shijie <b32955@freescale.com>
---
arch/arm/boot/dts/imx6q-arm2.dts | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/imx6q-arm2.dts b/arch/arm/boot/dts/imx6q-arm2.dts
index ce1c823..c9a6cb3 100644
--- a/arch/arm/boot/dts/imx6q-arm2.dts
+++ b/arch/arm/boot/dts/imx6q-arm2.dts
@@ -46,6 +46,14 @@
status = "okay";
};
+ uart2: uart@021e8000 {
+ fsl,uart-has-rtscts;
+ fsl,enable-dma;
+ fsl,uart-dma-events = <27 28>;
+ fsl,enable-dte;
+ status = "okay";
+ };
+
uart4: uart@021f0000 {
status = "okay";
};
--
1.7.0.4
^ permalink raw reply related
* [PATCH 1/2] serial/imx: add DMA support
From: Huang Shijie @ 2012-04-26 10:37 UTC (permalink / raw)
To: alan
Cc: linux-serial, gregkh, shawn.guo, linux-arm-kernel, B20223, r58066,
B20596, s.hauer, Huang Shijie
In-Reply-To: <1335436632-29499-1-git-send-email-b32955@freescale.com>
Add the DMA support for uart RX and TX.
Signed-off-by: Huang Shijie <b32955@freescale.com>
---
.../bindings/tty/serial/fsl-imx-uart.txt | 7 +
drivers/tty/serial/imx.c | 386 +++++++++++++++++++-
2 files changed, 389 insertions(+), 4 deletions(-)
diff --git a/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
index a9c0406..f27489d 100644
--- a/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
+++ b/Documentation/devicetree/bindings/tty/serial/fsl-imx-uart.txt
@@ -8,6 +8,10 @@ Required properties:
Optional properties:
- fsl,uart-has-rtscts : Indicate the uart has rts and cts
- fsl,irda-mode : Indicate the uart supports irda mode
+- fsl,enable-dma : Indicate the uart supports DMA
+- fsl,uart-dma-events : contains the DMA events for RX and TX,
+ The first is the RX event, while the other is TX.
+- fsl,enable-dte: Indicate the uart works in DTE mode
Example:
@@ -16,4 +20,7 @@ uart@73fbc000 {
reg = <0x73fbc000 0x4000>;
interrupts = <31>;
fsl,uart-has-rtscts;
+ fsl,enable-dma;
+ fsl,uart-dma-events = <xx xx>;
+ fsl,enable-dte;
};
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index e7fecee..65ba24d 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -47,9 +47,11 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <mach/dma.h>
#include <mach/imx-uart.h>
/* Register definitions */
@@ -82,6 +84,7 @@
#define UCR1_ADBR (1<<14) /* Auto detect baud rate */
#define UCR1_TRDYEN (1<<13) /* Transmitter ready interrupt enable */
#define UCR1_IDEN (1<<12) /* Idle condition interrupt */
+#define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */
#define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */
#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */
#define UCR1_IREN (1<<7) /* Infrared interface enable */
@@ -125,6 +128,7 @@
#define UCR4_ENIRI (1<<8) /* Serial infrared interrupt enable */
#define UCR4_WKEN (1<<7) /* Wake interrupt enable */
#define UCR4_REF16 (1<<6) /* Ref freq 16 MHz */
+#define UCR4_IDDMAEN (1<<6) /* DMA IDLE Condition Detected */
#define UCR4_IRSC (1<<5) /* IR special case */
#define UCR4_TCEN (1<<3) /* Transmit complete interrupt enable */
#define UCR4_BKEN (1<<2) /* Break condition interrupt enable */
@@ -134,6 +138,7 @@
#define UFCR_RFDIV (7<<7) /* Reference freq divider mask */
#define UFCR_RFDIV_REG(x) (((x) < 7 ? 6 - (x) : 6) << 7)
#define UFCR_TXTL_SHF 10 /* Transmitter trigger level shift */
+#define UFCR_DCEDTE (1<<6)
#define USR1_PARITYERR (1<<15) /* Parity error interrupt flag */
#define USR1_RTSS (1<<14) /* RTS pin status */
#define USR1_TRDY (1<<13) /* Transmitter ready interrupt/dma flag */
@@ -200,12 +205,27 @@ struct imx_port {
unsigned int old_status;
int txirq,rxirq,rtsirq;
unsigned int have_rtscts:1;
+ unsigned int enable_dte:1;
+ unsigned int enable_dma:1;
unsigned int use_irda:1;
unsigned int irda_inv_rx:1;
unsigned int irda_inv_tx:1;
unsigned short trcv_delay; /* transceiver delay */
struct clk *clk;
struct imx_uart_data *devdata;
+
+ /* DMA fields */
+ unsigned int dma_req_rx;
+ unsigned int dma_req_tx;
+ struct imx_dma_data dma_data;
+ struct dma_chan *dma_chan_rx, *dma_chan_tx;
+ struct scatterlist rx_sgl, tx_sgl[2];
+ void *rx_buf;
+ unsigned int rx_bytes, tx_bytes;
+ struct work_struct tsk_dma_rx, tsk_dma_tx;
+ unsigned int dma_tx_nents;
+ bool dma_is_rxing;
+ wait_queue_head_t dma_wait;
};
struct imx_port_ucrs {
@@ -394,6 +414,13 @@ static void imx_stop_rx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ /*
+ * We are maybe in the SMP now, so if the DMA RX thread is running,
+ * we have to wait for it to finish.
+ */
+ if (sport->enable_dma && sport->dma_is_rxing)
+ return;
+
temp = readl(sport->port.membase + UCR2);
writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2);
}
@@ -429,6 +456,80 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
imx_stop_tx(&sport->port);
}
+static void dma_tx_callback(void *data)
+{
+ struct imx_port *sport = data;
+ struct scatterlist *sgl = &sport->tx_sgl[0];
+ struct circ_buf *xmit = &sport->port.state->xmit;
+
+ dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
+
+ /* update the stat */
+ spin_lock(&sport->port.lock);
+ xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
+ sport->port.icount.tx += sport->tx_bytes;
+ spin_unlock(&sport->port.lock);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&sport->port);
+ schedule_work(&sport->tsk_dma_tx);
+}
+
+static void dma_tx_work(struct work_struct *w)
+{
+ struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_tx);
+ struct circ_buf *xmit = &sport->port.state->xmit;
+ struct scatterlist *sgl = &sport->tx_sgl[0];
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *chan = sport->dma_chan_tx;
+ enum dma_status status;
+ unsigned long flags;
+ int ret;
+
+ status = chan->device->device_tx_status(chan, (dma_cookie_t)NULL, NULL);
+ if (DMA_IN_PROGRESS == status)
+ return;
+
+ spin_lock_irqsave(&sport->port.lock, flags);
+ sport->tx_bytes = uart_circ_chars_pending(xmit);
+ if (sport->tx_bytes > 0) {
+ if (xmit->tail > xmit->head) {
+ sport->dma_tx_nents = 2;
+ sg_init_table(sgl, 2);
+ sg_set_buf(sgl, xmit->buf + xmit->tail,
+ UART_XMIT_SIZE - xmit->tail);
+ sg_set_buf(&sgl[1], xmit->buf, xmit->head);
+ } else {
+ sport->dma_tx_nents = 1;
+ sg_init_one(sgl, xmit->buf + xmit->tail,
+ sport->tx_bytes);
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+
+ ret = dma_map_sg(sport->port.dev, sgl,
+ sport->dma_tx_nents, DMA_TO_DEVICE);
+ if (ret == 0) {
+ pr_err("DMA mapping error for TX.\n");
+ return;
+ }
+ desc = dmaengine_prep_slave_sg(chan, sgl,
+ sport->dma_tx_nents, DMA_MEM_TO_DEV, 0);
+ if (!desc) {
+ pr_err("We cannot prepare for the TX slave dma!\n");
+ return;
+ }
+ desc->callback = dma_tx_callback;
+ desc->callback_param = sport;
+
+ /* fire it */
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+ return;
+ }
+ spin_unlock_irqrestore(&sport->port.lock, flags);
+ return;
+}
+
/*
* interrupts disabled on entry
*/
@@ -448,8 +549,10 @@ static void imx_start_tx(struct uart_port *port)
writel(temp, sport->port.membase + UCR1);
}
- temp = readl(sport->port.membase + UCR1);
- writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ if (!sport->enable_dma) {
+ temp = readl(sport->port.membase + UCR1);
+ writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
+ }
if (USE_IRDA(sport)) {
temp = readl(sport->port.membase + UCR1);
@@ -461,6 +564,11 @@ static void imx_start_tx(struct uart_port *port)
writel(temp, sport->port.membase + UCR4);
}
+ if (sport->enable_dma) {
+ schedule_work(&sport->tsk_dma_tx);
+ return;
+ }
+
if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
imx_transmit_buffer(sport);
}
@@ -577,6 +685,28 @@ out:
return IRQ_HANDLED;
}
+/*
+ * We wait for the RXFIFO is filled with some data, and then
+ * arise a DMA operation to receive the data.
+ */
+static void imx_dma_rxint(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ temp = readl(sport->port.membase + USR2);
+ if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
+ sport->dma_is_rxing = true;
+
+ /* disable the `Recerver Ready Interrrupt` */
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_RRDYEN);
+ writel(temp, sport->port.membase + UCR1);
+
+ /* tell the DMA to receive the data. */
+ schedule_work(&sport->tsk_dma_rx);
+ }
+}
+
static irqreturn_t imx_int(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
@@ -584,8 +714,12 @@ static irqreturn_t imx_int(int irq, void *dev_id)
sts = readl(sport->port.membase + USR1);
- if (sts & USR1_RRDY)
- imx_rxint(irq, dev_id);
+ if (sts & USR1_RRDY) {
+ if (sport->enable_dma)
+ imx_dma_rxint(sport);
+ else
+ imx_rxint(irq, dev_id);
+ }
if (sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
@@ -685,6 +819,195 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
return 0;
}
+static bool imx_uart_filter(struct dma_chan *chan, void *param)
+{
+ struct imx_port *sport = param;
+
+ if (!imx_dma_is_general_purpose(chan))
+ return false;
+ chan->private = &sport->dma_data;
+ return true;
+}
+
+#define RX_BUF_SIZE (PAGE_SIZE)
+static int start_rx_dma(struct imx_port *sport);
+static void dma_rx_work(struct work_struct *w)
+{
+ struct imx_port *sport = container_of(w, struct imx_port, tsk_dma_rx);
+ struct tty_struct *tty = sport->port.state->port.tty;
+
+ if (sport->rx_bytes) {
+ tty_insert_flip_string(tty, sport->rx_buf, sport->rx_bytes);
+ tty_flip_buffer_push(tty);
+ sport->rx_bytes = 0;
+ }
+
+ if (sport->dma_is_rxing)
+ start_rx_dma(sport);
+}
+
+static void imx_finish_dma(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ /* Enable the interrupt when the RXFIFO is not empty. */
+ temp = readl(sport->port.membase + UCR1);
+ temp |= UCR1_RRDYEN;
+ writel(temp, sport->port.membase + UCR1);
+
+ sport->dma_is_rxing = false;
+ if (waitqueue_active(&sport->dma_wait))
+ wake_up(&sport->dma_wait);
+}
+
+/*
+ * There are three kinds of RX DMA interrupts in the MX6Q:
+ * [1] the RX DMA buffer is full.
+ * [2] the Aging timer expires(wait for 8 bytes long)
+ * [3] the Idle Condition Detect(enabled the UCR4_IDDMAEN).
+ *
+ * The [2] and [3] are similar, but [3] is better.
+ * [3] can wait for 32 bytes long, so we do not use [2].
+ */
+static void dma_rx_callback(void *data)
+{
+ struct imx_port *sport = data;
+ struct dma_chan *chan = sport->dma_chan_rx;
+ unsigned int count;
+ struct tty_struct *tty;
+ struct scatterlist *sgl;
+ struct dma_tx_state state;
+ enum dma_status status;
+
+ tty = sport->port.state->port.tty;
+ sgl = &sport->rx_sgl;
+
+ /* unmap it first */
+ dma_unmap_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
+
+ /* If we have finish the reading. we will not accept any more data. */
+ if (tty->closing) {
+ imx_finish_dma(sport);
+ return;
+ }
+
+ status = chan->device->device_tx_status(chan,
+ (dma_cookie_t)NULL, &state);
+ count = RX_BUF_SIZE - state.residue;
+ if (count) {
+ sport->rx_bytes = count;
+ schedule_work(&sport->tsk_dma_rx);
+ } else
+ imx_finish_dma(sport);
+}
+
+static int start_rx_dma(struct imx_port *sport)
+{
+ struct scatterlist *sgl = &sport->rx_sgl;
+ struct dma_chan *chan = sport->dma_chan_rx;
+ struct dma_async_tx_descriptor *desc;
+ int ret;
+
+ sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE);
+ ret = dma_map_sg(sport->port.dev, sgl, 1, DMA_FROM_DEVICE);
+ if (ret == 0) {
+ pr_err("DMA mapping error for RX.\n");
+ return -EINVAL;
+ }
+ desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM, 0);
+ if (!desc) {
+ pr_err("We cannot prepare for the RX slave dma!\n");
+ return -EINVAL;
+ }
+ desc->callback = dma_rx_callback;
+ desc->callback_param = sport;
+
+ dmaengine_submit(desc);
+ dma_async_issue_pending(chan);
+ return 0;
+}
+
+static void imx_uart_dma_exit(struct imx_port *sport)
+{
+ if (sport->dma_chan_rx) {
+ dma_release_channel(sport->dma_chan_rx);
+ sport->dma_chan_rx = NULL;
+
+ kfree(sport->rx_buf);
+ sport->rx_buf = NULL;
+ }
+
+ if (sport->dma_chan_tx) {
+ dma_release_channel(sport->dma_chan_tx);
+ sport->dma_chan_tx = NULL;
+ }
+}
+
+/* see the "i.MX61 SDMA Scripts User Manual.doc" for the parameters */
+static int imx_uart_dma_init(struct imx_port *sport)
+{
+ struct dma_slave_config slave_config;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* prepare for RX : */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ sport->dma_data.priority = DMA_PRIO_HIGH;
+ sport->dma_data.dma_request = sport->dma_req_rx;
+ sport->dma_data.peripheral_type = IMX_DMATYPE_UART;
+
+ sport->dma_chan_rx = dma_request_channel(mask, imx_uart_filter, sport);
+ if (!sport->dma_chan_rx) {
+ pr_err("cannot get the DMA channel.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_DEV_TO_MEM;
+ slave_config.src_addr = sport->port.mapbase + URXD0;
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.src_maxburst = RXTL; /* fix me */
+ ret = dmaengine_slave_config(sport->dma_chan_rx, &slave_config);
+ if (ret) {
+ pr_err("error in RX dma configuration.\n");
+ goto err;
+ }
+
+ sport->rx_buf = kzalloc(PAGE_SIZE, GFP_DMA);
+ if (!sport->rx_buf) {
+ pr_err("cannot alloc DMA buffer.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ sport->rx_bytes = 0;
+
+ /* prepare for TX : */
+ sport->dma_data.dma_request = sport->dma_req_tx;
+ sport->dma_chan_tx = dma_request_channel(mask, imx_uart_filter, sport);
+ if (!sport->dma_chan_tx) {
+ pr_err("cannot get the TX DMA channel!\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ slave_config.direction = DMA_MEM_TO_DEV;
+ slave_config.dst_addr = sport->port.mapbase + URTX0;
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ slave_config.dst_maxburst = TXTL; /* fix me */
+ ret = dmaengine_slave_config(sport->dma_chan_tx, &slave_config);
+ if (ret) {
+ pr_err("error in TX dma configuration.");
+ goto err;
+ }
+
+ return 0;
+err:
+ imx_uart_dma_exit(sport);
+ return ret;
+}
+
/* half the RX buffer size */
#define CTSTL 16
@@ -756,6 +1079,19 @@ static int imx_startup(struct uart_port *port)
}
}
+ /* Enable the SDMA for uart. */
+ if (sport->enable_dma) {
+ int ret;
+ ret = imx_uart_dma_init(sport);
+ if (ret)
+ goto error_out3;
+
+ sport->port.flags |= UPF_LOW_LATENCY;
+ INIT_WORK(&sport->tsk_dma_tx, dma_tx_work);
+ INIT_WORK(&sport->tsk_dma_rx, dma_rx_work);
+ init_waitqueue_head(&sport->dma_wait);
+ }
+
/*
* Finally, clear and enable interrupts
*/
@@ -763,6 +1099,11 @@ static int imx_startup(struct uart_port *port)
temp = readl(sport->port.membase + UCR1);
temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
+ if (sport->enable_dma) {
+ temp |= UCR1_RDMAEN | UCR1_TDMAEN;
+ /* ICD, wait for more than 32 frames, but it still to short. */
+ temp |= UCR1_ICD_REG(3);
+ }
if (USE_IRDA(sport)) {
temp |= UCR1_IREN;
@@ -806,6 +1147,12 @@ static int imx_startup(struct uart_port *port)
writel(temp, sport->port.membase + UCR3);
}
+ if (sport->enable_dma) {
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_IDDMAEN;
+ writel(temp, sport->port.membase + UCR4);
+ }
+
/*
* Enable modem status interrupts
*/
@@ -840,6 +1187,13 @@ static void imx_shutdown(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ if (sport->enable_dma) {
+ /* We have to wait for the DMA to finish. */
+ wait_event(sport->dma_wait, !sport->dma_is_rxing);
+ imx_stop_rx(port);
+ imx_uart_dma_exit(sport);
+ }
+
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
writel(temp, sport->port.membase + UCR2);
@@ -875,8 +1229,16 @@ static void imx_shutdown(struct uart_port *port)
temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
if (USE_IRDA(sport))
temp &= ~(UCR1_IREN);
+ if (sport->enable_dma)
+ temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN);
writel(temp, sport->port.membase + UCR1);
+
+ if (sport->enable_dma) {
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~UCR4_IDDMAEN;
+ writel(temp, sport->port.membase + UCR4);
+ }
}
static void
@@ -1013,6 +1375,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
ufcr = readl(sport->port.membase + UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
+ /* set the DTE mode */
+ if (sport->enable_dte)
+ ufcr |= UFCR_DCEDTE;
writel(ufcr, sport->port.membase + UFCR);
writel(num, sport->port.membase + UBIR);
@@ -1409,6 +1774,7 @@ static int serial_imx_probe_dt(struct imx_port *sport,
const struct of_device_id *of_id =
of_match_device(imx_uart_dt_ids, &pdev->dev);
int ret;
+ u32 dma_req[2];
if (!np)
/* no device tree device */
@@ -1427,6 +1793,18 @@ static int serial_imx_probe_dt(struct imx_port *sport,
if (of_get_property(np, "fsl,irda-mode", NULL))
sport->use_irda = 1;
+ if (of_get_property(np, "fsl,enable-dma", NULL))
+ sport->enable_dma = 1;
+
+ if (of_get_property(np, "fsl,enable-dte", NULL))
+ sport->enable_dte = 1;
+
+ if (of_property_read_u32_array(np, "fsl,uart-dma-events", dma_req,
+ ARRAY_SIZE(dma_req)) == 0) {
+ sport->dma_req_rx = dma_req[0];
+ sport->dma_req_tx = dma_req[1];
+ }
+
sport->devdata = of_id->data;
return 0;
--
1.7.0.4
^ permalink raw reply related
* [PATCH 0/2] add DMA support to uart
From: Huang Shijie @ 2012-04-26 10:37 UTC (permalink / raw)
To: alan
Cc: linux-serial, gregkh, shawn.guo, linux-arm-kernel, B20223, r58066,
B20596, s.hauer, Huang Shijie
Add DMA support to UART driver.
Tested this patch on imx6q(arm2) board with a new SDMA firmware.
Huang Shijie (2):
serial/imx: add DMA support
ARM: MX6q: enable DMA support for UART2
.../bindings/tty/serial/fsl-imx-uart.txt | 7 +
arch/arm/boot/dts/imx6q-arm2.dts | 8 +
drivers/tty/serial/imx.c | 386 +++++++++++++++++++-
3 files changed, 397 insertions(+), 4 deletions(-)
^ permalink raw reply
* [PATCH] 8250.c: less than 2400 baud fix.
From: Christian Melki @ 2012-04-26 6:59 UTC (permalink / raw)
To: gregkh@linuxfoundation.org; +Cc: linux-serial@vger.kernel.org
From: Christian Melki <christian.melki@ericsson.se>
We noticed that we were loosing data at speed less than 2400 baud.
It turned out our (TI16750 compatible) uart with 64 byte outgoing fifo was truncated to 16 byte (bit 5 sets fifo len) when modifying the fcr reg.
The input code still fills the buffer with 64 bytes if I remember correctly and thus data is lost.
Our fix was to remove whiping of the fcr content and just add the TRIGGER_1 which we want for latency.
I can't see why this would not work on less than 2400 always, for all uarts...
Otherwise one would have to make sure the filling of the fifo re-checks the current state of available fifo size (urrk).
Signed-off-by: Christian Melki <christian.melki@ericsson.se>
---
diff -urpN linux-3.3.2.orig//drivers/tty/serial/8250/8250.c linux-3.3.2/drivers/tty/serial/8250/8250.c
--- linux-3.3.2.orig//drivers/tty/serial/8250/8250.c 2012-04-25 10:31:29.000000000 +0200
+++ linux-3.3.2/drivers/tty/serial/8250/8250.c 2012-04-26 08:46:29.000000000 +0200
@@ -2299,10 +2299,11 @@ serial8250_do_set_termios(struct uart_po
quot++;
if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
- if (baud < 2400)
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
- else
- fcr = uart_config[up->port.type].fcr;
+ fcr = uart_config[up->port.type].fcr;
+ if (baud < 2400) {
+ fcr &= ~UART_FCR_TRIGGER_MASK;
+ fcr |= UART_FCR_TRIGGER_1;
+ }
}
/*
^ permalink raw reply
* Re: 8250.c less than 2400 baud fix.
From: Greg KH @ 2012-04-25 14:11 UTC (permalink / raw)
To: Christian Melki; +Cc: linux-serial@vger.kernel.org
In-Reply-To: <1346E68D3A5B2043907CF4BF466383411A07E00CF0@ESESSCMS0353.eemea.ericsson.se>
On Wed, Apr 25, 2012 at 02:26:36PM +0200, Christian Melki wrote:
> Hi.
>
> We noticed that we were loosing data at speed less than 2400 baud.
> It turned out our (TI16750 compatible) uart with 64 byte outgoing fifo was truncated to 16 byte (bit 5 sets fifo len) when modifying the fcr reg.
> The input code still fills the buffer with 64 bytes if I remember correctly and thus data is lost.
> Our fix was to remove whiping of the fcr content and just add the TRIGGER_1 which we want for latency.
> I can't see why this would not work on less than 2400 always, for all uarts...
> Otherwise one would have to make sure the filling of the fifo re-checks the current state of available fifo size (urrk).
Thank you for your fix, but could you take a look at the file,
Documentation/SubmittingPatches and resend it with a Signed-off-by: line
so that I am able to apply the patch?
thanks,
greg k-h
^ permalink raw reply
* 8250.c less than 2400 baud fix.
From: Christian Melki @ 2012-04-25 12:26 UTC (permalink / raw)
To: linux-serial@vger.kernel.org
[-- Attachment #1: Type: text/plain, Size: 664 bytes --]
Hi.
We noticed that we were loosing data at speed less than 2400 baud.
It turned out our (TI16750 compatible) uart with 64 byte outgoing fifo was truncated to 16 byte (bit 5 sets fifo len) when modifying the fcr reg.
The input code still fills the buffer with 64 bytes if I remember correctly and thus data is lost.
Our fix was to remove whiping of the fcr content and just add the TRIGGER_1 which we want for latency.
I can't see why this would not work on less than 2400 always, for all uarts...
Otherwise one would have to make sure the filling of the fifo re-checks the current state of available fifo size (urrk).
Best regards,
Christian Melki
[-- Attachment #2: 8250-fix.patch --]
[-- Type: application/octet-stream, Size: 694 bytes --]
diff -urpN linux-3.3.2.orig//drivers/tty/serial/8250/8250.c linux-3.3.2/drivers/tty/serial/8250/8250.c
--- linux-3.3.2.orig//drivers/tty/serial/8250/8250.c 2012-04-25 08:19:35.000000000 +0200
+++ linux-3.3.2/drivers/tty/serial/8250/8250.c 2012-04-25 08:20:35.000000000 +0200
@@ -2299,10 +2299,11 @@ serial8250_do_set_termios(struct uart_po
quot++;
if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {
- if (baud < 2400)
- fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
- else
- fcr = uart_config[up->port.type].fcr;
+ fcr = uart_config[up->port.type].fcr;
+ if (baud < 2400) {
+ fcr &= ~UART_FCR_TRIGGER_MASK;
+ fcr |= UART_FCR_TRIGGER_1;
+ }
}
/*
^ permalink raw reply
* [PATCH v2] tty: omap-serial: configure wakeup mechanism based on port usage.
From: Govindraj.R @ 2012-04-24 13:53 UTC (permalink / raw)
To: linux-serial; +Cc: linux-omap, Govindraj.R, Kevin Hilman, Paul Walmsley
From: "Govindraj.R" <govindraj.raja@ti.com>
With set_wakeup port ops availability from serial_core layer
which will be called when port is opened with state as true
indicating the wakeups can be enabled for this port and state
as false while port shutdown where we can disable the wakeups.
But wakeup can be also enabled/disabled when requested from sysfs.
If wakeups are disabled while entering suspend we have to enable them
back while port is used from resume. This can be done with serial_omap_pm
this uart ops has state flag indicating if port is going to be used.
serial_omap_pm gets called from serial_core layer.
Thanks to Kevin Hilman <khilman@ti.com> for suggesting this.
Discussion References:
http://www.spinics.net/lists/linux-omap/msg67764.html
http://www.spinics.net/lists/linux-omap/msg67838.html
Cc: Kevin Hilman <khilman@ti.com>
Cc: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Govindraj.R <govindraj.raja@ti.com>
---
Patch is tested using the patch as in here[1]
Tested on beagle-XM by enabling and disabling wakeups
from sysfs and opening and closing a uart port.
[1]: http://marc.info/?l=linux-omap&m=133518614022144&w=2
arch/arm/plat-omap/include/plat/omap-serial.h | 1 -
drivers/tty/serial/omap-serial.c | 40 +++++++++++++-----------
2 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
index 9ff4444..8e6d734 100644
--- a/arch/arm/plat-omap/include/plat/omap-serial.h
+++ b/arch/arm/plat-omap/include/plat/omap-serial.h
@@ -130,7 +130,6 @@ struct uart_omap_port {
unsigned long port_activity;
u32 context_loss_cnt;
u32 errata;
- u8 wakeups_enabled;
struct pm_qos_request pm_qos_request;
u32 latency;
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index d00b38e..b66818b 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -910,11 +910,23 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
}
+static int serial_omap_set_wake(struct uart_port *port, unsigned int state)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
+
+ if (pdata->enable_wakeup)
+ pdata->enable_wakeup(up->pdev, state ? true : false);
+
+ return 0;
+}
+
static void
serial_omap_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
unsigned char efr;
dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->port.line);
@@ -930,12 +942,8 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
- if (!device_may_wakeup(&up->pdev->dev)) {
- if (!state)
- pm_runtime_forbid(&up->pdev->dev);
- else
- pm_runtime_allow(&up->pdev->dev);
- }
+ if (!state && pdata->enable_wakeup)
+ pdata->enable_wakeup(up->pdev, true);
pm_runtime_put(&up->pdev->dev);
}
@@ -1161,6 +1169,7 @@ static struct uart_ops serial_omap_pops = {
.shutdown = serial_omap_shutdown,
.set_termios = serial_omap_set_termios,
.pm = serial_omap_pm,
+ .set_wake = serial_omap_set_wake,
.type = serial_omap_type,
.release_port = serial_omap_release_port,
.request_port = serial_omap_request_port,
@@ -1184,10 +1193,14 @@ static struct uart_driver serial_omap_reg = {
static int serial_omap_suspend(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
+ struct omap_uart_port_info *pdata = dev->platform_data;
if (up) {
uart_suspend_port(&serial_omap_reg, &up->port);
flush_work_sync(&up->qos_work);
+
+ if (!device_may_wakeup(dev))
+ pdata->enable_wakeup(up->pdev, false);
}
return 0;
@@ -1476,6 +1489,9 @@ static int serial_omap_probe(struct platform_device *pdev)
if (ret != 0)
goto err_add_port;
+ if (omap_up_info->enable_wakeup)
+ omap_up_info->enable_wakeup(pdev, false);
+
pm_runtime_put(&pdev->dev);
platform_set_drvdata(pdev, up);
return 0;
@@ -1582,18 +1598,6 @@ static int serial_omap_runtime_suspend(struct device *dev)
if (pdata->get_context_loss_count)
up->context_loss_cnt = pdata->get_context_loss_count(dev);
- if (device_may_wakeup(dev)) {
- if (!up->wakeups_enabled) {
- pdata->enable_wakeup(up->pdev, true);
- up->wakeups_enabled = true;
- }
- } else {
- if (up->wakeups_enabled) {
- pdata->enable_wakeup(up->pdev, false);
- up->wakeups_enabled = false;
- }
- }
-
/* Errata i291 */
if (up->use_dma && pdata->set_forceidle &&
(up->errata & UART_ERRATA_i291_DMA_FORCEIDLE))
--
1.7.9
^ permalink raw reply related
* Emergency Email, Please get back.
From: Cao Lixin @ 2012-04-24 6:40 UTC (permalink / raw)
Hello,
I have a secured business proposal for you worth USD28,350,000.00. Get back for details.
Email: cao23lixin@live.co.uk
I await your response.
Best Regards.
Cao Lixin
^ permalink raw reply
* Re: [PATCH] tty: omap-serial: configure wakeup mechanism based on port usage.
From: Kevin Hilman @ 2012-04-23 23:54 UTC (permalink / raw)
To: Govindraj.R; +Cc: linux-serial, linux-omap, Paul Walmsley
In-Reply-To: <1335186937-24822-1-git-send-email-govindraj.raja@ti.com>
"Govindraj.R" <govindraj.raja@ti.com> writes:
> From: "Govindraj.R" <govindraj.raja@ti.com>
>
> With set_wakeup port ops availability from serial_core layer
> which will be called when port is opened with state as true
> indicating the wakeups can be enabled for this port and state
> as false while port shutdown where we can disable the wakeups.
>
> But wakeup can be also enabled/disabled when requested from sysfs.
> If disabled wakeup will be disabled while entering suspend and be
> enabled back based on pm state while resuming.
Nice, this is definitely the right direction. Thanks.
> Thanks to Kevin Hilman <khilman@ti.com> for suggesting this.
> Discussion References:
> http://www.spinics.net/lists/linux-omap/msg67764.html
> http://www.spinics.net/lists/linux-omap/msg67838.html
>
> Cc: Paul Walmsley <paul@pwsan.com>
> Signed-off-by: Kevin Hilman <khilman@ti.com>
> [suggestion and modification to enable and disable wakeup
> capability based on port usage]
I haven't signed-off on this, and technically since I'm not on the
delivery path, I shouldn't have a sign-off either. See
Documenation/SubmittingPatches for all the details on s-o-b tags.
I will give it an ack or tested-by after I've gone through it, but for
now I have a few minor comments below. Also just reported that l-o
master has broken UART wakeups due to the default mux settings change
earlier, so that has to be sorted out in order to properly test this.
> Signed-off-by: Govindraj.R <govindraj.raja@ti.com>
> ---
>
> Patch is tested using the patch as in here[1]
> Tested on beagle-XM by enabling and disabling wakeups
> from sysfs and opening and closing a uart port.
>
> [1]: http://marc.info/?l=linux-omap&m=133518614022144&w=2
>
>
> arch/arm/plat-omap/include/plat/omap-serial.h | 1 -
> drivers/tty/serial/omap-serial.c | 45 +++++++++++++++----------
> 2 files changed, 27 insertions(+), 19 deletions(-)
>
> diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
> index 9ff4444..8e6d734 100644
> --- a/arch/arm/plat-omap/include/plat/omap-serial.h
> +++ b/arch/arm/plat-omap/include/plat/omap-serial.h
> @@ -130,7 +130,6 @@ struct uart_omap_port {
> unsigned long port_activity;
> u32 context_loss_cnt;
> u32 errata;
> - u8 wakeups_enabled;
>
> struct pm_qos_request pm_qos_request;
> u32 latency;
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index d00b38e..b8f328e 100644
> --- a/drivers/tty/serial/omap-serial.c
> +++ b/drivers/tty/serial/omap-serial.c
> @@ -525,6 +525,7 @@ static int serial_omap_startup(struct uart_port *port)
> dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
>
> pm_runtime_get_sync(&up->pdev->dev);
> +
stray whitespace change?
> /*
> * Clear the FIFO buffers and disable them.
> * (they will be reenabled in set_termios())
> @@ -910,11 +911,27 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
> dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
> }
>
> +static int serial_omap_set_wake(struct uart_port *port, unsigned int state)
> +{
> + struct uart_omap_port *up = (struct uart_omap_port *)port;
> + struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
> + u8 enable_wakeup = false;
s/u8/bool/
> +
> + if (state)
> + enable_wakeup = true;
> +
> + if (pdata->enable_wakeup)
> + pdata->enable_wakeup(up->pdev, enable_wakeup);
but actually, the above 7 lines could be more concisely written:
if (pdata->enable_wakeup)
pdata->enable_wakeup(up->pdev, state ? true : false);
> +
> + return 0;
> +}
> +
> static void
> serial_omap_pm(struct uart_port *port, unsigned int state,
> unsigned int oldstate)
> {
> struct uart_omap_port *up = (struct uart_omap_port *)port;
> + struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
> unsigned char efr;
>
> dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->port.line);
> @@ -930,12 +947,8 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
> serial_out(up, UART_EFR, efr);
> serial_out(up, UART_LCR, 0);
>
> - if (!device_may_wakeup(&up->pdev->dev)) {
> - if (!state)
> - pm_runtime_forbid(&up->pdev->dev);
> - else
> - pm_runtime_allow(&up->pdev->dev);
> - }
> + if (!state && pdata->enable_wakeup)
> + pdata->enable_wakeup(up->pdev, true);
>
> pm_runtime_put(&up->pdev->dev);
> }
> @@ -1161,6 +1174,7 @@ static struct uart_ops serial_omap_pops = {
> .shutdown = serial_omap_shutdown,
> .set_termios = serial_omap_set_termios,
> .pm = serial_omap_pm,
> + .set_wake = serial_omap_set_wake,
> .type = serial_omap_type,
> .release_port = serial_omap_release_port,
> .request_port = serial_omap_request_port,
> @@ -1184,10 +1198,14 @@ static struct uart_driver serial_omap_reg = {
> static int serial_omap_suspend(struct device *dev)
> {
> struct uart_omap_port *up = dev_get_drvdata(dev);
> + struct omap_uart_port_info *pdata = dev->platform_data;
>
> if (up) {
> uart_suspend_port(&serial_omap_reg, &up->port);
> flush_work_sync(&up->qos_work);
> +
> + if (!device_may_wakeup(dev))
> + pdata->enable_wakeup(up->pdev, false);
If wakeups are disabled in suspend, where are they re-enabled? I don't
see anything added to the ->resume() method.
Is serial_omap_pm() called during resume to ensure wakeups are
(re)enabled?
If so this should be well described in the changelog.
Kevin
> }
>
> return 0;
> @@ -1476,6 +1494,9 @@ static int serial_omap_probe(struct platform_device *pdev)
> if (ret != 0)
> goto err_add_port;
>
> + if (omap_up_info->enable_wakeup)
> + omap_up_info->enable_wakeup(pdev, false);
> +
> pm_runtime_put(&pdev->dev);
> platform_set_drvdata(pdev, up);
> return 0;
> @@ -1582,18 +1603,6 @@ static int serial_omap_runtime_suspend(struct device *dev)
> if (pdata->get_context_loss_count)
> up->context_loss_cnt = pdata->get_context_loss_count(dev);
>
> - if (device_may_wakeup(dev)) {
> - if (!up->wakeups_enabled) {
> - pdata->enable_wakeup(up->pdev, true);
> - up->wakeups_enabled = true;
> - }
> - } else {
> - if (up->wakeups_enabled) {
> - pdata->enable_wakeup(up->pdev, false);
> - up->wakeups_enabled = false;
> - }
> - }
> -
> /* Errata i291 */
> if (up->use_dma && pdata->set_forceidle &&
> (up->errata & UART_ERRATA_i291_DMA_FORCEIDLE))
^ permalink raw reply
* trouble with interrupts on 16C
From: Stoltenberg, Matthew @ 2012-04-23 13:45 UTC (permalink / raw)
To: linux-serial@vger.kernel.org
I'm having trouble with an Oxford Semiconductor UART with the part number OXPCIe952. This is a PCI-E uart which we're using to connect to another device on our board. The CPU we're using is based on an ARM11 MPCORE.
Here's the output from lspci:
# lspci -s 05:00.1 -v
05:00.1 Serial controller: Oxford Semiconductor Ltd Device c13d (prog-if 02 [16550])
Subsystem: Oxford Semiconductor Ltd Device c13d
Flags: fast devsel, IRQ 77
Memory at 20300000 (32-bit, non-prefetchable) [size=16K]
Memory at 20600000 (32-bit, non-prefetchable) [size=2M]
Memory at 20800000 (32-bit, non-prefetchable) [size=2M]
Capabilities: [40] Power Management version 3
Capabilities: [70] Express Endpoint, MSI 00
Capabilities: [b0] MSI-X: Enable- Count=16 Masked-
Capabilities: [100] Power Budgeting <?>
Kernel driver in use: serial
>From the kernel when I modprobe 8250_pci:
[236954.361000] 1 ports detected on Oxford PCI Express device
[236954.361000] Setup PCI port: port 0, irq 77, type 2
[236954.365000] ttyS1: detected caps 00000700 should be 00000500
[236954.365000] 0000:05:00.1: ttyS1 at MMIO 0x20301000 (irq = 77) is a 16C950/954
The problem I'm having is that I almost never seem to get an interrupt when there is data ready to be read. I added a custom interrupt handler that does a printk of the iir, ier, and lsr registers after each valid interrupt. It's very rare that I see the status register say that there is data ready to be read. The other end is essentially a shell that echoes back every character as well as the results of a command with an error string for an invalid command. The only data I ever get back is a small part of the error string.
We've had some luck in uboot just polling the status register as opposed to using the interrupt. Apart from writing my own driver from scratch, is there an easy way to poll that register using the 8250 driver?
Thanks
Matt
^ permalink raw reply
* [PATCH] tty: omap-serial: configure wakeup mechanism based on port usage.
From: Govindraj.R @ 2012-04-23 13:15 UTC (permalink / raw)
To: linux-serial; +Cc: linux-omap, Govindraj.R, Paul Walmsley, Kevin Hilman
From: "Govindraj.R" <govindraj.raja@ti.com>
With set_wakeup port ops availability from serial_core layer
which will be called when port is opened with state as true
indicating the wakeups can be enabled for this port and state
as false while port shutdown where we can disable the wakeups.
But wakeup can be also enabled/disabled when requested from sysfs.
If disabled wakeup will be disabled while entering suspend and be
enabled back based on pm state while resuming.
Thanks to Kevin Hilman <khilman@ti.com> for suggesting this.
Discussion References:
http://www.spinics.net/lists/linux-omap/msg67764.html
http://www.spinics.net/lists/linux-omap/msg67838.html
Cc: Paul Walmsley <paul@pwsan.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
[suggestion and modification to enable and disable wakeup
capability based on port usage]
Signed-off-by: Govindraj.R <govindraj.raja@ti.com>
---
Patch is tested using the patch as in here[1]
Tested on beagle-XM by enabling and disabling wakeups
from sysfs and opening and closing a uart port.
[1]: http://marc.info/?l=linux-omap&m=133518614022144&w=2
arch/arm/plat-omap/include/plat/omap-serial.h | 1 -
drivers/tty/serial/omap-serial.c | 45 +++++++++++++++----------
2 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
index 9ff4444..8e6d734 100644
--- a/arch/arm/plat-omap/include/plat/omap-serial.h
+++ b/arch/arm/plat-omap/include/plat/omap-serial.h
@@ -130,7 +130,6 @@ struct uart_omap_port {
unsigned long port_activity;
u32 context_loss_cnt;
u32 errata;
- u8 wakeups_enabled;
struct pm_qos_request pm_qos_request;
u32 latency;
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index d00b38e..b8f328e 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -525,6 +525,7 @@ static int serial_omap_startup(struct uart_port *port)
dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
pm_runtime_get_sync(&up->pdev->dev);
+
/*
* Clear the FIFO buffers and disable them.
* (they will be reenabled in set_termios())
@@ -910,11 +911,27 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
}
+static int serial_omap_set_wake(struct uart_port *port, unsigned int state)
+{
+ struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
+ u8 enable_wakeup = false;
+
+ if (state)
+ enable_wakeup = true;
+
+ if (pdata->enable_wakeup)
+ pdata->enable_wakeup(up->pdev, enable_wakeup);
+
+ return 0;
+}
+
static void
serial_omap_pm(struct uart_port *port, unsigned int state,
unsigned int oldstate)
{
struct uart_omap_port *up = (struct uart_omap_port *)port;
+ struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
unsigned char efr;
dev_dbg(up->port.dev, "serial_omap_pm+%d\n", up->port.line);
@@ -930,12 +947,8 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
serial_out(up, UART_EFR, efr);
serial_out(up, UART_LCR, 0);
- if (!device_may_wakeup(&up->pdev->dev)) {
- if (!state)
- pm_runtime_forbid(&up->pdev->dev);
- else
- pm_runtime_allow(&up->pdev->dev);
- }
+ if (!state && pdata->enable_wakeup)
+ pdata->enable_wakeup(up->pdev, true);
pm_runtime_put(&up->pdev->dev);
}
@@ -1161,6 +1174,7 @@ static struct uart_ops serial_omap_pops = {
.shutdown = serial_omap_shutdown,
.set_termios = serial_omap_set_termios,
.pm = serial_omap_pm,
+ .set_wake = serial_omap_set_wake,
.type = serial_omap_type,
.release_port = serial_omap_release_port,
.request_port = serial_omap_request_port,
@@ -1184,10 +1198,14 @@ static struct uart_driver serial_omap_reg = {
static int serial_omap_suspend(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
+ struct omap_uart_port_info *pdata = dev->platform_data;
if (up) {
uart_suspend_port(&serial_omap_reg, &up->port);
flush_work_sync(&up->qos_work);
+
+ if (!device_may_wakeup(dev))
+ pdata->enable_wakeup(up->pdev, false);
}
return 0;
@@ -1476,6 +1494,9 @@ static int serial_omap_probe(struct platform_device *pdev)
if (ret != 0)
goto err_add_port;
+ if (omap_up_info->enable_wakeup)
+ omap_up_info->enable_wakeup(pdev, false);
+
pm_runtime_put(&pdev->dev);
platform_set_drvdata(pdev, up);
return 0;
@@ -1582,18 +1603,6 @@ static int serial_omap_runtime_suspend(struct device *dev)
if (pdata->get_context_loss_count)
up->context_loss_cnt = pdata->get_context_loss_count(dev);
- if (device_may_wakeup(dev)) {
- if (!up->wakeups_enabled) {
- pdata->enable_wakeup(up->pdev, true);
- up->wakeups_enabled = true;
- }
- } else {
- if (up->wakeups_enabled) {
- pdata->enable_wakeup(up->pdev, false);
- up->wakeups_enabled = false;
- }
- }
-
/* Errata i291 */
if (up->use_dma && pdata->set_forceidle &&
(up->errata & UART_ERRATA_i291_DMA_FORCEIDLE))
--
1.7.9
^ permalink raw reply related
* [PATCH] tty: serial_core: Utilise/Enable the set_wake uart ops
From: Govindraj.R @ 2012-04-23 13:00 UTC (permalink / raw)
To: linux-serial; +Cc: linux-omap, Govindraj.R, Greg Kroah-Hartman, Alan Cox
From: "Govindraj.R" <govindraj.raja@ti.com>
Currently all low level uart driver register to serial_core layer
this core layer exposes various serial uart ops.
Whenever port is opened the low level platform specific wakeup
capability can be enabled use the already available set_wakeup uart ops
to configure wakeup capability.
When port is opened the wakeup can be enabled and when port is shutdown
the wakeups can be disabled.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Alan Cox <alan@linux.intel.com>
Signed-off-by: Govindraj.R <govindraj.raja@ti.com>
---
drivers/tty/serial/serial_core.c | 14 ++++++++++++++
1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 9c4c05b..44b81aa 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -185,6 +185,13 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
}
/*
+ * Now with port open enable any platform specific wakeup
+ * capability for the port if available.
+ */
+ if (uport->ops->set_wake)
+ uport->ops->set_wake(uport, true);
+
+ /*
* This is to allow setserial on this port. People may want to set
* port/irq/type and then reconfigure the port properly if it failed
* now.
@@ -1427,6 +1434,13 @@ static void uart_port_shutdown(struct tty_port *port)
uport->ops->shutdown(uport);
/*
+ * now port is closed disable any platform
+ * specific wakeup capability that might be enabled.
+ */
+ if (uport->ops->set_wake)
+ uport->ops->set_wake(uport, false);
+
+ /*
* Ensure that the IRQ handler isn't running on another CPU.
*/
synchronize_irq(uport->irq);
--
1.7.9
^ permalink raw reply related
* Re: [Query] serial: AT91: Add SIM Driver
From: Alan Cox @ 2012-04-23 10:54 UTC (permalink / raw)
To: Chen, Bo
Cc: alan@linux.intel.com, rick@efn.org, linux-serial@vger.kernel.org,
linux-arm-kernel@lists.arm.linux.org.uk,
linux-kernel@vger.kernel.org
In-Reply-To: <7AD597EA49E297498E79AFA326C986BB0D317277@penmbx01>
> I mean to create a char driver which contains ioctl interface to control GPIOs connected to SIM card VCC and RESET.
We have a GPIO layer and existing GPIO interface support so the gpio side
if they are kept independent ought to be easy. However I wonder if the
separate gpio control will make the userspace side harder ?
> Thirdly, I want to use usual tty function (read / write) to implement the ATR / send or receive command / PPS.
> That means tty usual driver read or write will be called by user space application such as openct or else to realize the ATR and PPS.
> Openct may also call above char driver to realize the power or reset function.
That may be difficult to get right because of the tty object lifetime
and the locking rules. One way would be to make ISO7816 a line discipline
(the layer which sits about the tty).
Your user space interface would then be
int ldisc = N_ISO7816;
ioctl(tty_fd, TIOCSETD, &ldisc);
at which point the read/write/ioctl etc calls on your tty will also be
routed via your ldisc so you can manage them and provide ISO7816 sensible
interfaces.
You'd still need a way for the ldisc to ask the tty device to manage the
GPIO lines but I don't think that is too tricky to add.
That way it would at least keep everything in one place and avoid adding
new strange paths calling into the tty layer code via non tty devices.
Take a look at the gsmld_ parts of drivers/tty/n_gsm.c for a fairly full
example of a line discipline. You can ignore most of the rest of the code
there, the rest is implementing GSM mux and virtual ttys.
The two thing s it doesn't use are
tty_set_termios(tty, &whatever)
which is a helper for the ldisc or similar to set the terminal state
itself.
and
tty->driver->ops->tiocmget/tiocmset/etc
for doing things like modem lines. One way to handle the gpios
transparently might be to map them to the nearest equivalent 'modem' line
when in ISO7816 mode.
The tty also sees the ldisc change via tty->ops->set_ldisc(). This is
used by some drivers already for things like automatically selecting IRDA
FIR mode when in IRDA modes.
In your case the driver would set/exit ISO7816 mode when it saw this.
Alan
^ permalink raw reply
* Re: [PATCH] tty: serial_core: Add mechanism to identify port closure.
From: Alan Cox @ 2012-04-23 10:07 UTC (permalink / raw)
To: Raja, Govindraj; +Cc: linux-serial, linux-omap, Greg Kroah-Hartman, Alan Cox
In-Reply-To: <CAMrsUdLaamf9boyMF824iTXdDce0qB3AiUfMiM3a1gL2DcVPqA@mail.gmail.com>
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index 9c4c05b..c176ff2 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -1426,6 +1426,9 @@ static void uart_port_shutdown(struct tty_port *port)
> */
> uport->ops->shutdown(uport);
>
> + if (uport->ops->set_wake)
> + uport->ops->set_wake(uport, false);
> +
> /*
> * Ensure that the IRQ handler isn't running on another CPU.
> */
> @@ -1512,6 +1515,9 @@ static int uart_open(struct tty_struct *tty,
> struct file *filp)
> goto err_dec_count;
> }
>
> + if (uport->ops->set_wake)
> + uport->ops->set_wake(uport, true);
> +
> /*
> * Make sure the device is in D0 state.
> */
This is probably the right approach for now. In the ideal world we should
probably use the struct device * and device power management but that is
a big upheaval and would mean fixing lots of other stuff first.
So this looks good to me - providing it doesn't break anything else.
Alan
^ permalink raw reply
* Re: [PATCH] tty: serial_core: Add mechanism to identify port closure.
From: Raja, Govindraj @ 2012-04-23 8:55 UTC (permalink / raw)
To: Alan Cox; +Cc: linux-serial, linux-omap, Greg Kroah-Hartman, Alan Cox
In-Reply-To: <20120420142740.053b9981@pyramind.ukuu.org.uk>
Hi Alan,
On Fri, Apr 20, 2012 at 6:57 PM, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
>> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
>> index 9c4c05b..0dc246d 100644
>> --- a/drivers/tty/serial/serial_core.c
>> +++ b/drivers/tty/serial/serial_core.c
>> @@ -1284,6 +1284,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
>> uart_wait_until_sent(tty, uport->timeout);
>> }
>>
>> + state->uart_port->closing = true;
>
> So what are the locking rules for this - when is it valid and when can it
> be tested.
>
> This also still seems a hack to me - the core tty code doesn't have
> suspend/resume/open/close confused. The uart layer does, so really the
> uart layer needs untangling. I'm skeptical yet another magic state
> variable is the answer, particularly when the locking model for the
> variable appears undefined.
>
> Teach the uart core code to pass an extra argument indicating whether its
> really doing shutdown/open or merely doing suspend/resume. It's perfectly
> sensible that it tries to use the same helper for both, its broken that
> the called code cannot tell which.
>
> The parameter would also be a local variable which avoids all the locking
> questions.
Yes adding a _state_ local variable from port_shutdown from serial_core layer
and passing the same to low level driver can inform the low level driver.
whether current ops is shutdown or a suspend.
Also, looking into the struct uart_ops.
struct uart_ops {
.
.
int (*set_wake)(struct uart_port *, unsigned int state);
.
.
};
Unfortunately set wake is not being used in serial_core.c
So this uart ops can be used to enable wakeups
when port is opened and disable wakeups when port
is closed.
If set_wake is not supposed to be used in this manner I
will fall back to option1 to use state variable for shutdown ops.
tmp patch as in here[1] to use set_wake.
--
Thanks,
Govindraj.R
[1]:
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 9c4c05b..c176ff2 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1426,6 +1426,9 @@ static void uart_port_shutdown(struct tty_port *port)
*/
uport->ops->shutdown(uport);
+ if (uport->ops->set_wake)
+ uport->ops->set_wake(uport, false);
+
/*
* Ensure that the IRQ handler isn't running on another CPU.
*/
@@ -1512,6 +1515,9 @@ static int uart_open(struct tty_struct *tty,
struct file *filp)
goto err_dec_count;
}
+ if (uport->ops->set_wake)
+ uport->ops->set_wake(uport, true);
+
/*
* Make sure the device is in D0 state.
*/
--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [GIT PATCH] TTY/serial patches for 3.4-rc4
From: Greg KH @ 2012-04-20 17:21 UTC (permalink / raw)
To: Linus Torvalds, Andrew Morton; +Cc: linux-kernel, linux-serial
The following changes since commit e816b57a337ea3b755de72bec38c10c864f23015:
Linux 3.4-rc3 (2012-04-15 18:28:29 -0700)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git/ tags/tty-3.4-rc4
for you to fetch changes up to d3a7b83f865b46bb7b5e1ed18a129ce1af349db4:
drivers/tty/amiserial.c: add missing tty_unlock (2012-04-19 19:15:35 -0700)
----------------------------------------------------------------
TTY fixes for 3.4-rc4
Here are 3 tiny bugfixes for 3.4-rc4.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
----------------------------------------------------------------
Alexander Shiyan (1):
ARM: clps711x: serial driver hungs are a result of call disable_irq within ISR
Julia Lawall (1):
drivers/tty/amiserial.c: add missing tty_unlock
Tomoya MORINAGA (1):
pch_uart: Fix dma channel unallocated issue
drivers/tty/amiserial.c | 4 +++-
drivers/tty/serial/clps711x.c | 14 ++++++++------
drivers/tty/serial/pch_uart.c | 4 +++-
3 files changed, 14 insertions(+), 8 deletions(-)
^ permalink raw reply
* Re: [PATCH 2/7] UART: OMAP: Cut the clock in the error cases
From: Kevin Hilman @ 2012-04-20 13:46 UTC (permalink / raw)
To: Shubhrajyoti Datta
Cc: Shubhrajyoti, linux-serial, linux-omap, linux-arm-kernel, stable,
Govindraj.R
In-Reply-To: <CAM=Q2cvRs2z7sf1ZeU-tMAZLgjPSM5V2fABLyj7087drThf44A@mail.gmail.com>
Shubhrajyoti Datta <omaplinuxkernel@gmail.com> writes:
> On Wed, Apr 18, 2012 at 8:43 PM, Shubhrajyoti <shubhrajyoti@ti.com> wrote:
>>>
>>> Kevin
>> Yes agree completely. Will describe that in the changelog.
>> --
> Does the following changelog look ok?
A little better, but still doesn't explain things so that someone who is
not intimately familar with the driver can understand the auto-suspend
version is needed in the one case.
Possibly explaining in more detail what would happen if the normal put
is used here instead of the autosuspend version might help.
Kevin
> From 37fdc2d40c9b2b19b8c5a9a4b8f7dd547d420f55 Mon Sep 17 00:00:00 2001
> From: Shubhrajyoti D <shubhrajyoti@ti.com>
> Date: Wed, 4 Apr 2012 16:32:37 +0530
> Subject: [PATCH] UART: OMAP: call pm_runtime_put/autosuspend in the error cases
>
> In the error cases the runtime_put call is missed. This patch intends to fix the
> same. In case dma request fails, we fall back to the nondma mode so after
> enabling the threshold call put_autosuspend.
>
> Cc: Govindraj.R <govindraj.raja@ti.com>
> Signed-off-by: Shubhrajyoti D <shubhrajyoti@ti.com>
> ---
> drivers/tty/serial/omap-serial.c | 6 +++++-
> 1 files changed, 5 insertions(+), 1 deletions(-)
>
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index fe099bb..10e80bb 100644
> --- a/drivers/tty/serial/omap-serial.c
> +++ b/drivers/tty/serial/omap-serial.c
> @@ -319,6 +319,8 @@ static void serial_omap_start_tx(struct uart_port *port)
>
> if (ret < 0) {
> serial_omap_enable_ier_thri(up);
> + pm_runtime_mark_last_busy(&up->pdev->dev);
> + pm_runtime_put_autosuspend(&up->pdev->dev);
> return;
> }
> }
> @@ -1029,8 +1031,10 @@ static int serial_omap_poll_get_char(struct
> uart_port *port)
>
> pm_runtime_get_sync(&up->pdev->dev);
> status = serial_in(up, UART_LSR);
> - if (!(status & UART_LSR_DR))
> + if (!(status & UART_LSR_DR)) {
> + pm_runtime_put(&up->pdev->dev);
> return NO_POLL_CHAR;
> + }
>
> status = serial_in(up, UART_RX);
> pm_runtime_put(&up->pdev->dev);
^ permalink raw reply
* Re: [PATCH] tty: serial_core: Add mechanism to identify port closure.
From: Alan Cox @ 2012-04-20 13:27 UTC (permalink / raw)
To: Govindraj.R; +Cc: linux-serial, linux-omap, Greg Kroah-Hartman, Alan Cox
In-Reply-To: <1334919473-18870-1-git-send-email-govindraj.raja@ti.com>
> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index 9c4c05b..0dc246d 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -1284,6 +1284,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
> uart_wait_until_sent(tty, uport->timeout);
> }
>
> + state->uart_port->closing = true;
So what are the locking rules for this - when is it valid and when can it
be tested.
This also still seems a hack to me - the core tty code doesn't have
suspend/resume/open/close confused. The uart layer does, so really the
uart layer needs untangling. I'm skeptical yet another magic state
variable is the answer, particularly when the locking model for the
variable appears undefined.
Teach the uart core code to pass an extra argument indicating whether its
really doing shutdown/open or merely doing suspend/resume. It's perfectly
sensible that it tries to use the same helper for both, its broken that
the called code cannot tell which.
The parameter would also be a local variable which avoids all the locking
questions.
Alan
^ permalink raw reply
* Re: [PATCH 2/7] UART: OMAP: Cut the clock in the error cases
From: Shubhrajyoti Datta @ 2012-04-20 13:20 UTC (permalink / raw)
To: Shubhrajyoti
Cc: Kevin Hilman, linux-serial, linux-omap, linux-arm-kernel, stable,
Govindraj.R
In-Reply-To: <4F8EDA24.5020209@ti.com>
On Wed, Apr 18, 2012 at 8:43 PM, Shubhrajyoti <shubhrajyoti@ti.com> wrote:
>>
>> Kevin
> Yes agree completely. Will describe that in the changelog.
> --
Does the following changelog look ok?
>From 37fdc2d40c9b2b19b8c5a9a4b8f7dd547d420f55 Mon Sep 17 00:00:00 2001
From: Shubhrajyoti D <shubhrajyoti@ti.com>
Date: Wed, 4 Apr 2012 16:32:37 +0530
Subject: [PATCH] UART: OMAP: call pm_runtime_put/autosuspend in the error cases
In the error cases the runtime_put call is missed. This patch intends to fix the
same. In case dma request fails, we fall back to the nondma mode so after
enabling the threshold call put_autosuspend.
Cc: Govindraj.R <govindraj.raja@ti.com>
Signed-off-by: Shubhrajyoti D <shubhrajyoti@ti.com>
---
drivers/tty/serial/omap-serial.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index fe099bb..10e80bb 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -319,6 +319,8 @@ static void serial_omap_start_tx(struct uart_port *port)
if (ret < 0) {
serial_omap_enable_ier_thri(up);
+ pm_runtime_mark_last_busy(&up->pdev->dev);
+ pm_runtime_put_autosuspend(&up->pdev->dev);
return;
}
}
@@ -1029,8 +1031,10 @@ static int serial_omap_poll_get_char(struct
uart_port *port)
pm_runtime_get_sync(&up->pdev->dev);
status = serial_in(up, UART_LSR);
- if (!(status & UART_LSR_DR))
+ if (!(status & UART_LSR_DR)) {
+ pm_runtime_put(&up->pdev->dev);
return NO_POLL_CHAR;
+ }
status = serial_in(up, UART_RX);
pm_runtime_put(&up->pdev->dev);
--
1.7.5.4
^ permalink raw reply related
* [PATCH] tty: serial_core: Add mechanism to identify port closure.
From: Govindraj.R @ 2012-04-20 10:57 UTC (permalink / raw)
To: linux-serial; +Cc: linux-omap, Govindraj.R, Greg Kroah-Hartman, Alan Cox
From: "Govindraj.R" <govindraj.raja@ti.com>
Currently all low level uart driver register to serial_core layer
this core layer exposes various serial uart ops.
Currently the core layer provides startup and shutdown hooks
to low level driver, but in port suspend case the shutdown gets called
from uart_suspend_port this makes little difficult in low level driver
to any action specific to port closure.
For example if low level driver tries to disable uart platform
specific wakeups after port closure in low level driver it can be only done
in shutdown ops since this ops is called even in suspend case low level
driver needs some info whether the current shutdown ops is result of
port close or if its suspend ops.
To differentiate this expand the core layer uart_port info with closing flag
this flag is set to true during shutdown and set to false while port open.
Thus closing flag added with this patch can be used in low level driver to
identify the port close.
Discussion reference:
http://marc.info/?l=linux-omap&m=133491321605924&w=2
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Alan Cox <alan@linux.intel.com>
Signed-off-by: Govindraj.R <govindraj.raja@ti.com>
---
drivers/tty/serial/serial_core.c | 3 +++
include/linux/serial_core.h | 1 +
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 9c4c05b..0dc246d 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -1284,6 +1284,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
uart_wait_until_sent(tty, uport->timeout);
}
+ state->uart_port->closing = true;
+
mutex_lock(&port->mutex);
uart_shutdown(tty, state);
uart_flush_buffer(tty);
@@ -1518,6 +1520,7 @@ static int uart_open(struct tty_struct *tty, struct file *filp)
if (port->count == 1)
uart_change_pm(state, 0);
+ state->uart_port->closing = false;
/*
* Start up the serial port.
*/
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 2db407a..f5cd1ee 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -381,6 +381,7 @@ struct uart_port {
unsigned char irq_wake;
unsigned char unused[2];
void *private_data; /* generic platform data pointer */
+ unsigned char closing;
};
static inline int serial_port_in(struct uart_port *up, int offset)
--
1.7.9
^ permalink raw reply related
* Re: [PATCH] tty: omap-serial: Keep the wakeup mechanism enabled by default
From: Alan Cox @ 2012-04-20 9:13 UTC (permalink / raw)
To: Raja, Govindraj
Cc: Kevin Hilman, linux-omap, Paul Walmsley, linux-arm-kernel,
linux-serial
In-Reply-To: <CAMrsUdJ_ewe1td9nOO+0ociP4O_tyPgjH=w11r_mFW_yc7PDAQ@mail.gmail.com>
On Thu, 19 Apr 2012 20:00:12 +0530
"Raja, Govindraj" <govindraj.raja@ti.com> wrote:
> On Thu, Apr 19, 2012 at 12:38 AM, Alan Cox <alan@lxorguk.ukuu.org.uk> wrote:
> >> The point is that wakeups should be enabled whenever driver is in use,
> >> and disabled when the driver is not in use.
> >
> >
> > Which is the tty_port methods for initialisation and shutdown, which are
> > mutex protected against each other and hang up.
> >
> > Not sure how the uart layer glue exposes that.
>
> Is it okay to read the port flags to identify the port state in
> driver whether the serial port shutdown ops is called from
> port_suspend or due to port closure.
>
> Since port_shutdown gets called even from uart_suspend_port
That sounds like someone needs to fix the uart code to avoid muddling up
suspend/resume/open/close then, or at least pass the needed information
down.
Don't rely on port->flags ideally, they may well go away in part as they
cease to be needed by the lock changes.
Alan
^ permalink raw reply
* Re: Deterministic behavior for TTY serial
From: Greg KH @ 2012-04-19 15:46 UTC (permalink / raw)
To: Ivo Sieben; +Cc: linux-serial, Alan Cox, RT
In-Reply-To: <CAMSQXEEkssPGWYgC4dkpKEhfrB4s8yOoxiNNVdRRX7r+b91EMQ@mail.gmail.com>
On Thu, Apr 19, 2012 at 05:37:56PM +0200, Ivo Sieben wrote:
> Hi Greg,
>
> Op 19 april 2012 02:14 heeft Greg KH <gregkh@linuxfoundation.org> het
> volgende geschreven:
> > On Tue, Apr 17, 2012 at 04:38:30PM +0200, Ivo Sieben wrote:
> >> Hello,
> >>
> >> We are currently using the TTY framework for serial communication.
> >>
> >> We are wondering if it is possible to give the TTY device in more
> >> deterministic behavior (as in "less locks & no sleeping")
> >
> > What specifically are you looking for?
> >
>
> We run an application with a real-time thread, running on a high priority.
> This thread does serial communication, using non blocking read/write
> file I/O on a tty device, with small amounts of data (= 24 bytes).
> This application runs on a AT2AM9261 processor, 200 MHz
> The maximum execution time of both the read & write go up to 200 us
>
> >> So in case of non blocking read/write behavior:
> >> - We want directly write data to the serial_core transmit buffer and
> >> return immediately.
> >
> > What is "immediately"?
> >
>
> We use non blocking read & write functions
> We would like the read/write functions to always execute less than 100us
Ok, and are you sure that your processor can even do something like
this? Where is the time being spent when you make these calls? A read
function should never hit the hardware, only retrieving data from a
buffer in memory, so if your processor can go this fast, it should be
fine.
Have you done profiling to determine exactly what it taking "too long"
for you? If so, what is the delay? If not, you should do this :)
> We use a self written serial_core device uart driver that implements a
> driver for a UART peripheral in a FPGA on our target board..
Do you have a pointer to the driver anywhere? Why isn't it submitted
for inclusion in the main kernel tree?
thanks,
greg k-h
^ permalink raw reply
* Re: Deterministic behavior for TTY serial
From: Ivo Sieben @ 2012-04-19 15:42 UTC (permalink / raw)
To: Alan Cox; +Cc: linux-serial, RT, Greg KH
In-Reply-To: <20120419121938.1215b8c1@bob.linux.org.uk>
Hi Alan,
Op 19 april 2012 13:19 heeft Alan Cox <alan@linux.intel.com> het
volgende geschreven:
>> We are wondering if it is possible to give the TTY device in more
>> deterministic behavior (as in "less locks & no sleeping")
>> So in case of non blocking read/write behavior:
>> - We want directly write data to the serial_core transmit buffer and
>> return immediately.
>
> If you have the tty in raw mode then that is basically what the ldisc
> code does (plus any flow control you may have selected).
>
>> - Incoming data should be buffered, on a read data is read directly
>> from that buffer and when no data available return immediately
>
> Ditto in raw mode, and you can use O_NDELAY or the VMIN/VTIME fields to
> optimise block transfer behaviour. We do actually do an additional
> memcpy but memory copies of cached memory are so cheap it should be
> irrelevant unless trying to do megabit speeds on low end embedded
> processors.
>
I assume we use raw mode:
- We call the cfmakeraw() function.
- We also set the O_NDELAY flag, VMIN = 1, VTIME = 0
>> We have the idea that the default N_TTY line discipline introduces too
>
> Based upon what analysis ?
>
I think you are right: I did not yet do "real" analysis on the N_TTY
line discipline apart from source code analysis.
I will do some extra ftrace tests, to find out what causes the delays
in the execution time.
I'll come back to that...
>> much overhead & locking behavior what makes it less suitable for
>> deterministic serial communication on a PREEMT_RT platform.
>
> Are you using USB ports ?
>
No, it is a UART peripheral in a FPGA on our target board.
We use a self written serial_core base driver
> Alan
Thanks,
Ivo Sieben
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox