summaryrefslogtreecommitdiffstats
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.c157
1 files changed, 92 insertions, 65 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 57d6e6ba556e..0c6c63166250 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -8,10 +8,6 @@
* Copyright (C) 2004 Pengutronix
*/
-#if defined(CONFIG_SERIAL_IMX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
@@ -402,12 +398,6 @@ static void imx_uart_rts_inactive(struct imx_port *sport, u32 *ucr2)
mctrl_gpio_set(sport->gpios, sport->port.mctrl);
}
-/* called with port.lock taken and irqs caller dependent */
-static void imx_uart_rts_auto(struct imx_port *sport, u32 *ucr2)
-{
- *ucr2 |= UCR2_CTSC;
-}
-
/* called with port.lock taken and irqs off */
static void imx_uart_start_rx(struct uart_port *port)
{
@@ -445,7 +435,7 @@ static void imx_uart_stop_tx(struct uart_port *port)
return;
ucr1 = imx_uart_readl(sport, UCR1);
- imx_uart_writel(sport, ucr1 & ~UCR1_TXMPTYEN, UCR1);
+ imx_uart_writel(sport, ucr1 & ~UCR1_TRDYEN, UCR1);
/* in rs485 mode disable transmitter if shifter is empty */
if (port->rs485.flags & SER_RS485_ENABLED &&
@@ -523,7 +513,7 @@ static inline void imx_uart_transmit_buffer(struct imx_port *sport)
* and the TX IRQ is disabled.
**/
ucr1 = imx_uart_readl(sport, UCR1);
- ucr1 &= ~UCR1_TXMPTYEN;
+ ucr1 &= ~UCR1_TRDYEN;
if (sport->dma_is_txing) {
ucr1 |= UCR1_TXDMAEN;
imx_uart_writel(sport, ucr1, UCR1);
@@ -625,7 +615,7 @@ static void imx_uart_dma_tx(struct imx_port *sport)
dev_err(dev, "DMA mapping error for TX.\n");
return;
}
- desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
+ desc = dmaengine_prep_slave_sg(chan, sgl, ret,
DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
if (!desc) {
dma_unmap_sg(dev, sgl, sport->dma_tx_nents,
@@ -685,7 +675,7 @@ static void imx_uart_start_tx(struct uart_port *port)
if (!sport->dma_is_enabled) {
ucr1 = imx_uart_readl(sport, UCR1);
- imx_uart_writel(sport, ucr1 | UCR1_TXMPTYEN, UCR1);
+ imx_uart_writel(sport, ucr1 | UCR1_TRDYEN, UCR1);
}
if (sport->dma_is_enabled) {
@@ -694,7 +684,7 @@ static void imx_uart_start_tx(struct uart_port *port)
* disable TX DMA to let TX interrupt to send X-char */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~UCR1_TXDMAEN;
- ucr1 |= UCR1_TXMPTYEN;
+ ucr1 |= UCR1_TRDYEN;
imx_uart_writel(sport, ucr1, UCR1);
return;
}
@@ -706,22 +696,33 @@ static void imx_uart_start_tx(struct uart_port *port)
}
}
-static irqreturn_t imx_uart_rtsint(int irq, void *dev_id)
+static irqreturn_t __imx_uart_rtsint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
u32 usr1;
- spin_lock(&sport->port.lock);
-
imx_uart_writel(sport, USR1_RTSD, USR1);
usr1 = imx_uart_readl(sport, USR1) & USR1_RTSS;
uart_handle_cts_change(&sport->port, !!usr1);
wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
- spin_unlock(&sport->port.lock);
return IRQ_HANDLED;
}
+static irqreturn_t imx_uart_rtsint(int irq, void *dev_id)
+{
+ struct imx_port *sport = dev_id;
+ irqreturn_t ret;
+
+ spin_lock(&sport->port.lock);
+
+ ret = __imx_uart_rtsint(irq, dev_id);
+
+ spin_unlock(&sport->port.lock);
+
+ return ret;
+}
+
static irqreturn_t imx_uart_txint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
@@ -732,14 +733,12 @@ static irqreturn_t imx_uart_txint(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
+static irqreturn_t __imx_uart_rxint(int irq, void *dev_id)
{
struct imx_port *sport = dev_id;
unsigned int rx, flg, ignored = 0;
struct tty_port *port = &sport->port.state->port;
- spin_lock(&sport->port.lock);
-
while (imx_uart_readl(sport, USR2) & USR2_RDR) {
u32 usr2;
@@ -785,9 +784,7 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
if (rx & URXD_OVRRUN)
flg = TTY_OVERRUN;
-#ifdef SUPPORT_SYSRQ
sport->port.sysrq = 0;
-#endif
}
if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
@@ -798,11 +795,25 @@ static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
}
out:
- spin_unlock(&sport->port.lock);
tty_flip_buffer_push(port);
+
return IRQ_HANDLED;
}
+static irqreturn_t imx_uart_rxint(int irq, void *dev_id)
+{
+ struct imx_port *sport = dev_id;
+ irqreturn_t ret;
+
+ spin_lock(&sport->port.lock);
+
+ ret = __imx_uart_rxint(irq, dev_id);
+
+ spin_unlock(&sport->port.lock);
+
+ return ret;
+}
+
static void imx_uart_clear_rx_errors(struct imx_port *sport);
/*
@@ -861,6 +872,8 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4;
irqreturn_t ret = IRQ_NONE;
+ spin_lock(&sport->port.lock);
+
usr1 = imx_uart_readl(sport, USR1);
usr2 = imx_uart_readl(sport, USR2);
ucr1 = imx_uart_readl(sport, UCR1);
@@ -880,7 +893,7 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
usr1 &= ~USR1_RRDY;
if ((ucr2 & UCR2_ATEN) == 0)
usr1 &= ~USR1_AGTIM;
- if ((ucr1 & UCR1_TXMPTYEN) == 0)
+ if ((ucr1 & UCR1_TRDYEN) == 0)
usr1 &= ~USR1_TRDY;
if ((ucr4 & UCR4_TCEN) == 0)
usr2 &= ~USR2_TXDC;
@@ -894,27 +907,25 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
usr2 &= ~USR2_ORE;
if (usr1 & (USR1_RRDY | USR1_AGTIM)) {
- imx_uart_rxint(irq, dev_id);
+ __imx_uart_rxint(irq, dev_id);
ret = IRQ_HANDLED;
}
if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) {
- imx_uart_txint(irq, dev_id);
+ imx_uart_transmit_buffer(sport);
ret = IRQ_HANDLED;
}
if (usr1 & USR1_DTRD) {
imx_uart_writel(sport, USR1_DTRD, USR1);
- spin_lock(&sport->port.lock);
imx_uart_mctrl_check(sport);
- spin_unlock(&sport->port.lock);
ret = IRQ_HANDLED;
}
if (usr1 & USR1_RTSD) {
- imx_uart_rtsint(irq, dev_id);
+ __imx_uart_rtsint(irq, dev_id);
ret = IRQ_HANDLED;
}
@@ -929,6 +940,8 @@ static irqreturn_t imx_uart_int(int irq, void *dev_id)
ret = IRQ_HANDLED;
}
+ spin_unlock(&sport->port.lock);
+
return ret;
}
@@ -969,10 +982,22 @@ static void imx_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
if (!(port->rs485.flags & SER_RS485_ENABLED)) {
u32 ucr2;
+ /*
+ * Turn off autoRTS if RTS is lowered and restore autoRTS
+ * setting if RTS is raised.
+ */
ucr2 = imx_uart_readl(sport, UCR2);
ucr2 &= ~(UCR2_CTS | UCR2_CTSC);
- if (mctrl & TIOCM_RTS)
- ucr2 |= UCR2_CTS | UCR2_CTSC;
+ if (mctrl & TIOCM_RTS) {
+ ucr2 |= UCR2_CTS;
+ /*
+ * UCR2_IRTS is unset if and only if the port is
+ * configured for CRTSCTS, so we use inverted UCR2_IRTS
+ * to get the state to restore to.
+ */
+ if (!(ucr2 & UCR2_IRTS))
+ ucr2 |= UCR2_CTSC;
+ }
imx_uart_writel(sport, ucr2, UCR2);
}
@@ -1028,8 +1053,6 @@ static void imx_uart_timeout(struct timer_list *t)
}
}
-#define RX_BUF_SIZE (PAGE_SIZE)
-
/*
* There are two kinds of RX DMA interrupts(such as in the MX6Q):
* [1] the RX DMA buffer is full.
@@ -1112,7 +1135,8 @@ static void imx_uart_dma_rx_callback(void *data)
}
/* RX DMA buffer periods */
-#define RX_DMA_PERIODS 4
+#define RX_DMA_PERIODS 16
+#define RX_BUF_SIZE (RX_DMA_PERIODS * PAGE_SIZE / 4)
static int imx_uart_start_rx_dma(struct imx_port *sport)
{
@@ -1468,7 +1492,7 @@ static void imx_uart_shutdown(struct uart_port *port)
spin_lock_irqsave(&sport->port.lock, flags);
ucr1 = imx_uart_readl(sport, UCR1);
- ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | UCR1_RXDMAEN | UCR1_ATDMAEN);
imx_uart_writel(sport, ucr1, UCR1);
spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -1535,11 +1559,11 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- u32 ucr2, old_ucr1, old_ucr2, ufcr;
+ u32 ucr2, old_ucr2, ufcr;
unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
unsigned long div;
- unsigned long num, denom;
+ unsigned long num, denom, old_ubir, old_ubmr;
uint64_t tdiv64;
/*
@@ -1587,8 +1611,14 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
else
imx_uart_rts_inactive(sport, &ucr2);
- } else if (termios->c_cflag & CRTSCTS)
- imx_uart_rts_auto(sport, &ucr2);
+ } else if (termios->c_cflag & CRTSCTS) {
+ /*
+ * Only let receiver control RTS output if we were not requested
+ * to have RTS inactive (which then should take precedence).
+ */
+ if (ucr2 & UCR2_CTS)
+ ucr2 |= UCR2_CTSC;
+ }
if (termios->c_cflag & CRTSCTS)
ucr2 &= ~UCR2_IRTS;
@@ -1631,21 +1661,6 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
*/
uart_update_timeout(port, termios->c_cflag, baud);
- /*
- * disable interrupts and drain transmitter
- */
- old_ucr1 = imx_uart_readl(sport, UCR1);
- imx_uart_writel(sport,
- old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
- UCR1);
- imx_uart_writel(sport, old_ucr2 & ~UCR2_ATEN, UCR2);
-
- while (!(imx_uart_readl(sport, USR2) & USR2_TXDC))
- barrier();
-
- /* then, disable everything */
- imx_uart_writel(sport, old_ucr2 & ~(UCR2_TXEN | UCR2_RXEN | UCR2_ATEN), UCR2);
-
/* custom-baudrate handling */
div = sport->port.uartclk / (baud * 16);
if (baud == 38400 && quot != div)
@@ -1673,15 +1688,26 @@ imx_uart_set_termios(struct uart_port *port, struct ktermios *termios,
ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
imx_uart_writel(sport, ufcr, UFCR);
- imx_uart_writel(sport, num, UBIR);
- imx_uart_writel(sport, denom, UBMR);
+ /*
+ * Two registers below should always be written both and in this
+ * particular order. One consequence is that we need to check if any of
+ * them changes and then update both. We do need the check for change
+ * as even writing the same values seem to "restart"
+ * transmission/receiving logic in the hardware, that leads to data
+ * breakage even when rate doesn't in fact change. E.g., user switches
+ * RTS/CTS handshake and suddenly gets broken bytes.
+ */
+ old_ubir = imx_uart_readl(sport, UBIR);
+ old_ubmr = imx_uart_readl(sport, UBMR);
+ if (old_ubir != num || old_ubmr != denom) {
+ imx_uart_writel(sport, num, UBIR);
+ imx_uart_writel(sport, denom, UBMR);
+ }
if (!imx_uart_is_imx1(sport))
imx_uart_writel(sport, sport->port.uartclk / div / 1000,
IMX21_ONEMS);
- imx_uart_writel(sport, old_ucr1, UCR1);
-
imx_uart_writel(sport, ucr2, UCR2);
if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
@@ -1770,7 +1796,7 @@ static int imx_uart_poll_init(struct uart_port *port)
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
- ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN | UCR1_RRDYEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RTSDEN | UCR1_RRDYEN);
ucr2 |= UCR2_RXEN;
ucr2 &= ~UCR2_ATEN;
@@ -1930,7 +1956,7 @@ imx_uart_console_write(struct console *co, const char *s, unsigned int count)
if (imx_uart_is_imx1(sport))
ucr1 |= IMX1_UCR1_UARTCLKEN;
ucr1 |= UCR1_UARTEN;
- ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
+ ucr1 &= ~(UCR1_TRDYEN | UCR1_RRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
@@ -2214,8 +2240,8 @@ static int imx_uart_probe(struct platform_device *pdev)
return PTR_ERR(base);
rxirq = platform_get_irq(pdev, 0);
- txirq = platform_get_irq(pdev, 1);
- rtsirq = platform_get_irq(pdev, 2);
+ txirq = platform_get_irq_optional(pdev, 1);
+ rtsirq = platform_get_irq_optional(pdev, 2);
sport->port.dev = &pdev->dev;
sport->port.mapbase = res->start;
@@ -2224,6 +2250,7 @@ static int imx_uart_probe(struct platform_device *pdev)
sport->port.iotype = UPIO_MEM;
sport->port.irq = rxirq;
sport->port.fifosize = 32;
+ sport->port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_IMX_CONSOLE);
sport->port.ops = &imx_uart_pops;
sport->port.rs485_config = imx_uart_rs485_config;
sport->port.flags = UPF_BOOT_AUTOCONF;
@@ -2286,7 +2313,7 @@ static int imx_uart_probe(struct platform_device *pdev)
/* Disable interrupts before requesting them */
ucr1 = imx_uart_readl(sport, UCR1);
ucr1 &= ~(UCR1_ADEN | UCR1_TRDYEN | UCR1_IDEN | UCR1_RRDYEN |
- UCR1_TXMPTYEN | UCR1_RTSDEN);
+ UCR1_TRDYEN | UCR1_RTSDEN);
imx_uart_writel(sport, ucr1, UCR1);
if (!imx_uart_is_imx1(sport) && sport->dte_mode) {
OpenPOWER on IntegriCloud