summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/imx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/imx.c')
-rw-r--r--drivers/tty/serial/imx.c99
1 files changed, 72 insertions, 27 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index e3e152cbc75e..33509b4beaec 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -719,6 +719,27 @@ out:
return IRQ_HANDLED;
}
+static void imx_disable_rx_int(struct imx_port *sport)
+{
+ unsigned long temp;
+
+ sport->dma_is_rxing = 1;
+
+ /* disable the receiver ready and aging timer interrupts */
+ temp = readl(sport->port.membase + UCR1);
+ temp &= ~(UCR1_RRDYEN);
+ writel(temp, sport->port.membase + UCR1);
+
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~(UCR2_ATEN);
+ writel(temp, sport->port.membase + UCR2);
+
+ /* disable the rx errors interrupts */
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~UCR4_OREN;
+ writel(temp, sport->port.membase + UCR4);
+}
+
static void clear_rx_errors(struct imx_port *sport);
static int start_rx_dma(struct imx_port *sport);
/*
@@ -734,21 +755,8 @@ static void imx_dma_rxint(struct imx_port *sport)
temp = readl(sport->port.membase + USR2);
if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
- sport->dma_is_rxing = 1;
- /* disable the receiver ready and aging timer interrupts */
- temp = readl(sport->port.membase + UCR1);
- temp &= ~(UCR1_RRDYEN);
- writel(temp, sport->port.membase + UCR1);
-
- temp = readl(sport->port.membase + UCR2);
- temp &= ~(UCR2_ATEN);
- writel(temp, sport->port.membase + UCR2);
-
- /* disable the rx errors interrupts */
- temp = readl(sport->port.membase + UCR4);
- temp &= ~UCR4_OREN;
- writel(temp, sport->port.membase + UCR4);
+ imx_disable_rx_int(sport);
/* tell the DMA to receive the data. */
start_rx_dma(sport);
@@ -1317,19 +1325,10 @@ static int imx_startup(struct uart_port *port)
if (!is_imx1_uart(sport)) {
temp = readl(sport->port.membase + UCR3);
- /*
- * The effect of RI and DCD differs depending on the UFCR_DCEDTE
- * bit. In DCE mode they control the outputs, in DTE mode they
- * enable the respective irqs. At least the DCD irq cannot be
- * cleared on i.MX25 at least, so it's not usable and must be
- * disabled. I don't have test hardware to check if RI has the
- * same problem but I consider this likely so it's disabled for
- * now, too.
- */
- temp |= IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP |
- UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
+ temp |= UCR3_DTRDEN | UCR3_RI | UCR3_DCD;
if (sport->dte_mode)
+ /* disable broken interrupts */
temp &= ~(UCR3_RI | UCR3_DCD);
writel(temp, sport->port.membase + UCR3);
@@ -1339,6 +1338,33 @@ static int imx_startup(struct uart_port *port)
* Enable modem status interrupts
*/
imx_enable_ms(&sport->port);
+
+ /*
+ * If the serial port is opened for reading start RX DMA immediately
+ * instead of waiting for RX FIFO interrupts. In our iMX53 the average
+ * delay for the first reception dropped from approximately 35000
+ * microseconds to 1000 microseconds.
+ */
+ if (sport->dma_is_enabled) {
+ struct tty_struct *tty = sport->port.state->port.tty;
+ struct tty_file_private *file_priv;
+ int readcnt = 0;
+
+ spin_lock(&tty->files_lock);
+
+ if (!list_empty(&tty->tty_files))
+ list_for_each_entry(file_priv, &tty->tty_files, list)
+ if (!(file_priv->file->f_flags & O_WRONLY))
+ readcnt++;
+
+ spin_unlock(&tty->files_lock);
+
+ if (readcnt > 0) {
+ imx_disable_rx_int(sport);
+ start_rx_dma(sport);
+ }
+ }
+
spin_unlock_irqrestore(&sport->port.lock, flags);
return 0;
@@ -1584,8 +1610,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
ufcr = readl(sport->port.membase + UFCR);
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
- if (sport->dte_mode)
- ufcr |= UFCR_DCEDTE;
writel(ufcr, sport->port.membase + UFCR);
writel(num, sport->port.membase + UBIR);
@@ -2153,6 +2177,27 @@ static int serial_imx_probe(struct platform_device *pdev)
UCR1_TXMPTYEN | UCR1_RTSDEN);
writel_relaxed(reg, sport->port.membase + UCR1);
+ if (!is_imx1_uart(sport) && sport->dte_mode) {
+ /*
+ * The DCEDTE bit changes the direction of DSR, DCD, DTR and RI
+ * and influences if UCR3_RI and UCR3_DCD changes the level of RI
+ * and DCD (when they are outputs) or enables the respective
+ * irqs. So set this bit early, i.e. before requesting irqs.
+ */
+ writel(UFCR_DCEDTE, sport->port.membase + UFCR);
+
+ /*
+ * Disable UCR3_RI and UCR3_DCD irqs. They are also not
+ * enabled later because they cannot be cleared
+ * (confirmed on i.MX25) which makes them unusable.
+ */
+ writel(IMX21_UCR3_RXDMUXSEL | UCR3_ADNIMP | UCR3_DSR,
+ sport->port.membase + UCR3);
+
+ } else {
+ writel(0, sport->port.membase + UFCR);
+ }
+
clk_disable_unprepare(sport->clk_ipg);
/*