diff mbox

[3.11.y.z,extended,stable] Patch "serial: 8250_dw: Report CTS asserted for auto flow" has been added to staging queue

Message ID 1404204654-7871-1-git-send-email-luis.henriques@canonical.com
State New
Headers show

Commit Message

Luis Henriques July 1, 2014, 8:50 a.m. UTC
This is a note to let you know that I have just added a patch titled

    serial: 8250_dw: Report CTS asserted for auto flow

to the linux-3.11.y-queue branch of the 3.11.y.z extended stable tree 
which can be found at:

 http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.11.y-queue

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.11.y.z tree, see
https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable

Thanks.
-Luis

------

From 091ef2c06d541a6d6150d4af0458cf0fb25a4c82 Mon Sep 17 00:00:00 2001
From: Tim Kryger <tim.kryger@linaro.org>
Date: Fri, 16 Aug 2013 13:50:15 -0700
Subject: serial: 8250_dw: Report CTS asserted for auto flow

commit 33acbb82695f84e9429c1f7fbdeb4588dea12ffa upstream.

When a serial port is configured for RTS/CTS flow control, serial core
will disable the transmitter if it observes CTS is de-asserted. This is
perfectly reasonable and appropriate when the UART lacks the ability to
automatically perform CTS flow control.

However, if the UART hardware can manage flow control automatically, it
is important that software not get involved.  When the DesignWare UART
enables 16C750 style auto-RTS/CTS it stops generating interrupts for
changes in CTS state so software mostly stays out of the way.  However,
it does report the true state of CTS in the MSR so software may notice
it is de-asserted and respond by improperly disabling the transmitter.
Once this happens the transmitter will be blocked forever.

To avoid this situation, we simply lie to the 8250 and serial core by
reporting that CTS is asserted whenever auto-RTS/CTS mode is enabled.

Signed-off-by: Tim Kryger <tim.kryger@linaro.org>
Reviewed-by: Matt Porter <matt.porter@linaro.org>
Reviewed-by: Markus Mayer <markus.mayer@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Wang Nan <wangnan0@huawei.com>
Signed-off-by: Luis Henriques <luis.henriques@canonical.com>
---
 drivers/tty/serial/8250/8250_dw.c | 34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

--
1.9.1
diff mbox

Patch

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index b8cacb850d96..8b2accbad3d1 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -57,11 +57,25 @@ 

 struct dw8250_data {
 	int		last_lcr;
+	int		last_mcr;
 	int		line;
 	struct clk	*clk;
 	u8		usr_reg;
 };

+static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value)
+{
+	struct dw8250_data *d = p->private_data;
+
+	/* If reading MSR, report CTS asserted when auto-CTS/RTS enabled */
+	if (offset == UART_MSR && d->last_mcr & UART_MCR_AFE) {
+		value |= UART_MSR_CTS;
+		value &= ~UART_MSR_DCTS;
+	}
+
+	return value;
+}
+
 static void dw8250_serial_out(struct uart_port *p, int offset, int value)
 {
 	struct dw8250_data *d = p->private_data;
@@ -69,15 +83,17 @@  static void dw8250_serial_out(struct uart_port *p, int offset, int value)
 	if (offset == UART_LCR)
 		d->last_lcr = value;

-	offset <<= p->regshift;
-	writeb(value, p->membase + offset);
+	if (offset == UART_MCR)
+		d->last_mcr = value;
+
+	writeb(value, p->membase + (offset << p->regshift));
 }

 static unsigned int dw8250_serial_in(struct uart_port *p, int offset)
 {
-	offset <<= p->regshift;
+	unsigned int value = readb(p->membase + (offset << p->regshift));

-	return readb(p->membase + offset);
+	return dw8250_modify_msr(p, offset, value);
 }

 /* Read Back (rb) version to ensure register access ording. */
@@ -94,15 +110,17 @@  static void dw8250_serial_out32(struct uart_port *p, int offset, int value)
 	if (offset == UART_LCR)
 		d->last_lcr = value;

-	offset <<= p->regshift;
-	writel(value, p->membase + offset);
+	if (offset == UART_MCR)
+		d->last_mcr = value;
+
+	writel(value, p->membase + (offset << p->regshift));
 }

 static unsigned int dw8250_serial_in32(struct uart_port *p, int offset)
 {
-	offset <<= p->regshift;
+	unsigned int value = readl(p->membase + (offset << p->regshift));

-	return readl(p->membase + offset);
+	return dw8250_modify_msr(p, offset, value);
 }

 static int dw8250_handle_irq(struct uart_port *p)