Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
@ 2014-09-05 19:02 Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 01/18] tty: serial: 8250_core: provide a function to export uart_8250_port Sebastian Andrzej Siewior
                   ` (19 more replies)
  0 siblings, 20 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

This is my complete queue fo the omap serial driver based on the 8250 core
code. I played with it on beagle bone, am335x-evm and dra7xx including DMA.
The runtime-pm pieces look now bug-compatible with the omap-serial driver.
Besides the runtime-om improvement I also fixed a few corner cases for the
TX-DMA problem. The DMA fixes (in edma and omap-dma) were dropped and the
problem has been in 8250-dma via patch #13.

The whole queue is available at
  git://git.breakpoint.cc/bigeasy/linux.git uart_v8

Sebastian

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 01/18] tty: serial: 8250_core: provide a function to export uart_8250_port
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 02/18] tty: serial: 8250_core: allow to overwrite & export serial8250_startup() Sebastian Andrzej Siewior
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

There is no way to access a struct uart_8250_port for a specific
line. This is only required outside of the 8250/uart callbacks like for
devices' suspend & remove callbacks. For those the 8250-core provides a
wrapper like serial8250_unregister_port() which passes the struct
to the proper function based on the line argument.

For run time suspend I need access to this struct not only to make
serial_out() work but also to properly restore up->ier and up->mcr.

Acked-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250.h      |  2 ++
 drivers/tty/serial/8250/8250_core.c | 18 ++++++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 1b08c918cd51..85bfec58d77c 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -112,6 +112,8 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)
 	up->dl_write(up, value);
 }
 
+struct uart_8250_port *serial8250_get_port(int line);
+
 #if defined(__alpha__) && !defined(CONFIG_PCI)
 /*
  * Digital did something really horribly wrong with the OUT1 and OUT2
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 1d42dba6121d..e2ad13a9aea4 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -2880,6 +2880,24 @@ static struct uart_ops serial8250_pops = {
 
 static struct uart_8250_port serial8250_ports[UART_NR];
 
+/**
+ * serial8250_get_port - retrieve struct uart_8250_port
+ * @line: serial line number
+ *
+ * This function retrieves struct uart_8250_port for the specific line.
+ * This struct *must* *not* be used to perform a 8250 or serial core operation
+ * which is not accessible otherwise. Its only purpose is to make the struct
+ * accessible to the runtime-pm callbacks for context suspend/restore.
+ * The lock assumption made here is none because runtime-pm suspend/resume
+ * callbacks should not be invoked if there is any operation performed on the
+ * port.
+ */
+struct uart_8250_port *serial8250_get_port(int line)
+{
+	return &serial8250_ports[line];
+}
+EXPORT_SYMBOL_GPL(serial8250_get_port);
+
 static void (*serial8250_isa_config)(int port, struct uart_port *up,
 	unsigned short *capabilities);
 
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 02/18] tty: serial: 8250_core: allow to overwrite & export serial8250_startup()
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 01/18] tty: serial: 8250_core: provide a function to export uart_8250_port Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 03/18] tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks Sebastian Andrzej Siewior
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

The OMAP version of the 8250 can actually use 1:1 serial8250_startup().
However it needs to be extended by a wake up irq which should to be
requested & enabled at ->startup() time and disabled at ->shutdown() time.

v2?v3: properly copy callbacks
v1?v2: add shutdown callback

Acked-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_core.c | 25 +++++++++++++++++++++++--
 include/linux/serial_8250.h         |  2 ++
 include/linux/serial_core.h         |  2 ++
 3 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index e2ad13a9aea4..54fd42f0e6f0 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1929,7 +1929,7 @@ static void serial8250_put_poll_char(struct uart_port *port,
 
 #endif /* CONFIG_CONSOLE_POLL */
 
-static int serial8250_startup(struct uart_port *port)
+int serial8250_do_startup(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 	unsigned long flags;
@@ -2180,8 +2180,16 @@ static int serial8250_startup(struct uart_port *port)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(serial8250_do_startup);
 
-static void serial8250_shutdown(struct uart_port *port)
+static int serial8250_startup(struct uart_port *port)
+{
+	if (port->startup)
+		return port->startup(port);
+	return serial8250_do_startup(port);
+}
+
+void serial8250_do_shutdown(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 	unsigned long flags;
@@ -2231,6 +2239,15 @@ static void serial8250_shutdown(struct uart_port *port)
 	if (port->irq)
 		serial_unlink_irq_chain(up);
 }
+EXPORT_SYMBOL_GPL(serial8250_do_shutdown);
+
+static void serial8250_shutdown(struct uart_port *port)
+{
+	if (port->shutdown)
+		port->shutdown(port);
+	else
+		serial8250_do_shutdown(port);
+}
 
 static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
 {
@@ -3428,6 +3445,10 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 		/*  Possibly override set_termios call */
 		if (up->port.set_termios)
 			uart->port.set_termios = up->port.set_termios;
+		if (up->port.startup)
+			uart->port.startup = up->port.startup;
+		if (up->port.shutdown)
+			uart->port.shutdown = up->port.shutdown;
 		if (up->port.pm)
 			uart->port.pm = up->port.pm;
 		if (up->port.handle_break)
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index f93649e22c43..c3aa0040c72e 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -121,6 +121,8 @@ extern void serial8250_early_out(struct uart_port *port, int offset, int value);
 extern int setup_early_serial8250_console(char *cmdline);
 extern void serial8250_do_set_termios(struct uart_port *port,
 		struct ktermios *termios, struct ktermios *old);
+extern int serial8250_do_startup(struct uart_port *port);
+extern void serial8250_do_shutdown(struct uart_port *port);
 extern void serial8250_do_pm(struct uart_port *port, unsigned int state,
 			     unsigned int oldstate);
 extern int fsl8250_handle_irq(struct uart_port *port);
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index cf3a1e789bf5..f3ea5312d89f 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -122,6 +122,8 @@ struct uart_port {
 	void			(*set_termios)(struct uart_port *,
 				               struct ktermios *new,
 				               struct ktermios *old);
+	int			(*startup)(struct uart_port *port);
+	void			(*shutdown)(struct uart_port *port);
 	int			(*handle_irq)(struct uart_port *);
 	void			(*pm)(struct uart_port *, unsigned int state,
 				      unsigned int old);
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 03/18] tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 01/18] tty: serial: 8250_core: provide a function to export uart_8250_port Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 02/18] tty: serial: 8250_core: allow to overwrite & export serial8250_startup() Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-08 23:33   ` Greg Kroah-Hartman
  2014-09-05 19:02 ` [PATCH 04/18] tty: serial: 8250_core: add run time pm Sebastian Andrzej Siewior
                   ` (16 subsequent siblings)
  19 siblings, 1 reply; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

The OMAP UART provides support for HW assisted flow control. What is
missing is the support to throttle / unthrottle callbacks which are used
by the omap-serial driver at the moment.
This patch adds the callbacks. It should be safe to add them since they
are only invoked from the serial_core (uart_throttle()) if the feature
flags are set.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_core.c | 14 ++++++++++++++
 include/linux/serial_core.h         |  2 ++
 2 files changed, 16 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 54fd42f0e6f0..e2703e8d4b87 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1318,6 +1318,16 @@ static void serial8250_start_tx(struct uart_port *port)
 	}
 }
 
+static void serial8250_throttle(struct uart_port *port)
+{
+	port->throttle(port);
+}
+
+static void serial8250_unthrottle(struct uart_port *port)
+{
+	port->unthrottle(port);
+}
+
 static void serial8250_stop_rx(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
@@ -2876,6 +2886,8 @@ static struct uart_ops serial8250_pops = {
 	.get_mctrl	= serial8250_get_mctrl,
 	.stop_tx	= serial8250_stop_tx,
 	.start_tx	= serial8250_start_tx,
+	.throttle	= serial8250_throttle,
+	.unthrottle	= serial8250_unthrottle,
 	.stop_rx	= serial8250_stop_rx,
 	.enable_ms	= serial8250_enable_ms,
 	.break_ctl	= serial8250_break_ctl,
@@ -3423,6 +3435,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
 		uart->port.fifosize	= up->port.fifosize;
 		uart->tx_loadsz		= up->tx_loadsz;
 		uart->capabilities	= up->capabilities;
+		uart->port.throttle	= up->port.throttle;
+		uart->port.unthrottle	= up->port.unthrottle;
 
 		/* Take tx_loadsz from fifosize if it wasn't set separately */
 		if (uart->port.fifosize && !uart->tx_loadsz)
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index f3ea5312d89f..edaaaa036c0b 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -124,6 +124,8 @@ struct uart_port {
 				               struct ktermios *old);
 	int			(*startup)(struct uart_port *port);
 	void			(*shutdown)(struct uart_port *port);
+	void			(*throttle)(struct uart_port *port);
+	void			(*unthrottle)(struct uart_port *port);
 	int			(*handle_irq)(struct uart_port *);
 	void			(*pm)(struct uart_port *, unsigned int state,
 				      unsigned int old);
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 04/18] tty: serial: 8250_core: add run time pm
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (2 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 03/18] tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 05/18] tty: serial: 8250_core: read only RX if there is something in the FIFO Sebastian Andrzej Siewior
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

While comparing the OMAP-serial and the 8250 part of this I noticed that
the latter does not use run time-pm. Here are the pieces. It is
basically a get before first register access and a last_busy + put after
last access. This has to be enabled from userland _and_ UART_CAP_RPM is
required for this.
The runtime PM can usually work transparently in the background however
there is one exception to this: After serial8250_tx_chars() completes
there still may be unsent bytes in the FIFO (depending on CPU speed vs
baud rate + flow control). Even if the TTY-buffer is empty we do not
want RPM to disable the device because it won't send the remaining
bytes. Instead we leave serial8250_tx_chars() with RPM enabled and wait
for the FIFO empty interrupt. Once we enter serial8250_tx_chars() with
an empty buffer we know that the FIFO is empty and since we are not going
to send anything, we can disable the device.
That xchg() is to ensure that serial8250_tx_chars() can be called
multiple times and only the first invocation will actually invoke the
runtime PM function. So that the last invocation of __stop_tx() will
disable runtime pm.

NOTE: do not enable RPM on the device unless you know what you do! If
the device goes idle, it won't be woken up by incomming RX data _unless_
there is a wakeup irq configured which is usually the RX pin configure
for wakeup via the reset module. The RX activity will then wake up the
device from idle. However the first character is garbage and lost. The
following bytes will be received once the device is up in time. On the
beagle board xm (omap3) it takes approx 13ms from the first wakeup byte
until the first byte that is received properly if the device was in
core-off.

v5?v8:
	- drop RPM from serial8250_set_mctrl() it will be used in
	  restore path which already has RPM active and holds
	  dev->power.lock
v4?v5:
	- add a wrapper around rpm function and introduce UART_CAP_RPM
	  to ensure RPM put is invoked after the TX FIFO is empty.
v3?v4:
	- added runtime to the console code
	- removed device_may_wakeup() from serial8250_set_sleep()

Cc: mika.westerberg at linux.intel.com
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250.h      |   1 +
 drivers/tty/serial/8250/8250_core.c | 133 ++++++++++++++++++++++++++++++++----
 include/linux/serial_8250.h         |   1 +
 3 files changed, 122 insertions(+), 13 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 85bfec58d77c..1bcb4b2141a6 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -72,6 +72,7 @@ struct serial8250_config {
 #define UART_CAP_UUE	(1 << 12)	/* UART needs IER bit 6 set (Xscale) */
 #define UART_CAP_RTOIE	(1 << 13)	/* UART needs IER bit 4 set (Xscale, Tegra) */
 #define UART_CAP_HFIFO	(1 << 14)	/* UART has a "hidden" FIFO */
+#define UART_CAP_RPM	(1 << 15)	/* Runtime PM is active while idle */
 
 #define UART_BUG_QUOT	(1 << 0)	/* UART has buggy quot LSB */
 #define UART_BUG_TXEN	(1 << 1)	/* UART has buggy TX IIR status */
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index e2703e8d4b87..f9dd20ebab52 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -37,6 +37,7 @@
 #include <linux/nmi.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 #ifdef CONFIG_SPARC
 #include <linux/sunserialcore.h>
 #endif
@@ -539,6 +540,53 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p)
 }
 EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos);
 
+static void serial8250_rpm_get(struct uart_8250_port *p)
+{
+	if (!(p->capabilities & UART_CAP_RPM))
+		return;
+	pm_runtime_get_sync(p->port.dev);
+}
+
+static void serial8250_rpm_put(struct uart_8250_port *p)
+{
+	if (!(p->capabilities & UART_CAP_RPM))
+		return;
+	pm_runtime_mark_last_busy(p->port.dev);
+	pm_runtime_put_autosuspend(p->port.dev);
+}
+
+/*
+ * This two wrapper ensure, that enable_runtime_pm_tx() can be called more than
+ * once and disable_runtime_pm_tx() will still disable RPM because the fifo is
+ * empty and the HW can idle again.
+ */
+static void serial8250_rpm_get_tx(struct uart_8250_port *p)
+{
+	unsigned char rpm_active;
+
+	if (!(p->capabilities & UART_CAP_RPM))
+		return;
+
+	rpm_active = xchg(&p->rpm_tx_active, 1);
+	if (rpm_active)
+		return;
+	pm_runtime_get_sync(p->port.dev);
+}
+
+static void serial8250_rpm_put_tx(struct uart_8250_port *p)
+{
+	unsigned char rpm_active;
+
+	if (!(p->capabilities & UART_CAP_RPM))
+		return;
+
+	rpm_active = xchg(&p->rpm_tx_active, 0);
+	if (!rpm_active)
+		return;
+	pm_runtime_mark_last_busy(p->port.dev);
+	pm_runtime_put_autosuspend(p->port.dev);
+}
+
 /*
  * IER sleep support.  UARTs which have EFRs need the "extended
  * capability" bit enabled.  Note that on XR16C850s, we need to
@@ -553,10 +601,11 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
 	 * offset but the UART channel may only write to the corresponding
 	 * bit.
 	 */
+	serial8250_rpm_get(p);
 	if ((p->port.type == PORT_XR17V35X) ||
 	   (p->port.type == PORT_XR17D15X)) {
 		serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0);
-		return;
+		goto out;
 	}
 
 	if (p->capabilities & UART_CAP_SLEEP) {
@@ -572,6 +621,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
 			serial_out(p, UART_LCR, 0);
 		}
 	}
+out:
+	serial8250_rpm_put(p);
 }
 
 #ifdef CONFIG_SERIAL_8250_RSA
@@ -1272,6 +1323,7 @@ static inline void __stop_tx(struct uart_8250_port *p)
 	if (p->ier & UART_IER_THRI) {
 		p->ier &= ~UART_IER_THRI;
 		serial_out(p, UART_IER, p->ier);
+		serial8250_rpm_put_tx(p);
 	}
 }
 
@@ -1279,6 +1331,7 @@ static void serial8250_stop_tx(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
+	serial8250_rpm_get(up);
 	__stop_tx(up);
 
 	/*
@@ -1288,12 +1341,14 @@ static void serial8250_stop_tx(struct uart_port *port)
 		up->acr |= UART_ACR_TXDIS;
 		serial_icr_write(up, UART_ACR, up->acr);
 	}
+	serial8250_rpm_put(up);
 }
 
 static void serial8250_start_tx(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
+	serial8250_rpm_get_tx(up);
 	if (up->dma && !serial8250_tx_dma(up)) {
 		return;
 	} else if (!(up->ier & UART_IER_THRI)) {
@@ -1332,9 +1387,13 @@ static void serial8250_stop_rx(struct uart_port *port)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
+	serial8250_rpm_get(up);
+
 	up->ier &= ~UART_IER_RLSI;
 	up->port.read_status_mask &= ~UART_LSR_DR;
 	serial_port_out(port, UART_IER, up->ier);
+
+	serial8250_rpm_put(up);
 }
 
 static void serial8250_enable_ms(struct uart_port *port)
@@ -1346,7 +1405,10 @@ static void serial8250_enable_ms(struct uart_port *port)
 		return;
 
 	up->ier |= UART_IER_MSI;
+
+	serial8250_rpm_get(up);
 	serial_port_out(port, UART_IER, up->ier);
+	serial8250_rpm_put(up);
 }
 
 /*
@@ -1468,7 +1530,12 @@ void serial8250_tx_chars(struct uart_8250_port *up)
 
 	DEBUG_INTR("THRE...");
 
-	if (uart_circ_empty(xmit))
+	/*
+	 * With RPM enabled, we have to wait once the FIFO is empty before the
+	 * HW can go idle. So we get here once again with empty FIFO and disable
+	 * the interrupt and RPM in __stop_tx()
+	 */
+	if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM))
 		__stop_tx(up);
 }
 EXPORT_SYMBOL_GPL(serial8250_tx_chars);
@@ -1535,9 +1602,17 @@ EXPORT_SYMBOL_GPL(serial8250_handle_irq);
 
 static int serial8250_default_handle_irq(struct uart_port *port)
 {
-	unsigned int iir = serial_port_in(port, UART_IIR);
+	struct uart_8250_port *up = up_to_u8250p(port);
+	unsigned int iir;
+	int ret;
+
+	serial8250_rpm_get(up);
 
-	return serial8250_handle_irq(port, iir);
+	iir = serial_port_in(port, UART_IIR);
+	ret = serial8250_handle_irq(port, iir);
+
+	serial8250_rpm_put(up);
+	return ret;
 }
 
 /*
@@ -1794,11 +1869,15 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
 	unsigned long flags;
 	unsigned int lsr;
 
+	serial8250_rpm_get(up);
+
 	spin_lock_irqsave(&port->lock, flags);
 	lsr = serial_port_in(port, UART_LSR);
 	up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
 	spin_unlock_irqrestore(&port->lock, flags);
 
+	serial8250_rpm_put(up);
+
 	return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
 }
 
@@ -1808,7 +1887,9 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
 	unsigned int status;
 	unsigned int ret;
 
+	serial8250_rpm_get(up);
 	status = serial8250_modem_status(up);
+	serial8250_rpm_put(up);
 
 	ret = 0;
 	if (status & UART_MSR_DCD)
@@ -1848,6 +1929,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
 	struct uart_8250_port *up = up_to_u8250p(port);
 	unsigned long flags;
 
+	serial8250_rpm_get(up);
 	spin_lock_irqsave(&port->lock, flags);
 	if (break_state == -1)
 		up->lcr |= UART_LCR_SBC;
@@ -1855,6 +1937,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
 		up->lcr &= ~UART_LCR_SBC;
 	serial_port_out(port, UART_LCR, up->lcr);
 	spin_unlock_irqrestore(&port->lock, flags);
+	serial8250_rpm_put(up);
 }
 
 /*
@@ -1899,12 +1982,23 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
 
 static int serial8250_get_poll_char(struct uart_port *port)
 {
-	unsigned char lsr = serial_port_in(port, UART_LSR);
+	struct uart_8250_port *up = up_to_u8250p(port);
+	unsigned char lsr;
+	int status;
+
+	serial8250_rpm_get(up);
 
-	if (!(lsr & UART_LSR_DR))
-		return NO_POLL_CHAR;
+	lsr = serial_port_in(port, UART_LSR);
 
-	return serial_port_in(port, UART_RX);
+	if (!(lsr & UART_LSR_DR)) {
+		status = NO_POLL_CHAR;
+		goto out;
+	}
+
+	status = serial_port_in(port, UART_RX);
+out:
+	serial8250_rpm_put(up);
+	return status;
 }
 
 
@@ -1914,6 +2008,7 @@ static void serial8250_put_poll_char(struct uart_port *port,
 	unsigned int ier;
 	struct uart_8250_port *up = up_to_u8250p(port);
 
+	serial8250_rpm_get(up);
 	/*
 	 *	First save the IER then disable the interrupts
 	 */
@@ -1935,6 +2030,7 @@ static void serial8250_put_poll_char(struct uart_port *port,
 	 */
 	wait_for_xmitr(up, BOTH_EMPTY);
 	serial_port_out(port, UART_IER, ier);
+	serial8250_rpm_put(up);
 }
 
 #endif /* CONFIG_CONSOLE_POLL */
@@ -1960,6 +2056,7 @@ int serial8250_do_startup(struct uart_port *port)
 	if (port->iotype != up->cur_iotype)
 		set_io_from_upio(port);
 
+	serial8250_rpm_get(up);
 	if (port->type == PORT_16C950) {
 		/* Wake up and initialize UART */
 		up->acr = 0;
@@ -1980,7 +2077,6 @@ int serial8250_do_startup(struct uart_port *port)
 	 */
 	enable_rsa(up);
 #endif
-
 	/*
 	 * Clear the FIFO buffers and disable them.
 	 * (they will be reenabled in set_termios())
@@ -2004,7 +2100,8 @@ int serial8250_do_startup(struct uart_port *port)
 	    (serial_port_in(port, UART_LSR) == 0xff)) {
 		printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
 				   serial_index(port));
-		return -ENODEV;
+		retval = -ENODEV;
+		goto out;
 	}
 
 	/*
@@ -2089,7 +2186,7 @@ int serial8250_do_startup(struct uart_port *port)
 	} else {
 		retval = serial_link_irq_chain(up);
 		if (retval)
-			return retval;
+			goto out;
 	}
 
 	/*
@@ -2187,8 +2284,10 @@ int serial8250_do_startup(struct uart_port *port)
 		outb_p(0x80, icp);
 		inb_p(icp);
 	}
-
-	return 0;
+	retval = 0;
+out:
+	serial8250_rpm_put(up);
+	return retval;
 }
 EXPORT_SYMBOL_GPL(serial8250_do_startup);
 
@@ -2204,6 +2303,7 @@ void serial8250_do_shutdown(struct uart_port *port)
 	struct uart_8250_port *up = up_to_u8250p(port);
 	unsigned long flags;
 
+	serial8250_rpm_get(up);
 	/*
 	 * Disable interrupts from this port
 	 */
@@ -2243,6 +2343,7 @@ void serial8250_do_shutdown(struct uart_port *port)
 	 * the IRQ chain.
 	 */
 	serial_port_in(port, UART_RX);
+	serial8250_rpm_put(up);
 
 	del_timer_sync(&up->timer);
 	up->timer.function = serial8250_timeout;
@@ -2360,6 +2461,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 	 * Ok, we're now changing the port state.  Do it with
 	 * interrupts disabled.
 	 */
+	serial8250_rpm_get(up);
 	spin_lock_irqsave(&port->lock, flags);
 
 	/*
@@ -2481,6 +2583,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
 	}
 	serial8250_set_mctrl(port, port->mctrl);
 	spin_unlock_irqrestore(&port->lock, flags);
+	serial8250_rpm_put(up);
+
 	/* Don't rewrite B0 */
 	if (tty_termios_baud_rate(termios))
 		tty_termios_encode_baud_rate(termios, baud, baud);
@@ -3054,6 +3158,8 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
 
 	touch_nmi_watchdog();
 
+	serial8250_rpm_get(up);
+
 	if (port->sysrq || oops_in_progress)
 		locked = spin_trylock_irqsave(&port->lock, flags);
 	else
@@ -3090,6 +3196,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count)
 
 	if (locked)
 		spin_unlock_irqrestore(&port->lock, flags);
+	serial8250_rpm_put(up);
 }
 
 static int __init serial8250_console_setup(struct console *co, char *options)
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index c3aa0040c72e..b161eee46108 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -84,6 +84,7 @@ struct uart_8250_port {
 	unsigned char		mcr_mask;	/* mask of user bits */
 	unsigned char		mcr_force;	/* mask of forced bits */
 	unsigned char		cur_iotype;	/* Running I/O type */
+	unsigned char		rpm_tx_active;
 
 	/*
 	 * Some bits in registers are cleared on a read, so they must
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 05/18] tty: serial: 8250_core: read only RX if there is something in the FIFO
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (3 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 04/18] tty: serial: 8250_core: add run time pm Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 06/18] tty: serial: 8250_core: user the ->line argument as a hint in serial8250_find_match_or_unused() Sebastian Andrzej Siewior
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

The serial8250_do_startup() function unconditionally clears the
interrupts and for that it reads from the RX-FIFO without checking if
there is a byte in the FIFO or not. This works fine on OMAP4+ HW like
AM335x or DRA7.
OMAP3630 ES1.1 (which means probably all OMAP3 and earlier) does not like
this:

|Unhandled fault: external abort on non-linefetch (0x1028) at 0xfb020000
|Internal error: : 1028 [#1] ARM
|Modules linked in:
|CPU: 0 PID: 1 Comm: swapper Not tainted 3.16.0-00022-g7edcb57-dirty #1213
|task: de0572c0 ti: de058000 task.ti: de058000
|PC is at mem32_serial_in+0xc/0x1c
|LR is at serial8250_do_startup+0x220/0x85c
|Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
|Control: 10c5387d  Table: 80004019  DAC: 00000015
|[<c03051d4>] (mem32_serial_in) from [<c0307fe8>] (serial8250_do_startup+0x220/0x85c)
|[<c0307fe8>] (serial8250_do_startup) from [<c0309e00>] (omap_8250_startup+0x5c/0xe0)
|[<c0309e00>] (omap_8250_startup) from [<c030863c>] (serial8250_startup+0x18/0x2c)
|[<c030863c>] (serial8250_startup) from [<c030394c>] (uart_startup+0x78/0x1d8)
|[<c030394c>] (uart_startup) from [<c0304678>] (uart_open+0xe8/0x114)
|[<c0304678>] (uart_open) from [<c02e9e10>] (tty_open+0x1a8/0x5a4)

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_core.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index f9dd20ebab52..eaab8974a456 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -2086,8 +2086,8 @@ int serial8250_do_startup(struct uart_port *port)
 	/*
 	 * Clear the interrupt registers.
 	 */
-	serial_port_in(port, UART_LSR);
-	serial_port_in(port, UART_RX);
+	if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
+		serial_port_in(port, UART_RX);
 	serial_port_in(port, UART_IIR);
 	serial_port_in(port, UART_MSR);
 
@@ -2248,8 +2248,8 @@ int serial8250_do_startup(struct uart_port *port)
 	 * saved flags to avoid getting false values from polling
 	 * routines or the previous session.
 	 */
-	serial_port_in(port, UART_LSR);
-	serial_port_in(port, UART_RX);
+	if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
+		serial_port_in(port, UART_RX);
 	serial_port_in(port, UART_IIR);
 	serial_port_in(port, UART_MSR);
 	up->lsr_saved_flags = 0;
@@ -2342,7 +2342,8 @@ void serial8250_do_shutdown(struct uart_port *port)
 	 * Read data port to reset things, and then unlink from
 	 * the IRQ chain.
 	 */
-	serial_port_in(port, UART_RX);
+	if (serial_port_in(port, UART_LSR) & UART_LSR_DR)
+		serial_port_in(port, UART_RX);
 	serial8250_rpm_put(up);
 
 	del_timer_sync(&up->timer);
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 06/18] tty: serial: 8250_core: user the ->line argument as a hint in serial8250_find_match_or_unused()
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (4 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 05/18] tty: serial: 8250_core: read only RX if there is something in the FIFO Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 07/18] tty: serial: 8250_core: remove UART_IER_RDI in serial8250_stop_rx() Sebastian Andrzej Siewior
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

Tony noticed that the old omap-serial driver picked the uart "number"
based on the hint given from device tree or platform device's id.
The 8250 based omap driver doesn't do this because the core code does
not honour the ->line argument which is passed by the driver.

This patch aims to keep the same behaviour as with omap-serial. The
function will first try to use the line suggested ->line argument and
then fallback to the old strategy in case the port is taken.

That means the the third uart will always be ttyS2 even if the previous
two have not been enabled in DT.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_core.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index eaab8974a456..8f5fb81d5fad 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -3479,6 +3479,11 @@ static struct uart_8250_port *serial8250_find_match_or_unused(struct uart_port *
 		if (uart_match_port(&serial8250_ports[i].port, port))
 			return &serial8250_ports[i];
 
+	/* try line number first if still available */
+	i = port->line;
+	if (i < nr_uarts && serial8250_ports[i].port.type == PORT_UNKNOWN &&
+			serial8250_ports[i].port.iobase == 0)
+		return &serial8250_ports[i];
 	/*
 	 * We didn't find a matching entry, so look for the first
 	 * free entry.  We look for one which hasn't been previously
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 07/18] tty: serial: 8250_core: remove UART_IER_RDI in serial8250_stop_rx()
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (5 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 06/18] tty: serial: 8250_core: user the ->line argument as a hint in serial8250_find_match_or_unused() Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 08/18] tty: serial: Add 8250-core based omap driver Sebastian Andrzej Siewior
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

serial8250_do_startup() adds UART_IER_RDI and UART_IER_RLSI to ier.
serial8250_stop_rx() should remove both.
This is what the serial-omap driver has been doing and is now moved to
the 8250-core since it does no look to be *that* omap specific.

Cc: heikki.krogerus at linux.intel.com
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 8f5fb81d5fad..e8df7cd85ba7 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1389,7 +1389,7 @@ static void serial8250_stop_rx(struct uart_port *port)
 
 	serial8250_rpm_get(up);
 
-	up->ier &= ~UART_IER_RLSI;
+	up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
 	up->port.read_status_mask &= ~UART_LSR_DR;
 	serial_port_out(port, UART_IER, up->ier);
 
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 08/18] tty: serial: Add 8250-core based omap driver
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (6 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 07/18] tty: serial: 8250_core: remove UART_IER_RDI in serial8250_stop_rx() Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-08 16:39   ` Tony Lindgren
  2014-09-05 19:02 ` [PATCH 09/18] tty: serial: 8250_dma: handle error on TX submit Sebastian Andrzej Siewior
                   ` (11 subsequent siblings)
  19 siblings, 1 reply; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

This patch provides a 8250-core based UART driver for the internal OMAP
UART. The long term goal is to provide the same functionality as the
current OMAP uart driver and DMA support.
I tried to merge omap-serial code together with the 8250-core code.
There should should be hardly a noticable difference. The trigger levels
are different compared to omap-serial:
- omap serial
  TX: Interrupt comes after TX FIFO has room for 16 bytes.
      TX of 4096 bytes in one go results in 256 interrupts

  RX: Interrupt comes after there is on byte in the FIFO.
      RX of 4096 bytes results in 4096 interrupts.

- this driver
  TX: Interrupt comes once the TX FIFO is empty.
      TX of 4096 bytes results in 65 interrupts. That means there will
      be gaps on the line while the driver reloads the FIFO.

  RX: Interrupt comes once there are 48 bytes in the FIFO or less over
      "longer" time frame. We have
          1 / 11520 * 10^3 * 16 => 1.38? ms
      1.38ms to react and purge the FIFO on 115200,8N1. Since the other
      driver fired after each byte it had ~5.47ms time to react. This
      _may_ cause problems if one relies on no missing bytes and has no
      flow control. On the other hand we get only 85 interrupts for the
      same amount of data.

It has been only tested as console UART on am335x-evm, dra7-evm and
beagle bone. I also did some longer raw-transfers to meassure the load.

The device name is ttyS based instead of ttyO. If a ttyO based node name
is required please ask udev for it. If both driver are activated (this
and omap-serial) then this serial driver will take control over the
device due to the link order

v7?v8:
	- redo the register write. There is now one function for that
	  which is used from set_termios() and runtime-resume.
	- drop PORT_OMAP_16750 and move the setup to the omap file. We
	  have our own set termios function anyway (Heikki Krogerus)
	- use MEM instead of MEM32. TRM of AM/DM37x says that 32bit
	  access on THR might result in data abort. We only need 32bit
	  access in the errata function which is before we use 8250's
	  read function so it doesn't matter.
v4?v7:
	- change trigger levels after some tests with raw transfers.
v3?v4:
	- drop RS485 support
	- wire up ->throttle / ->unthrottle
v2?v3:
	- wire up startup & shutdown for wakeup-irq handling.
	- RS485 handling (well the core does).

v1?v2:
	- added runtime PM. Could somebody could please double check
	  this?
	- added omap_8250_set_termios()

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_omap.c | 905 ++++++++++++++++++++++++++++++++++++
 drivers/tty/serial/8250/Kconfig     |   9 +
 drivers/tty/serial/8250/Makefile    |   1 +
 3 files changed, 915 insertions(+)
 create mode 100644 drivers/tty/serial/8250/8250_omap.c

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
new file mode 100644
index 000000000000..4128bbc35089
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -0,0 +1,905 @@
+/*
+ * 8250-core based driver for the OMAP internal UART
+ *
+ *  Copyright (C) 2014 Sebastian Andrzej Siewior
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/serial_8250.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/console.h>
+#include <linux/pm_qos.h>
+
+#include "8250.h"
+
+#define UART_DLL_EM 9
+#define UART_DLM_EM 10
+
+#define DEFAULT_CLK_SPEED	48000000 /* 48 Mhz*/
+
+#define UART_ERRATA_i202_MDR1_ACCESS	(1 << 0)
+#define OMAP_UART_WER_HAS_TX_WAKEUP	(1 << 1)
+
+#define OMAP_UART_FCR_RX_TRIG		6
+#define OMAP_UART_FCR_TX_TRIG		4
+
+/* SCR register bitmasks */
+#define OMAP_UART_SCR_RX_TRIG_GRANU1_MASK	(1 << 7)
+#define OMAP_UART_SCR_TX_TRIG_GRANU1_MASK	(1 << 6)
+#define OMAP_UART_SCR_TX_EMPTY			(1 << 3)
+#define OMAP_UART_SCR_DMAMODE_MASK		(3 << 1)
+#define OMAP_UART_SCR_DMAMODE_1			(1 << 1)
+#define OMAP_UART_SCR_DMAMODE_CTL		(1 << 0)
+
+/* MVR register bitmasks */
+#define OMAP_UART_MVR_SCHEME_SHIFT	30
+#define OMAP_UART_LEGACY_MVR_MAJ_MASK	0xf0
+#define OMAP_UART_LEGACY_MVR_MAJ_SHIFT	4
+#define OMAP_UART_LEGACY_MVR_MIN_MASK	0x0f
+#define OMAP_UART_MVR_MAJ_MASK		0x700
+#define OMAP_UART_MVR_MAJ_SHIFT		8
+#define OMAP_UART_MVR_MIN_MASK		0x3f
+
+#define UART_TI752_TLR_TX	0
+#define UART_TI752_TLR_RX	4
+
+#define TRIGGER_TLR_MASK(x)	((x & 0x3c) >> 2)
+#define TRIGGER_FCR_MASK(x)	(x & 3)
+
+/* Enable XON/XOFF flow control on output */
+#define OMAP_UART_SW_TX		0x08
+/* Enable XON/XOFF flow control on input */
+#define OMAP_UART_SW_RX		0x02
+
+#define OMAP_UART_WER_MOD_WKUP	0x7f
+#define OMAP_UART_TX_WAKEUP_EN	(1 << 7)
+
+#define TX_TRIGGER	1
+#define RX_TRIGGER	48
+
+#define OMAP_UART_TCR_RESTORE(x)	((x / 4) << 4)
+#define OMAP_UART_TCR_HALT(x)		((x / 4) << 0)
+
+#define UART_BUILD_REVISION(x, y)	(((x) << 8) | (y))
+
+#define OMAP_UART_REV_46 0x0406
+#define OMAP_UART_REV_52 0x0502
+#define OMAP_UART_REV_63 0x0603
+
+struct omap8250_priv {
+	int line;
+	u32 habit;
+	u32 mdr1;
+	u32 efr;
+	u32 quot;
+	u32 scr;
+	u32 wer;
+	u32 xon;
+	u32 xoff;
+
+	bool is_suspending;
+	int wakeirq;
+	int wakeups_enabled;
+	u32 latency;
+	u32 calc_latency;
+	struct pm_qos_request pm_qos_request;
+	struct work_struct qos_work;
+	struct uart_8250_dma omap8250_dma;
+	bool dma_active;
+};
+
+static u32 uart_read(struct uart_8250_port *up, u32 reg)
+{
+	return readl(up->port.membase + (reg << up->port.regshift));
+}
+
+/*
+ * Work Around for Errata i202 (2430, 3430, 3630, 4430 and 4460)
+ * The access to uart register after MDR1 Access
+ * causes UART to corrupt data.
+ *
+ * Need a delay =
+ * 5 L4 clock cycles + 5 UART functional clock cycle (@48MHz = ~0.2uS)
+ * give 10 times as much
+ */
+static void omap_8250_mdr1_errataset(struct uart_8250_port *up, u8 mdr1)
+{
+	u8 timeout = 255;
+
+	serial_out(up, UART_OMAP_MDR1, mdr1);
+	udelay(2);
+	serial_out(up, UART_FCR, up->fcr | UART_FCR_CLEAR_XMIT |
+			UART_FCR_CLEAR_RCVR);
+	/*
+	 * Wait for FIFO to empty: when empty, RX_FIFO_E bit is 0 and
+	 * TX_FIFO_E bit is 1.
+	 */
+	while (UART_LSR_THRE != (serial_in(up, UART_LSR) &
+				(UART_LSR_THRE | UART_LSR_DR))) {
+		timeout--;
+		if (!timeout) {
+			/* Should *never* happen. we warn and carry on */
+			dev_crit(up->port.dev, "Errata i202: timedout %x\n",
+						serial_in(up, UART_LSR));
+			break;
+		}
+		udelay(1);
+	}
+}
+
+static void omap_8250_get_divisor(struct uart_port *port, unsigned int baud,
+		struct omap8250_priv *priv)
+{
+	unsigned int uartclk = port->uartclk;
+	unsigned int div_13, div_16;
+	unsigned int abs_d13, abs_d16;
+
+	/*
+	 * Old custom speed handling.
+	 */
+	if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) {
+		priv->quot = port->custom_divisor & 0xffff;
+		/*
+		 * I assume that nobody is using this. But hey, if somebody
+		 * would like to specify the divisor _and_ the mode then the
+		 * driver is ready and waiting for it.
+		 */
+		if (port->custom_divisor & (1 << 16))
+			priv->mdr1 = UART_OMAP_MDR1_13X_MODE;
+		else
+			priv->mdr1 = UART_OMAP_MDR1_16X_MODE;
+		return;
+	}
+	div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud);
+	div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud);
+
+	abs_d13 = abs(baud - port->uartclk / 13 / div_13);
+	abs_d16 = abs(baud - port->uartclk / 16 / div_16);
+
+	if (abs_d13 >= abs_d16) {
+		priv->mdr1 = UART_OMAP_MDR1_16X_MODE;
+		priv->quot = div_16;
+	} else {
+		priv->mdr1 = UART_OMAP_MDR1_13X_MODE;
+		priv->quot = div_13;
+	}
+}
+
+static void omap8250_update_scr(struct uart_8250_port *up,
+		struct omap8250_priv *priv)
+{
+	/*
+	 * The manual recommends not to enable the DMA mode selector in the SCR
+	 * (instead of the FCR) register _and_ selecting the DMA mode as one
+	 * register write because this may lead to malfunction.
+	 */
+	if (priv->scr & OMAP_UART_SCR_DMAMODE_MASK)
+		serial_out(up, UART_OMAP_SCR,
+				priv->scr & ~OMAP_UART_SCR_DMAMODE_MASK);
+	serial_out(up, UART_OMAP_SCR, priv->scr);
+}
+
+static void omap8250_restore_regs(struct uart_8250_port *up)
+{
+	struct omap8250_priv *priv = up->port.private_data;
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	serial_out(up, UART_DLL, 0);
+	serial_out(up, UART_DLM, 0);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, UART_EFR_ECB);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+	serial_out(up, UART_MCR, UART_MCR_TCRTLR);
+
+	serial_out(up, UART_TI752_TCR,
+			OMAP_UART_TCR_RESTORE(16) | OMAP_UART_TCR_HALT(52));
+
+	serial_out(up, UART_TI752_TLR,
+			TRIGGER_TLR_MASK(TX_TRIGGER) << UART_TI752_TLR_TX |
+			TRIGGER_TLR_MASK(RX_TRIGGER) << UART_TI752_TLR_RX);
+	serial_out(up, UART_FCR, up->fcr);
+
+	omap8250_update_scr(up, priv);
+
+	/* Protocol, Baud Rate, and Interrupt Settings */
+	if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS)
+		omap_8250_mdr1_errataset(up, UART_OMAP_MDR1_DISABLE);
+	else
+		serial_out(up, UART_OMAP_MDR1, UART_OMAP_MDR1_DISABLE);
+
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_dl_write(up, priv->quot);
+	serial_out(up, UART_XON1, priv->xon);
+	serial_out(up, UART_XOFF1, priv->xoff);
+	serial_out(up, UART_EFR, priv->efr);
+
+	serial_out(up, UART_LCR, 0);
+	serial_out(up, UART_IER, up->ier);
+
+	serial_out(up, UART_FCR, up->fcr);
+
+	if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS)
+		omap_8250_mdr1_errataset(up, priv->mdr1);
+	else
+		serial_out(up, UART_OMAP_MDR1, priv->mdr1);
+
+	serial_out(up, UART_LCR, up->lcr);
+	up->port.ops->set_mctrl(&up->port, up->port.mctrl);
+}
+/*
+ * OMAP can use "CLK / (16 or 13) / div" for baud rate. And then we have have
+ * some differences in how we want to handle flow control.
+ */
+static void omap_8250_set_termios(struct uart_port *port,
+		struct ktermios *termios, struct ktermios *old)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	struct omap8250_priv *priv = up->port.private_data;
+	unsigned char cval = 0;
+	unsigned long flags = 0;
+	unsigned int baud;
+
+	switch (termios->c_cflag & CSIZE) {
+	case CS5:
+		cval = UART_LCR_WLEN5;
+		break;
+	case CS6:
+		cval = UART_LCR_WLEN6;
+		break;
+	case CS7:
+		cval = UART_LCR_WLEN7;
+		break;
+	default:
+	case CS8:
+		cval = UART_LCR_WLEN8;
+		break;
+	}
+
+	if (termios->c_cflag & CSTOPB)
+		cval |= UART_LCR_STOP;
+	if (termios->c_cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(termios->c_cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+	if (termios->c_cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+
+	/*
+	 * Ask the core to calculate the divisor for us.
+	 */
+	baud = uart_get_baud_rate(port, termios, old,
+			port->uartclk / 16 / 0xffff,
+			port->uartclk / 13);
+	omap_8250_get_divisor(port, baud, priv);
+
+	/*
+	 * Ok, we're now changing the port state. Do it with
+	 * interrupts disabled.
+	 */
+	pm_runtime_get_sync(port->dev);
+	spin_lock_irqsave(&port->lock, flags);
+
+	/*
+	 * Update the per-port timeout.
+	 */
+	uart_update_timeout(port, termios->c_cflag, baud);
+
+	up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (termios->c_iflag & INPCK)
+		up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (termios->c_iflag & (BRKINT | PARMRK))
+		up->port.read_status_mask |= UART_LSR_BI;
+
+	/*
+	 * Characters to ignore
+	 */
+	up->port.ignore_status_mask = 0;
+	if (termios->c_iflag & IGNPAR)
+		up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	if (termios->c_iflag & IGNBRK) {
+		up->port.ignore_status_mask |= UART_LSR_BI;
+		/*
+		 * If we're ignoring parity and break indicators,
+		 * ignore overruns too (for real raw support).
+		 */
+		if (termios->c_iflag & IGNPAR)
+			up->port.ignore_status_mask |= UART_LSR_OE;
+	}
+
+	/*
+	 * ignore all characters if CREAD is not set
+	 */
+	if ((termios->c_cflag & CREAD) == 0)
+		up->port.ignore_status_mask |= UART_LSR_DR;
+
+	/*
+	 * Modem status interrupts
+	 */
+	up->ier &= ~UART_IER_MSI;
+	if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+		up->ier |= UART_IER_MSI;
+
+	up->lcr = cval;
+	/* Up to here it was mostly serial8250_do_set_termios() */
+
+	/*
+	 * We enable TRIG_GRANU for RX and TX and additionaly we set
+	 * SCR_TX_EMPTY bit. The result is the following:
+	 * - RX_TRIGGER amount of bytes in the FIFO will cause an interrupt.
+	 * - less than RX_TRIGGER number of bytes will also cause an interrupt
+	 *   once the UART decides that there no new bytes arriving.
+	 * - Once THRE is enabled, the interrupt will be fired once the FIFO is
+	 *   empty - the trigger level is ignored here.
+	 *
+	 * Once DMA is enabled:
+	 * - UART will assert the TX DMA line once there is room for TX_TRIGGER
+	 *   bytes in the TX FIFO. On each assert the DMA engine will move
+	 *   TX_TRIGGER bytes into the FIFO.
+	 * - UART will assert the RX DMA line once there are RX_TRIGGER bytes in
+	 *   the FIFO and move RX_TRIGGER bytes.
+	 * This is because treshold and trigger values are the same.
+	 */
+	up->fcr = UART_FCR_ENABLE_FIFO;
+	up->fcr |= TRIGGER_FCR_MASK(TX_TRIGGER) << OMAP_UART_FCR_TX_TRIG;
+	up->fcr |= TRIGGER_FCR_MASK(RX_TRIGGER) << OMAP_UART_FCR_RX_TRIG;
+
+	priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY |
+		OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
+
+	priv->xon = termios->c_cc[VSTART];
+	priv->xoff = termios->c_cc[VSTOP];
+
+	priv->efr = 0;
+	if (termios->c_cflag & CRTSCTS && up->port.flags & UPF_HARD_FLOW) {
+		/* Enable AUTORTS and AUTOCTS */
+		priv->efr |= UART_EFR_CTS | UART_EFR_RTS;
+
+		/* Ensure MCR RTS is asserted */
+		up->mcr |= UART_MCR_RTS;
+	}
+
+	if (up->port.flags & UPF_SOFT_FLOW) {
+		/*
+		 * IXON Flag:
+		 * Enable XON/XOFF flow control on input.
+		 * Receiver compares XON1, XOFF1.
+		 */
+		if (termios->c_iflag & IXON)
+			priv->efr |= OMAP_UART_SW_RX;
+
+		/*
+		 * IXOFF Flag:
+		 * Enable XON/XOFF flow control on output.
+		 * Transmit XON1, XOFF1
+		 */
+		if (termios->c_iflag & IXOFF)
+			priv->efr |= OMAP_UART_SW_TX;
+
+		/*
+		 * IXANY Flag:
+		 * Enable any character to restart output.
+		 * Operation resumes after receiving any
+		 * character after recognition of the XOFF character
+		 */
+		if (termios->c_iflag & IXANY)
+			up->mcr |= UART_MCR_XONANY;
+		else
+			up->mcr &= ~UART_MCR_XONANY;
+	}
+	omap8250_restore_regs(up);
+
+	spin_unlock_irqrestore(&up->port.lock, flags);
+	pm_runtime_mark_last_busy(port->dev);
+	pm_runtime_put_autosuspend(port->dev);
+
+	/* calculate wakeup latency constraint */
+	priv->calc_latency = USEC_PER_SEC * 64 * 8 / baud;
+	priv->latency = priv->calc_latency;
+
+	schedule_work(&priv->qos_work);
+
+	/* Don't rewrite B0 */
+	if (tty_termios_baud_rate(termios))
+		tty_termios_encode_baud_rate(termios, baud, baud);
+}
+
+/* same as 8250 except that we may have extra flow bits set in EFR */
+static void omap_8250_pm(struct uart_port *port, unsigned int state,
+		unsigned int oldstate)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	struct omap8250_priv *priv = up->port.private_data;
+
+	pm_runtime_get_sync(port->dev);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, priv->efr | UART_EFR_ECB);
+	serial_out(up, UART_LCR, 0);
+
+	serial_out(up, UART_IER, (state != 0) ? UART_IERX_SLEEP : 0);
+	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+	serial_out(up, UART_EFR, priv->efr);
+	serial_out(up, UART_LCR, 0);
+
+	pm_runtime_mark_last_busy(port->dev);
+	pm_runtime_put_autosuspend(port->dev);
+}
+
+static void omap_serial_fill_features_erratas(struct uart_8250_port *up,
+		struct omap8250_priv *priv)
+{
+	u32 mvr, scheme;
+	u16 revision, major, minor;
+
+	mvr = uart_read(up, UART_OMAP_MVER);
+
+	/* Check revision register scheme */
+	scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT;
+
+	switch (scheme) {
+	case 0: /* Legacy Scheme: OMAP2/3 */
+		/* MINOR_REV[0:4], MAJOR_REV[4:7] */
+		major = (mvr & OMAP_UART_LEGACY_MVR_MAJ_MASK) >>
+			OMAP_UART_LEGACY_MVR_MAJ_SHIFT;
+		minor = (mvr & OMAP_UART_LEGACY_MVR_MIN_MASK);
+		break;
+	case 1:
+		/* New Scheme: OMAP4+ */
+		/* MINOR_REV[0:5], MAJOR_REV[8:10] */
+		major = (mvr & OMAP_UART_MVR_MAJ_MASK) >>
+			OMAP_UART_MVR_MAJ_SHIFT;
+		minor = (mvr & OMAP_UART_MVR_MIN_MASK);
+		break;
+	default:
+		dev_warn(up->port.dev,
+				"Unknown revision, defaulting to highest\n");
+		/* highest possible revision */
+		major = 0xff;
+		minor = 0xff;
+	}
+	/* normalize revision for the driver */
+	revision = UART_BUILD_REVISION(major, minor);
+
+	switch (revision) {
+	case OMAP_UART_REV_46:
+		priv->habit = UART_ERRATA_i202_MDR1_ACCESS;
+		break;
+	case OMAP_UART_REV_52:
+		priv->habit = UART_ERRATA_i202_MDR1_ACCESS |
+				OMAP_UART_WER_HAS_TX_WAKEUP;
+		break;
+	case OMAP_UART_REV_63:
+		priv->habit = UART_ERRATA_i202_MDR1_ACCESS |
+			OMAP_UART_WER_HAS_TX_WAKEUP;
+		break;
+	default:
+		break;
+	}
+}
+
+static void omap8250_uart_qos_work(struct work_struct *work)
+{
+	struct omap8250_priv *priv;
+
+	priv = container_of(work, struct omap8250_priv, qos_work);
+	pm_qos_update_request(&priv->pm_qos_request, priv->latency);
+}
+
+static irqreturn_t omap_wake_irq(int irq, void *dev_id)
+{
+	struct uart_port *port = dev_id;
+	int ret;
+
+	ret = port->handle_irq(port);
+	if (ret)
+		return IRQ_HANDLED;
+	return IRQ_NONE;
+}
+
+static int omap_8250_startup(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	struct omap8250_priv *priv = port->private_data;
+
+	int ret;
+
+	if (priv->wakeirq) {
+		ret = request_irq(priv->wakeirq, omap_wake_irq,
+				port->irqflags, "wakeup irq", port);
+		if (ret)
+			return ret;
+		disable_irq(priv->wakeirq);
+	}
+
+	pm_runtime_get_sync(port->dev);
+
+	ret = serial8250_do_startup(port);
+	if (ret)
+		goto err;
+
+#ifdef CONFIG_PM_RUNTIME
+	up->capabilities |= UART_CAP_RPM;
+#endif
+
+	/* Enable module level wake up */
+	priv->wer = OMAP_UART_WER_MOD_WKUP;
+	if (priv->habit & OMAP_UART_WER_HAS_TX_WAKEUP)
+		priv->wer |= OMAP_UART_TX_WAKEUP_EN;
+	serial_out(up, UART_OMAP_WER, priv->wer);
+
+	pm_runtime_mark_last_busy(port->dev);
+	pm_runtime_put_autosuspend(port->dev);
+	return 0;
+err:
+	pm_runtime_mark_last_busy(port->dev);
+	pm_runtime_put_autosuspend(port->dev);
+	if (priv->wakeirq)
+		free_irq(priv->wakeirq, port);
+	return ret;
+}
+
+static void omap_8250_shutdown(struct uart_port *port)
+{
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+	struct omap8250_priv *priv = port->private_data;
+
+	flush_work(&priv->qos_work);
+
+	pm_runtime_get_sync(port->dev);
+
+	serial_out(up, UART_OMAP_WER, 0);
+	serial8250_do_shutdown(port);
+
+	pm_runtime_mark_last_busy(port->dev);
+	pm_runtime_put_autosuspend(port->dev);
+
+	if (priv->wakeirq)
+		free_irq(priv->wakeirq, port);
+}
+
+static void omap_8250_throttle(struct uart_port *port)
+{
+	unsigned long flags;
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	pm_runtime_get_sync(port->dev);
+
+	spin_lock_irqsave(&port->lock, flags);
+	up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
+	serial_out(up, UART_IER, up->ier);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	pm_runtime_mark_last_busy(port->dev);
+	pm_runtime_put_autosuspend(port->dev);
+}
+
+static void omap_8250_unthrottle(struct uart_port *port)
+{
+	unsigned long flags;
+	struct uart_8250_port *up =
+		container_of(port, struct uart_8250_port, port);
+
+	pm_runtime_get_sync(port->dev);
+
+	spin_lock_irqsave(&port->lock, flags);
+	up->ier |= UART_IER_RLSI | UART_IER_RDI;
+	serial_out(up, UART_IER, up->ier);
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	pm_runtime_mark_last_busy(port->dev);
+	pm_runtime_put_autosuspend(port->dev);
+}
+
+static int omap8250_probe(struct platform_device *pdev)
+{
+	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct resource *irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	struct omap8250_priv *priv;
+	struct uart_8250_port up;
+	int ret;
+	void __iomem *membase;
+
+	if (!regs || !irq) {
+		dev_err(&pdev->dev, "missing registers or irq\n");
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	membase = devm_ioremap_nocache(&pdev->dev, regs->start,
+			resource_size(regs));
+	if (!membase)
+		return -ENODEV;
+
+	memset(&up, 0, sizeof(up));
+	up.port.dev = &pdev->dev;
+	up.port.mapbase = regs->start;
+	up.port.membase = membase;
+	up.port.irq = irq->start;
+	/*
+	 * It claims to be 16C750 compatible however it is a little different.
+	 * It has EFR and has no FCR7_64byte bit. The AFE (which it claims to
+	 * have) is enabled via EFR instead of MCR. The type is set here 8250
+	 * just to get things going. UNKNOWN does not work for a few reasons and
+	 * we don't need our own type since we don't use 8250's set_termios()
+	 * and our "bugs" are handeld via the bug member.
+	 */
+	up.port.type = PORT_8250;
+	up.port.iotype = UPIO_MEM;
+	up.port.flags = UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_SOFT_FLOW |
+		UPF_HARD_FLOW;
+	up.port.private_data = priv;
+
+	up.port.regshift = 2;
+	up.port.fifosize = 64;
+	up.tx_loadsz = 64;
+	up.capabilities = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP;
+#ifdef CONFIG_PM_RUNTIME
+	/*
+	 * PM_RUNTIME is mostly transparent. However to do it right we need to a
+	 * TX empty interrupt before we can put the device to auto idle. So if
+	 * PM_RUNTIME is not enabled we don't add that flag and can spare that
+	 * one extra interrupt in the TX path.
+	 */
+	up.capabilities |= UART_CAP_RPM;
+#endif
+	up.port.set_termios = omap_8250_set_termios;
+	up.port.pm = omap_8250_pm;
+	up.port.startup = omap_8250_startup;
+	up.port.shutdown = omap_8250_shutdown;
+	up.port.throttle = omap_8250_throttle;
+	up.port.unthrottle = omap_8250_unthrottle;
+
+	if (pdev->dev.of_node) {
+		up.port.line = of_alias_get_id(pdev->dev.of_node, "serial");
+		of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+				&up.port.uartclk);
+		priv->wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1);
+	} else {
+		up.port.line = pdev->id;
+	}
+
+	if (up.port.line < 0) {
+		dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n",
+				up.port.line);
+		return -ENODEV;
+	}
+	if (!up.port.uartclk) {
+		up.port.uartclk = DEFAULT_CLK_SPEED;
+		dev_warn(&pdev->dev,
+				"No clock speed specified: using default: %d\n",
+				DEFAULT_CLK_SPEED);
+	}
+
+	priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+	priv->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+	pm_qos_add_request(&priv->pm_qos_request,
+			PM_QOS_CPU_DMA_LATENCY, priv->latency);
+	INIT_WORK(&priv->qos_work, omap8250_uart_qos_work);
+
+	device_init_wakeup(&pdev->dev, true);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, -1);
+
+	pm_runtime_irq_safe(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	pm_runtime_get_sync(&pdev->dev);
+
+	omap_serial_fill_features_erratas(&up, priv);
+	ret = serial8250_register_8250_port(&up);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "unable to register 8250 port\n");
+		goto err;
+	}
+	priv->line = ret;
+	platform_set_drvdata(pdev, priv);
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_put_autosuspend(&pdev->dev);
+	return 0;
+err:
+	pm_runtime_put(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	return ret;
+}
+
+static int omap8250_remove(struct platform_device *pdev)
+{
+	struct omap8250_priv *priv = platform_get_drvdata(pdev);
+
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	serial8250_unregister_port(priv->line);
+	pm_qos_remove_request(&priv->pm_qos_request);
+	device_init_wakeup(&pdev->dev, false);
+	return 0;
+}
+
+#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_PM_RUNTIME)
+
+static inline void omap8250_enable_wakeirq(struct omap8250_priv *priv,
+		bool enable)
+{
+	if (!priv->wakeirq)
+		return;
+
+	if (enable)
+		enable_irq(priv->wakeirq);
+	else
+		disable_irq_nosync(priv->wakeirq);
+}
+
+static void omap8250_enable_wakeup(struct omap8250_priv *priv,
+		bool enable)
+{
+	if (enable == priv->wakeups_enabled)
+		return;
+
+	omap8250_enable_wakeirq(priv, enable);
+	priv->wakeups_enabled = enable;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int omap8250_prepare(struct device *dev)
+{
+	struct omap8250_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv)
+		return 0;
+	priv->is_suspending = true;
+	return 0;
+}
+
+static void omap8250_complete(struct device *dev)
+{
+	struct omap8250_priv *priv = dev_get_drvdata(dev);
+
+	if (!priv)
+		return;
+	priv->is_suspending = false;
+}
+
+static int omap8250_suspend(struct device *dev)
+{
+	struct omap8250_priv *priv = dev_get_drvdata(dev);
+
+	serial8250_suspend_port(priv->line);
+	flush_work(&priv->qos_work);
+
+	if (device_may_wakeup(dev))
+		omap8250_enable_wakeup(priv, true);
+	else
+		omap8250_enable_wakeup(priv, false);
+	return 0;
+}
+
+static int omap8250_resume(struct device *dev)
+{
+	struct omap8250_priv *priv = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		omap8250_enable_wakeup(priv, false);
+
+	serial8250_resume_port(priv->line);
+	return 0;
+}
+#else
+#define omap8250_prepare NULL
+#define omap8250_complete NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int omap8250_lost_context(struct uart_8250_port *up)
+{
+	u32 val;
+
+	val = serial_in(up, UART_OMAP_MDR1);
+	/*
+	 * If we lose context, then MDR1 is set to its reset value which is
+	 * UART_OMAP_MDR1_DISABLE. After set_termios() we set it either to 13x
+	 * or 16x but never to disable again.
+	 */
+	if (val == UART_OMAP_MDR1_DISABLE)
+		return 1;
+	return 0;
+}
+
+static int omap8250_runtime_suspend(struct device *dev)
+{
+	struct omap8250_priv *priv = dev_get_drvdata(dev);
+	struct uart_8250_port *up;
+
+	up = serial8250_get_port(priv->line);
+	/*
+	 * When using 'no_console_suspend', the console UART must not be
+	 * suspended. Since driver suspend is managed by runtime suspend,
+	 * preventing runtime suspend (by returning error) will keep device
+	 * active during suspend.
+	 */
+	if (priv->is_suspending && !console_suspend_enabled) {
+		if (uart_console(&up->port))
+			return -EBUSY;
+	}
+
+	omap8250_enable_wakeup(priv, true);
+
+	priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+	schedule_work(&priv->qos_work);
+
+	return 0;
+}
+
+static int omap8250_runtime_resume(struct device *dev)
+{
+	struct omap8250_priv *priv = dev_get_drvdata(dev);
+	struct uart_8250_port *up;
+	int loss_cntx;
+
+	/* In case runtime-pm tries this before we are setup */
+	if (!priv)
+		return 0;
+
+	up = serial8250_get_port(priv->line);
+	omap8250_enable_wakeup(priv, false);
+	loss_cntx = omap8250_lost_context(up);
+
+	if (loss_cntx)
+		omap8250_restore_regs(up);
+
+	priv->latency = priv->calc_latency;
+	schedule_work(&priv->qos_work);
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops omap8250_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(omap8250_suspend, omap8250_resume)
+	SET_RUNTIME_PM_OPS(omap8250_runtime_suspend,
+			omap8250_runtime_resume, NULL)
+	.prepare        = omap8250_prepare,
+	.complete       = omap8250_complete,
+};
+
+static const struct of_device_id omap8250_dt_ids[] = {
+	{ .compatible = "ti,omap2-uart" },
+	{ .compatible = "ti,omap3-uart" },
+	{ .compatible = "ti,omap4-uart" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, omap8250_dt_ids);
+
+static struct platform_driver omap8250_platform_driver = {
+	.driver = {
+		.name		= "omap8250",
+		.pm		= &omap8250_dev_pm_ops,
+		.of_match_table = omap8250_dt_ids,
+		.owner		= THIS_MODULE,
+	},
+	.probe			= omap8250_probe,
+	.remove			= omap8250_remove,
+};
+module_platform_driver(omap8250_platform_driver);
+
+MODULE_AUTHOR("Sebastian Andrzej Siewior");
+MODULE_DESCRIPTION("OMAP 8250 Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index 349ee598b34c..7a5073b3726f 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -298,3 +298,12 @@ config SERIAL_8250_RT288X
 	  If you have a Ralink RT288x/RT305x SoC based board and want to use the
 	  serial port, say Y to this option. The driver can handle up to 2 serial
 	  ports. If unsure, say N.
+
+config SERIAL_8250_OMAP
+	tristate "Support for OMAP internal UART (8250 based driver)"
+	depends on SERIAL_8250 && ARCH_OMAP2PLUS
+	help
+	  If you have a machine based on an Texas Instruments OMAP CPU you
+	  can enable its onboard serial ports by enabling this option.
+
+	  This driver is in early stage and uses ttyS instead of ttyO.
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index 36d68d054307..4bac3923759b 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -20,3 +20,4 @@ obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
 obj-$(CONFIG_SERIAL_8250_FSL)		+= 8250_fsl.o
 obj-$(CONFIG_SERIAL_8250_DW)		+= 8250_dw.o
 obj-$(CONFIG_SERIAL_8250_EM)		+= 8250_em.o
+obj-$(CONFIG_SERIAL_8250_OMAP)		+= 8250_omap.o
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 09/18] tty: serial: 8250_dma: handle error on TX submit
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (7 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 08/18] tty: serial: Add 8250-core based omap driver Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 10/18] tty: serial: 8250_dma: enqueue RX dma again on completion Sebastian Andrzej Siewior
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

Right now it is possible that serial8250_tx_dma() fails and returns
-EBUSY. The caller (serial8250_start_tx()) will then enable
UART_IER_THRI which will generate an interrupt once the TX FIFO is
empty.
In serial8250_handle_irq() nothing will happen because up->dma is set
and so serial8250_tx_chars() won't be invoked. We end up with plenty of
interrupts and some "too much work for irq" output.

This patch introduces dma_tx_err in struct uart_8250_port to signal that
the last invocation of serial8250_tx_dma() failed so we can fill the TX
FIFO manually. Should the next invocation of serial8250_start_tx()
succeed then the dma_tx_err flag along with the THRI bit is removed and
DMA only usage may continue.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250.h      |  1 +
 drivers/tty/serial/8250/8250_core.c |  4 +++-
 drivers/tty/serial/8250/8250_dma.c  | 30 +++++++++++++++++++++++++-----
 3 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 1bcb4b2141a6..a63d198f8d03 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -41,6 +41,7 @@ struct uart_8250_dma {
 	size_t			tx_size;
 
 	unsigned char		tx_running:1;
+	unsigned char		tx_err: 1;
 };
 
 struct old_serial_port {
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index e8df7cd85ba7..2101134e5031 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1592,8 +1592,10 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
 			status = serial8250_rx_chars(up, status);
 	}
 	serial8250_modem_status(up);
-	if (!up->dma && (status & UART_LSR_THRE))
+	if ((!up->dma || (up->dma && up->dma->tx_err)) &&
+			(status & UART_LSR_THRE)) {
 		serial8250_tx_chars(up);
+	}
 
 	spin_unlock_irqrestore(&port->lock, flags);
 	return 1;
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 148ffe4c232f..69e54abb6e71 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -36,8 +36,16 @@ static void __dma_tx_complete(void *param)
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 		uart_write_wakeup(&p->port);
 
-	if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port))
-		serial8250_tx_dma(p);
+	if (!uart_circ_empty(xmit) && !uart_tx_stopped(&p->port)) {
+		int ret;
+
+		ret = serial8250_tx_dma(p);
+		if (ret) {
+			dma->tx_err = 1;
+			p->ier |= UART_IER_THRI;
+			serial_port_out(&p->port, UART_IER, p->ier);
+		}
+	}
 
 	spin_unlock_irqrestore(&p->port.lock, flags);
 }
@@ -69,6 +77,7 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 	struct uart_8250_dma		*dma = p->dma;
 	struct circ_buf			*xmit = &p->port.state->xmit;
 	struct dma_async_tx_descriptor	*desc;
+	int ret;
 
 	if (uart_tx_stopped(&p->port) || dma->tx_running ||
 	    uart_circ_empty(xmit))
@@ -80,8 +89,10 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 					   dma->tx_addr + xmit->tail,
 					   dma->tx_size, DMA_MEM_TO_DEV,
 					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-	if (!desc)
-		return -EBUSY;
+	if (!desc) {
+		ret = -EBUSY;
+		goto err;
+	}
 
 	dma->tx_running = 1;
 
@@ -94,8 +105,17 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 				   UART_XMIT_SIZE, DMA_TO_DEVICE);
 
 	dma_async_issue_pending(dma->txchan);
-
+	if (dma->tx_err) {
+		dma->tx_err = 0;
+		if (p->ier & UART_IER_THRI) {
+			p->ier &= ~UART_IER_THRI;
+			serial_out(p, UART_IER, p->ier);
+		}
+	}
 	return 0;
+err:
+	dma->tx_err = 1;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(serial8250_tx_dma);
 
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 10/18] tty: serial: 8250_dma: enqueue RX dma again on completion.
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (8 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 09/18] tty: serial: 8250_dma: handle error on TX submit Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 11/18] tty: serial: 8250_dma: Add a TX trigger workaround for AM33xx Sebastian Andrzej Siewior
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

The omap needs a DMA request pending right away. If it is enqueued once
the bytes are in the FIFO then nothing will happen and the FIFO will be
later purged via RX-timeout interrupt.
This patch enqueues RX-DMA request on completion but not if it was
aborted on error. The first enqueue will happen in the driver in
startup.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250.h      |  3 ++-
 drivers/tty/serial/8250/8250_core.c |  3 +++
 drivers/tty/serial/8250/8250_dma.c  | 12 +++++++++---
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index a63d198f8d03..fbed1636e9c4 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -80,7 +80,8 @@ struct serial8250_config {
 #define UART_BUG_NOMSR	(1 << 2)	/* UART has buggy MSR status bits (Au1x00) */
 #define UART_BUG_THRE	(1 << 3)	/* UART has buggy THRE reassertion */
 #define UART_BUG_PARITY	(1 << 4)	/* UART mishandles parity if FIFO enabled */
-
+#define UART_BUG_DMA_RX	(1 << 5)	/* UART needs DMA RX req before there is
+					   data in FIFO */
 #define PROBE_RSA	(1 << 0)
 #define PROBE_ANY	(~0)
 
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 2101134e5031..079048db8c81 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1590,6 +1590,9 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
 
 		if (!up->dma || dma_err)
 			status = serial8250_rx_chars(up, status);
+
+		if (dma_err && up->bugs & UART_BUG_DMA_RX)
+			serial8250_rx_dma(up, 0);
 	}
 	serial8250_modem_status(up);
 	if ((!up->dma || (up->dma && up->dma->tx_err)) &&
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 69e54abb6e71..3674900a1f14 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -50,9 +50,8 @@ static void __dma_tx_complete(void *param)
 	spin_unlock_irqrestore(&p->port.lock, flags);
 }
 
-static void __dma_rx_complete(void *param)
+static void __dma_rx_do_complete(struct uart_8250_port *p, bool error)
 {
-	struct uart_8250_port	*p = param;
 	struct uart_8250_dma	*dma = p->dma;
 	struct tty_port		*tty_port = &p->port.state->port;
 	struct dma_tx_state	state;
@@ -68,10 +67,17 @@ static void __dma_rx_complete(void *param)
 
 	tty_insert_flip_string(tty_port, dma->rx_buf, count);
 	p->port.icount.rx += count;
+	if (!error && p->bugs & UART_BUG_DMA_RX)
+		serial8250_rx_dma(p, 0);
 
 	tty_flip_buffer_push(tty_port);
 }
 
+static void __dma_rx_complete(void *param)
+{
+	__dma_rx_do_complete(param, false);
+}
+
 int serial8250_tx_dma(struct uart_8250_port *p)
 {
 	struct uart_8250_dma		*dma = p->dma;
@@ -139,7 +145,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
 		 */
 		if (dma_status == DMA_IN_PROGRESS) {
 			dmaengine_pause(dma->rxchan);
-			__dma_rx_complete(p);
+			__dma_rx_do_complete(p, true);
 		}
 		return -ETIMEDOUT;
 	default:
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 11/18] tty: serial: 8250_dma: Add a TX trigger workaround for AM33xx
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (9 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 10/18] tty: serial: 8250_dma: enqueue RX dma again on completion Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-08 16:41   ` Tony Lindgren
  2014-09-05 19:02 ` [PATCH 12/18] tty: serial: 8250_dma: optimize the xmit path due to UART_BUG_DMA_TX Sebastian Andrzej Siewior
                   ` (8 subsequent siblings)
  19 siblings, 1 reply; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

At least on AM335x the following problem exists: Even if the TX FIFO is
empty and a TX transfer is programmed (and started) the UART does not
trigger the DMA transfer.
After $TRESHOLD number of bytes have been written to the FIFO manually the
UART reevaluates the whole situation and decides that now there is enough
room in the FIFO and so the transfer begins.
This problem has not been seen on DRA7 or beaglebone (OMAP3). I am not
sure if this is UART-IP core specific or DMA engine.

The workaround is to use a threshold of one byte, program the DMA
transfer minus one byte and then to put the first byte into the FIFO to
kick start the transfer.

v7?v8:
	- fix the problem when get invoked and the FIFO is full.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250.h     |  3 +++
 drivers/tty/serial/8250/8250_dma.c | 39 +++++++++++++++++++++++++++++++++++---
 include/uapi/linux/serial_reg.h    |  1 +
 3 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index fbed1636e9c4..09489b391568 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -82,6 +82,9 @@ struct serial8250_config {
 #define UART_BUG_PARITY	(1 << 4)	/* UART mishandles parity if FIFO enabled */
 #define UART_BUG_DMA_RX	(1 << 5)	/* UART needs DMA RX req before there is
 					   data in FIFO */
+#define UART_BUG_DMA_TX	(1 << 6)	/* UART needs one byte in FIFO for
+					   kickstart */
+
 #define PROBE_RSA	(1 << 0)
 #define PROBE_ANY	(~0)
 
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 3674900a1f14..48dc57aad0dd 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -83,6 +83,7 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 	struct uart_8250_dma		*dma = p->dma;
 	struct circ_buf			*xmit = &p->port.state->xmit;
 	struct dma_async_tx_descriptor	*desc;
+	unsigned int			skip_byte = 0;
 	int ret;
 
 	if (uart_tx_stopped(&p->port) || dma->tx_running ||
@@ -91,10 +92,40 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 
 	dma->tx_size = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
 
+	if (p->bugs & UART_BUG_DMA_TX) {
+		u8 tx_lvl;
+
+		/*
+		 * We need to put the first byte into the FIFO in order to start
+		 * the DMA transfer. For transfers smaller than four bytes we
+		 * don't bother doing DMA at all. It seem not matter if there
+		 * are still bytes in the FIFO from the last transfer (in case
+		 * we got here directly from __dma_tx_complete()). Bytes leaving
+		 * the FIFO seem not to trigger the DMA transfer. It is really
+		 * the byte that we put into the FIFO.
+		 * If the FIFO is already full then we most likely got here from
+		 * __dma_tx_complete(). And this means the DMA engine just
+		 * completed its work. We don't have to wait the complete 86us
+		 * at 115200,8n1 but around 60us (not to mention lower
+		 * baudrates). So in that case we take the interrupt and try
+		 * again with an empty FIFO.
+		 */
+		tx_lvl = serial_in(p, UART_OMAP_TX_LVL);
+		if (tx_lvl == p->tx_loadsz) {
+			ret = -EBUSY;
+			goto err;
+		}
+		if (dma->tx_size < 4) {
+			ret = -EINVAL;
+			goto err;
+		}
+		skip_byte = 1;
+	}
+
 	desc = dmaengine_prep_slave_single(dma->txchan,
-					   dma->tx_addr + xmit->tail,
-					   dma->tx_size, DMA_MEM_TO_DEV,
-					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+			dma->tx_addr + xmit->tail + skip_byte,
+			dma->tx_size - skip_byte, DMA_MEM_TO_DEV,
+			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
 	if (!desc) {
 		ret = -EBUSY;
 		goto err;
@@ -118,6 +149,8 @@ int serial8250_tx_dma(struct uart_8250_port *p)
 			serial_out(p, UART_IER, p->ier);
 		}
 	}
+	if (skip_byte)
+		serial_out(p, UART_TX, xmit->buf[xmit->tail]);
 	return 0;
 err:
 	dma->tx_err = 1;
diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h
index df6c9ab6b0cd..53af3b790129 100644
--- a/include/uapi/linux/serial_reg.h
+++ b/include/uapi/linux/serial_reg.h
@@ -359,6 +359,7 @@
 #define UART_OMAP_SYSC		0x15	/* System configuration register */
 #define UART_OMAP_SYSS		0x16	/* System status register */
 #define UART_OMAP_WER		0x17	/* Wake-up enable register */
+#define UART_OMAP_TX_LVL	0x1a	/* TX FIFO level register */
 
 /*
  * These are the definitions for the MDR1 register
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 12/18] tty: serial: 8250_dma: optimize the xmit path due to UART_BUG_DMA_TX
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (10 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 11/18] tty: serial: 8250_dma: Add a TX trigger workaround for AM33xx Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 13/18] tty: serial: 8250_dma: keep own book keeping about RX transfers Sebastian Andrzej Siewior
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

Due to UART_BUG_DMA_TX the am335x has to put the first one byte into the
TX FIFO to get things going. If we get to serial8250_tx_dma() via
__dma_tx_complete() then the DMA engine just finished and the FIFO is
full and we can't put the first byte into the TX FIFO as kick start so
we leave with THRI enabled.
The result is that we end up manually filling the FIFO. We could be a
little better at this and try DMA once again. If it works, it works and
if not we do it manually.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_core.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 079048db8c81..7111b22de000 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1597,9 +1597,21 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
 	serial8250_modem_status(up);
 	if ((!up->dma || (up->dma && up->dma->tx_err)) &&
 			(status & UART_LSR_THRE)) {
-		serial8250_tx_chars(up);
-	}
+		if (!up->dma) {
+			serial8250_tx_chars(up);
+		} else {
+			if (uart_tx_stopped(&up->port) ||
+				uart_circ_empty(&up->port.state->xmit)) {
+				serial8250_tx_chars(up);
 
+			} else  {
+				/* try again due to UART_BUG_DMA_TX+full fifo */
+				dma_err = serial8250_tx_dma(up);
+				if (dma_err)
+					serial8250_tx_chars(up);
+			}
+		}
+	}
 	spin_unlock_irqrestore(&port->lock, flags);
 	return 1;
 }
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 13/18] tty: serial: 8250_dma: keep own book keeping about RX transfers
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (11 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 12/18] tty: serial: 8250_dma: optimize the xmit path due to UART_BUG_DMA_TX Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 14/18] tty: serial: 8250_dma: handle the when UART response while DMA remains idle Sebastian Andrzej Siewior
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

After dmaengine_terminate_all() has been invoked then both DMA drivers
(edma and omap-dma) do not invoke dma_cookie_complete() to mark the
transfer as complete. This dma_cookie_complete() is performed by the
Synopsys DesignWare driver which is probably the only one that is used
by omap8250-dma and hence don't see following problem?
?which is that once a RX transfer has been terminated then following
query of channel status reports DMA_IN_PROGRESS (again: the actual
transfer has been canceled, there is nothing going on anymore).

This means that serial8250_rx_dma() never enqueues another DMA transfer
because it (wrongly) assumes that there is a transer already pending.

Vinod Koul refuses to accept a patch which adds this
dma_cookie_complete() to both drivers and so dmaengine_tx_status() would
report DMA_COMPLETE instead (and behave like the Synopsys DesignWare
driver already does). He argues that I am not allowed to use the cookie
to query the status and that the driver already cleaned everything up after
the invokation of dmaengine_terminate_all().

To end this I add a bookkeeping whether or not a RX-transfer has been
started to the 8250-dma code. It has already been done for the TX side.
*Now* we learn about the RX status based on our bookkeeping and don't
need dmaengine_tx_status() for this anymore.

Cc: vinod.koul at intel.com
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250.h     |  1 +
 drivers/tty/serial/8250/8250_dma.c | 14 ++++++++------
 2 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 09489b391568..a7581b37f264 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -42,6 +42,7 @@ struct uart_8250_dma {
 
 	unsigned char		tx_running:1;
 	unsigned char		tx_err: 1;
+	unsigned char		rx_running:1;
 };
 
 struct old_serial_port {
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 48dc57aad0dd..6cd8a4db609a 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -60,6 +60,7 @@ static void __dma_rx_do_complete(struct uart_8250_port *p, bool error)
 	dma_sync_single_for_cpu(dma->rxchan->device->dev, dma->rx_addr,
 				dma->rx_size, DMA_FROM_DEVICE);
 
+	dma->rx_running = 0;
 	dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
 	dmaengine_terminate_all(dma->rxchan);
 
@@ -162,21 +163,21 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
 {
 	struct uart_8250_dma		*dma = p->dma;
 	struct dma_async_tx_descriptor	*desc;
-	struct dma_tx_state		state;
-	int				dma_status;
-
-	dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
 
 	switch (iir & 0x3f) {
 	case UART_IIR_RLSI:
 		/* 8250_core handles errors and break interrupts */
+		if (dma->rx_running) {
+			dmaengine_pause(dma->rxchan);
+			__dma_rx_do_complete(p, true);
+		}
 		return -EIO;
 	case UART_IIR_RX_TIMEOUT:
 		/*
 		 * If RCVR FIFO trigger level was not reached, complete the
 		 * transfer and let 8250_core copy the remaining data.
 		 */
-		if (dma_status == DMA_IN_PROGRESS) {
+		if (dma->rx_running) {
 			dmaengine_pause(dma->rxchan);
 			__dma_rx_do_complete(p, true);
 		}
@@ -185,7 +186,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
 		break;
 	}
 
-	if (dma_status)
+	if (dma->rx_running)
 		return 0;
 
 	desc = dmaengine_prep_slave_single(dma->rxchan, dma->rx_addr,
@@ -194,6 +195,7 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
 	if (!desc)
 		return -EBUSY;
 
+	dma->rx_running = 1;
 	desc->callback = __dma_rx_complete;
 	desc->callback_param = p;
 
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 14/18] tty: serial: 8250_dma: handle the when UART response while DMA remains idle
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (12 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 13/18] tty: serial: 8250_dma: keep own book keeping about RX transfers Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 15/18] tty: serial: 8250_dma: add pm runtime Sebastian Andrzej Siewior
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

Sometimes the OMAP UART does not signal the DMA engine to unload the FIFO.
Usually this happens when we have >threshold bytes in the FIFO
and start the DMA transfer. It seems that in those cases the UART won't
trigger the transfer once the requested threshold is reached. In some
rare cases the UART does not trigger the DMA transfer even if programmed
while the FIFO was empty.
In those cases the UART drops an RDI event and we have to empty the FIFO
manually. If we ignore it because the DMA transfer is programmed then we
will enter the function a few times until we receive the RX_TIMEOUT
event. At that point the FIFO is usually full and we risk to overflow
the FIFO.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_dma.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 6cd8a4db609a..f0fce8236279 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -182,6 +182,24 @@ int serial8250_rx_dma(struct uart_8250_port *p, unsigned int iir)
 			__dma_rx_do_complete(p, true);
 		}
 		return -ETIMEDOUT;
+	case UART_IIR_RDI:
+		if (p->bugs & UART_BUG_DMA_RX)
+			break;
+		/*
+		 * The OMAP UART is a special BEAST. If we receive RDI we _have_
+		 * a DMA transfer programmed but it didn't worked. One reason is
+		 * that we were too slow and there were too many bytes in the
+		 * FIFO, the UART counted wrong and never kicked the DMA engine
+		 * to do anything. That means once we receive RDI on OMAP than
+		 * the DMA won't do anything soon so we have to cancel the DMA
+		 * transfer and purge the FIFO manually.
+		 */
+		if (dma->rx_running) {
+			dmaengine_pause(dma->rxchan);
+			__dma_rx_do_complete(p, true);
+		}
+		return -ETIMEDOUT;
+
 	default:
 		break;
 	}
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 15/18] tty: serial: 8250_dma: add pm runtime
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (13 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 14/18] tty: serial: 8250_dma: handle the when UART response while DMA remains idle Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 16/18] arm: dts: am33xx: add DMA properties for UART Sebastian Andrzej Siewior
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

There is nothing to do for RPM in the RX path. If the HW goes off then it
won't assert the DMA line and the transfer won't happen. So we hope that
the HW does not go off for RX to work (DMA or PIO makes no difference
here).

For TX the situation is slightly different. RPM is enabled on
start_tx(). We can't disable RPM on DMA complete callback because there
is still data in the FIFO which is being sent. We have to wait until
the FIFO is empty before we disable it.
For this to happen we fake a TX sent error and enable THRI. Once the
FIFO is empty we receive an interrupt and since the TTY-buffer is still
empty we "put RPM" via __stop_tx(). Should it been filed then in the
start_tx() path we should program the DMA transfer and remove the error
flag and the THRI bit.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_dma.c | 16 +++++++++++-----
 1 file changed, 11 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index f0fce8236279..1b4a8e02e844 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -21,6 +21,7 @@ static void __dma_tx_complete(void *param)
 	struct uart_8250_dma	*dma = p->dma;
 	struct circ_buf		*xmit = &p->port.state->xmit;
 	unsigned long	flags;
+	bool en_thri = false;
 
 	dma_sync_single_for_cpu(dma->txchan->device->dev, dma->tx_addr,
 				UART_XMIT_SIZE, DMA_TO_DEVICE);
@@ -40,11 +41,16 @@ static void __dma_tx_complete(void *param)
 		int ret;
 
 		ret = serial8250_tx_dma(p);
-		if (ret) {
-			dma->tx_err = 1;
-			p->ier |= UART_IER_THRI;
-			serial_port_out(&p->port, UART_IER, p->ier);
-		}
+		if (ret)
+			en_thri = true;
+
+	} else if (p->capabilities & UART_CAP_RPM)
+		en_thri = true;
+
+	if (en_thri) {
+		dma->tx_err = 1;
+		p->ier |= UART_IER_THRI;
+		serial_port_out(&p->port, UART_IER, p->ier);
 	}
 
 	spin_unlock_irqrestore(&p->port.lock, flags);
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 16/18] arm: dts: am33xx: add DMA properties for UART
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (14 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 15/18] tty: serial: 8250_dma: add pm runtime Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 17/18] arm: dts: dra7: " Sebastian Andrzej Siewior
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: devicetree at vger.kernel.org
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 arch/arm/boot/dts/am33xx.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 3a0a161342ba..078a760a4a21 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -200,6 +200,8 @@
 			reg = <0x44e09000 0x2000>;
 			interrupts = <72>;
 			status = "disabled";
+			dmas = <&edma 26>, <&edma 27>;
+			dma-names = "tx", "rx";
 		};
 
 		uart1: serial at 48022000 {
@@ -209,6 +211,8 @@
 			reg = <0x48022000 0x2000>;
 			interrupts = <73>;
 			status = "disabled";
+			dmas = <&edma 28>, <&edma 29>;
+			dma-names = "tx", "rx";
 		};
 
 		uart2: serial at 48024000 {
@@ -218,6 +222,8 @@
 			reg = <0x48024000 0x2000>;
 			interrupts = <74>;
 			status = "disabled";
+			dmas = <&edma 30>, <&edma 31>;
+			dma-names = "tx", "rx";
 		};
 
 		uart3: serial at 481a6000 {
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 17/18] arm: dts: dra7: add DMA properties for UART
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (15 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 16/18] arm: dts: am33xx: add DMA properties for UART Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-05 19:02 ` [PATCH 18/18] tty: serial: 8250: omap: add dma support Sebastian Andrzej Siewior
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: devicetree at vger.kernel.org
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 arch/arm/boot/dts/dra7.dtsi | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi
index d678152db4cb..f273e3811f75 100644
--- a/arch/arm/boot/dts/dra7.dtsi
+++ b/arch/arm/boot/dts/dra7.dtsi
@@ -332,6 +332,8 @@
 			ti,hwmods = "uart1";
 			clock-frequency = <48000000>;
 			status = "disabled";
+			dmas = <&sdma 49>, <&sdma 50>;
+			dma-names = "tx", "rx";
 		};
 
 		uart2: serial at 4806c000 {
@@ -341,6 +343,8 @@
 			ti,hwmods = "uart2";
 			clock-frequency = <48000000>;
 			status = "disabled";
+			dmas = <&sdma 51>, <&sdma 52>;
+			dma-names = "tx", "rx";
 		};
 
 		uart3: serial at 48020000 {
@@ -350,6 +354,8 @@
 			ti,hwmods = "uart3";
 			clock-frequency = <48000000>;
 			status = "disabled";
+			dmas = <&sdma 53>, <&sdma 54>;
+			dma-names = "tx", "rx";
 		};
 
 		uart4: serial at 4806e000 {
@@ -359,6 +365,8 @@
 			ti,hwmods = "uart4";
 			clock-frequency = <48000000>;
                         status = "disabled";
+			dmas = <&sdma 55>, <&sdma 56>;
+			dma-names = "tx", "rx";
 		};
 
 		uart5: serial at 48066000 {
@@ -368,6 +376,8 @@
 			ti,hwmods = "uart5";
 			clock-frequency = <48000000>;
 			status = "disabled";
+			dmas = <&sdma 63>, <&sdma 64>;
+			dma-names = "tx", "rx";
 		};
 
 		uart6: serial at 48068000 {
@@ -377,6 +387,8 @@
 			ti,hwmods = "uart6";
 			clock-frequency = <48000000>;
 			status = "disabled";
+			dmas = <&sdma 79>, <&sdma 80>;
+			dma-names = "tx", "rx";
 		};
 
 		uart7: serial at 48420000 {
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 18/18] tty: serial: 8250: omap: add dma support
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (16 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 17/18] arm: dts: dra7: " Sebastian Andrzej Siewior
@ 2014-09-05 19:02 ` Sebastian Andrzej Siewior
  2014-09-08 14:46 ` [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Frans Klaver
  2014-09-08 17:40 ` Tony Lindgren
  19 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-05 19:02 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds the required pieces to 8250-OMAP UART driver for DMA
support. The TX burst size is set to 1 so we can send an arbitrary
amount of bytes.

The RX burst is currently set to 48 which means we receive an DMA
interrupt every 48 bytes and have to reprogram everything. Less bytes in
the RX-FIFO mean that no DMA transfer will happen and the UART will send a
RX-timeout _or_ RDI event at which point the FIFO will be manually purged.
There is a workaround for TX-DMA on AM33xx where we put the first byte
into the FIFO to kick start the DMA process. Haven't seen this problem on
AM375x (beagle bone) or DRA7xx.

On AM375x there is "Usage Note 2.7: UART: Cannot Acknowledge Idle
Requests in Smartidle Mode When Configured for DMA Operations" in the
errata document. This problem persists even after disabling DMA in the
UART and will be addressed in the HWMOD.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 drivers/tty/serial/8250/8250_omap.c | 49 +++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index 4128bbc35089..7e98dd413203 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -360,6 +360,10 @@ static void omap_8250_set_termios(struct uart_port *port,
 	priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY |
 		OMAP_UART_SCR_TX_TRIG_GRANU1_MASK;
 
+	if (up->dma)
+		priv->scr |= OMAP_UART_SCR_DMAMODE_1 |
+			OMAP_UART_SCR_DMAMODE_CTL;
+
 	priv->xon = termios->c_cc[VSTART];
 	priv->xoff = termios->c_cc[VSTOP];
 
@@ -542,6 +546,11 @@ static int omap_8250_startup(struct uart_port *port)
 		priv->wer |= OMAP_UART_TX_WAKEUP_EN;
 	serial_out(up, UART_OMAP_WER, priv->wer);
 
+	if (up->dma) {
+		serial8250_rx_dma(up, 0);
+		priv->dma_active = true;
+	}
+
 	pm_runtime_mark_last_busy(port->dev);
 	pm_runtime_put_autosuspend(port->dev);
 	return 0;
@@ -560,6 +569,10 @@ static void omap_8250_shutdown(struct uart_port *port)
 	struct omap8250_priv *priv = port->private_data;
 
 	flush_work(&priv->qos_work);
+	if (up->dma) {
+		serial8250_rx_dma(up, UART_IIR_RX_TIMEOUT);
+		priv->dma_active = false;
+	}
 
 	pm_runtime_get_sync(port->dev);
 
@@ -607,6 +620,13 @@ static void omap_8250_unthrottle(struct uart_port *port)
 	pm_runtime_put_autosuspend(port->dev);
 }
 
+#ifdef CONFIG_SERIAL_8250_DMA
+static bool the_no_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+	return false;
+}
+#endif
+
 static int omap8250_probe(struct platform_device *pdev)
 {
 	struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -706,6 +726,30 @@ static int omap8250_probe(struct platform_device *pdev)
 	pm_runtime_get_sync(&pdev->dev);
 
 	omap_serial_fill_features_erratas(&up, priv);
+#ifdef CONFIG_SERIAL_8250_DMA
+	if (pdev->dev.of_node) {
+		/*
+		 * Oh DMA support. If there are no DMA properties in the DT then
+		 * we will fall back to a generic DMA channel which does not
+		 * really work here. To ensure that we do not get a generic DMA
+		 * channel assigned, we have the the_no_dma_filter_fn() here.
+		 * To avoid "failed to request DMA" messages we check for DMA
+		 * properties in DT.
+		 */
+		ret = of_property_count_strings(pdev->dev.of_node, "dma-names");
+		if (ret == 2) {
+			up.dma = &priv->omap8250_dma;
+			priv->omap8250_dma.fn = the_no_dma_filter_fn;
+			priv->omap8250_dma.rx_size = RX_TRIGGER;
+			priv->omap8250_dma.rxconf.src_maxburst = RX_TRIGGER;
+			priv->omap8250_dma.txconf.dst_maxburst = TX_TRIGGER;
+
+			if (of_machine_is_compatible("ti,am33xx"))
+				up.bugs |= UART_BUG_DMA_TX;
+			up.bugs |= UART_BUG_DMA_RX;
+		}
+	}
+#endif
 	ret = serial8250_register_8250_port(&up);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "unable to register 8250 port\n");
@@ -842,6 +886,8 @@ static int omap8250_runtime_suspend(struct device *dev)
 	}
 
 	omap8250_enable_wakeup(priv, true);
+	if (priv->dma_active)
+		serial8250_rx_dma(up, UART_IIR_RX_TIMEOUT);
 
 	priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
 	schedule_work(&priv->qos_work);
@@ -866,6 +912,9 @@ static int omap8250_runtime_resume(struct device *dev)
 	if (loss_cntx)
 		omap8250_restore_regs(up);
 
+	if (priv->dma_active)
+		serial8250_rx_dma(up, 0);
+
 	priv->latency = priv->calc_latency;
 	schedule_work(&priv->qos_work);
 	return 0;
-- 
2.1.0

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (17 preceding siblings ...)
  2014-09-05 19:02 ` [PATCH 18/18] tty: serial: 8250: omap: add dma support Sebastian Andrzej Siewior
@ 2014-09-08 14:46 ` Frans Klaver
  2014-09-08 15:15   ` Sebastian Andrzej Siewior
  2014-09-08 17:40 ` Tony Lindgren
  19 siblings, 1 reply; 35+ messages in thread
From: Frans Klaver @ 2014-09-08 14:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Fri, Sep 05, 2014 at 09:02:35PM +0200, Sebastian Andrzej Siewior wrote:
> This is my complete queue fo the omap serial driver based on the 8250 core
> code. I played with it on beagle bone, am335x-evm and dra7xx including DMA.
> The runtime-pm pieces look now bug-compatible with the omap-serial driver.
> Besides the runtime-om improvement I also fixed a few corner cases for the
> TX-DMA problem. The DMA fixes (in edma and omap-dma) were dropped and the
> problem has been in 8250-dma via patch #13.

Thanks for the respin. I've just tested the series a bit. Here's some
things I ran into.

- Basic console use is better than the previous series. It behaves
  pretty much like the omap-serial implementation.

- ncurses based applications (vi, less) don't play nice for me on the
  console with this series. less doesn't show me anything. vi doesn't
  return to console properly.

- I seem seem to get stuck in a "serial8250: too much work for irq%d"
  loop somewhat reliably. We have a rather demanding application with
  typically somewhere between 600 and 1000 byte packets being sent at
  240Hz (roughly somewhere between 1.5 and 2 Mb/s). We run at baudrate
  3500k. I get into this "too much work" thing already when running at
  300 bytes per packet.

I hope this is of some use to you. I'll do more testing later.

Thanks,
Frans

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-08 14:46 ` [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Frans Klaver
@ 2014-09-08 15:15   ` Sebastian Andrzej Siewior
  2014-09-08 16:33     ` Sebastian Andrzej Siewior
  2014-09-08 18:33     ` Frans Klaver
  0 siblings, 2 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-08 15:15 UTC (permalink / raw)
  To: linux-arm-kernel

* Frans Klaver | 2014-09-08 16:46:18 [+0200]:

>- ncurses based applications (vi, less) don't play nice for me on the
>  console with this series. less doesn't show me anything. vi doesn't
>  return to console properly.

Can you give a test case 

>- I seem seem to get stuck in a "serial8250: too much work for irq%d"
>  loop somewhat reliably. We have a rather demanding application with
>  typically somewhere between 600 and 1000 byte packets being sent at
>  240Hz (roughly somewhere between 1.5 and 2 Mb/s). We run at baudrate
>  3500k. I get into this "too much work" thing already when running at
>  300 bytes per packet.

Do you get this message also at lower baud rates, say 115200?

What I am trying to understand is why you are spinning in the handler. 
_With_ DMA you should hardly get into the serial handler under normal 
conditions. Running at 3.5MB/sec should give one byte every 2.8us and
48 Bytes every ~137us. This looks like plenty of time to get  out of
the handler. My *guess* is that serial8250_handle_irq() has IIR often
set to timeout and you end up fetching byte after byte. 

This patch should protocol when and why you got into the handler.

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 7111b22de000..59852069e4a0 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1583,6 +1583,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
 	status = serial_port_in(port, UART_LSR);
 
 	DEBUG_INTR("status = %x...", status);
+	trace_printk("l%d IIR %x LSR %x\n", port->line, iir, status);
 
 	if (status & (UART_LSR_DR | UART_LSR_BI)) {
 		if (up->dma)
@@ -1707,6 +1708,7 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
 
 	spin_unlock(&i->lock);
 
+	trace_printk("%d e\n", irq);
 	DEBUG_INTR("end.\n");
 
 	return IRQ_RETVAL(handled);

>I hope this is of some use to you. I'll do more testing later.

Which SoC do you use and do you have DMA enabled?

>Thanks,
>Frans

Sebastian

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-08 15:15   ` Sebastian Andrzej Siewior
@ 2014-09-08 16:33     ` Sebastian Andrzej Siewior
  2014-09-08 18:25       ` Frans Klaver
  2014-09-08 18:33     ` Frans Klaver
  1 sibling, 1 reply; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-08 16:33 UTC (permalink / raw)
  To: linux-arm-kernel

* Sebastian Andrzej Siewior | 2014-09-08 17:15:01 [+0200]:

>* Frans Klaver | 2014-09-08 16:46:18 [+0200]:
>
>>- ncurses based applications (vi, less) don't play nice for me on the
>>  console with this series. less doesn't show me anything. vi doesn't
>>  return to console properly.
>
>Can you give a test case 

Okay. less. My am335x-evm freezes after a while for no obvious reason.
The data that hits the RX fifo is still received but the TX won't do
anything. The DMA request is pending, the FIFO level is @64 bytes and
the UART doesn't make any progress.
On beagle-board I see what you described: less on a file and nothing
happens.
Disabling DMA seems to help fix the problem omap3. Nothing changes for
am335x.

Sebastian

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 08/18] tty: serial: Add 8250-core based omap driver
  2014-09-05 19:02 ` [PATCH 08/18] tty: serial: Add 8250-core based omap driver Sebastian Andrzej Siewior
@ 2014-09-08 16:39   ` Tony Lindgren
  0 siblings, 0 replies; 35+ messages in thread
From: Tony Lindgren @ 2014-09-08 16:39 UTC (permalink / raw)
  To: linux-arm-kernel

* Sebastian Andrzej Siewior <bigeasy@linutronix.de> [140905 12:03]:
> 
> It has been only tested as console UART on am335x-evm, dra7-evm and
> beagle bone. I also did some longer raw-transfers to meassure the load.

You may want to also update this with beagleboard xm now too?

Regards,

Tony 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 11/18] tty: serial: 8250_dma: Add a TX trigger workaround for AM33xx
  2014-09-05 19:02 ` [PATCH 11/18] tty: serial: 8250_dma: Add a TX trigger workaround for AM33xx Sebastian Andrzej Siewior
@ 2014-09-08 16:41   ` Tony Lindgren
  2014-09-08 16:45     ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 35+ messages in thread
From: Tony Lindgren @ 2014-09-08 16:41 UTC (permalink / raw)
  To: linux-arm-kernel

* Sebastian Andrzej Siewior <bigeasy@linutronix.de> [140905 12:28]:
> This problem has not been seen on DRA7 or beaglebone (OMAP3). I am not
> sure if this is UART-IP core specific or DMA engine.

Maybe check this comment for future reference too because
beaglebone and bealeboard do different things for PM.. Do
you mean Beaglebone or beagleboard? Or both?

Regards,

Tony

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 11/18] tty: serial: 8250_dma: Add a TX trigger workaround for AM33xx
  2014-09-08 16:41   ` Tony Lindgren
@ 2014-09-08 16:45     ` Sebastian Andrzej Siewior
  0 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-08 16:45 UTC (permalink / raw)
  To: linux-arm-kernel

On 09/08/2014 06:41 PM, Tony Lindgren wrote:
> * Sebastian Andrzej Siewior <bigeasy@linutronix.de> [140905 12:28]:
>> This problem has not been seen on DRA7 or beaglebone (OMAP3). I am not
>> sure if this is UART-IP core specific or DMA engine.
> 
> Maybe check this comment for future reference too because
> beaglebone and bealeboard do different things for PM.. Do
> you mean Beaglebone or beagleboard? Or both?

Beagle bone (am335x) has the problem.
Beagle board xm (omap3) does not have the problem.
So let me replace "beaglebone" with "beagle board xm"

> 
> Regards,
> 
> Tony
> 

Sebastian

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
                   ` (18 preceding siblings ...)
  2014-09-08 14:46 ` [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Frans Klaver
@ 2014-09-08 17:40 ` Tony Lindgren
  2014-09-08 17:55   ` Tony Lindgren
  19 siblings, 1 reply; 35+ messages in thread
From: Tony Lindgren @ 2014-09-08 17:40 UTC (permalink / raw)
  To: linux-arm-kernel

* Sebastian Andrzej Siewior <bigeasy@linutronix.de> [140905 12:03]:
> This is my complete queue fo the omap serial driver based on the 8250 core
> code. I played with it on beagle bone, am335x-evm and dra7xx including DMA.
> The runtime-pm pieces look now bug-compatible with the omap-serial driver.
> Besides the runtime-om improvement I also fixed a few corner cases for the
> TX-DMA problem. The DMA fixes (in edma and omap-dma) were dropped and the
> problem has been in 8250-dma via patch #13.
> 
> The whole queue is available at
>   git://git.breakpoint.cc/bigeasy/linux.git uart_v8

Gave the above branch a quick try, but it again does not idle for
my omap3 test cases. It seems that now the cm_idlest1_core sdma
bit is blocking deeper idle states. Is that the correct branch
to use?

Regards,

Tony

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-08 17:40 ` Tony Lindgren
@ 2014-09-08 17:55   ` Tony Lindgren
  2014-09-09  7:33     ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 35+ messages in thread
From: Tony Lindgren @ 2014-09-08 17:55 UTC (permalink / raw)
  To: linux-arm-kernel

* Tony Lindgren <tony@atomide.com> [140908 10:41]:
> * Sebastian Andrzej Siewior <bigeasy@linutronix.de> [140905 12:03]:
> > This is my complete queue fo the omap serial driver based on the 8250 core
> > code. I played with it on beagle bone, am335x-evm and dra7xx including DMA.
> > The runtime-pm pieces look now bug-compatible with the omap-serial driver.
> > Besides the runtime-om improvement I also fixed a few corner cases for the
> > TX-DMA problem. The DMA fixes (in edma and omap-dma) were dropped and the
> > problem has been in 8250-dma via patch #13.
> > 
> > The whole queue is available at
> >   git://git.breakpoint.cc/bigeasy/linux.git uart_v8
> 
> Gave the above branch a quick try, but it again does not idle for
> my omap3 test cases. It seems that now the cm_idlest1_core sdma
> bit is blocking deeper idle states. Is that the correct branch
> to use?

Never mind. User error. I forgot to apply my own patch for force
idling the UARTs for the omap hwmod. And the sdma bit clears if
I do sleep 5 before attempting to read the sysfs entry..

Regards,

Tony

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-08 16:33     ` Sebastian Andrzej Siewior
@ 2014-09-08 18:25       ` Frans Klaver
  0 siblings, 0 replies; 35+ messages in thread
From: Frans Klaver @ 2014-09-08 18:25 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Sep 08, 2014 at 06:33:13PM +0200, Sebastian Andrzej Siewior wrote:
> * Sebastian Andrzej Siewior | 2014-09-08 17:15:01 [+0200]:
> 
> >* Frans Klaver | 2014-09-08 16:46:18 [+0200]:
> >
> >>- ncurses based applications (vi, less) don't play nice for me on the
> >>  console with this series. less doesn't show me anything. vi doesn't
> >>  return to console properly.
> >
> >Can you give a test case 
> 
> Okay. less. My am335x-evm freezes after a while for no obvious reason.
> The data that hits the RX fifo is still received but the TX won't do
> anything. The DMA request is pending, the FIFO level is @64 bytes and
> the UART doesn't make any progress.
> On beagle-board I see what you described: less on a file and nothing
> happens.

Exactly that, yes.

Frans

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-08 15:15   ` Sebastian Andrzej Siewior
  2014-09-08 16:33     ` Sebastian Andrzej Siewior
@ 2014-09-08 18:33     ` Frans Klaver
  2014-09-09 19:41       ` Sebastian Andrzej Siewior
  1 sibling, 1 reply; 35+ messages in thread
From: Frans Klaver @ 2014-09-08 18:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Sep 08, 2014 at 05:15:01PM +0200, Sebastian Andrzej Siewior wrote:
> * Frans Klaver | 2014-09-08 16:46:18 [+0200]:
> 
> >- I seem seem to get stuck in a "serial8250: too much work for irq%d"
> >  loop somewhat reliably. We have a rather demanding application with
> >  typically somewhere between 600 and 1000 byte packets being sent at
> >  240Hz (roughly somewhere between 1.5 and 2 Mb/s). We run at baudrate
> >  3500k. I get into this "too much work" thing already when running at
> >  300 bytes per packet.
> 
> Do you get this message also at lower baud rates, say 115200?

I don't get this message at lower data rates. Haven't tested lower baud
rates yet.

> What I am trying to understand is why you are spinning in the handler. 
> _With_ DMA you should hardly get into the serial handler under normal 
> conditions. Running at 3.5MB/sec should give one byte every 2.8us and
> 48 Bytes every ~137us. This looks like plenty of time to get  out of
> the handler. My *guess* is that serial8250_handle_irq() has IIR often
> set to timeout and you end up fetching byte after byte. 
> 
> This patch should protocol when and why you got into the handler.
>
> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
> index 7111b22de000..59852069e4a0 100644
> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -1583,6 +1583,7 @@ int serial8250_handle_irq(struct uart_port *port, unsigned int iir)
>  	status = serial_port_in(port, UART_LSR);
>  
>  	DEBUG_INTR("status = %x...", status);
> +	trace_printk("l%d IIR %x LSR %x\n", port->line, iir, status);
>  
>  	if (status & (UART_LSR_DR | UART_LSR_BI)) {
>  		if (up->dma)
> @@ -1707,6 +1708,7 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
>  
>  	spin_unlock(&i->lock);
>  
> +	trace_printk("%d e\n", irq);
>  	DEBUG_INTR("end.\n");
>  
>  	return IRQ_RETVAL(handled);
> 

Thanks. I'll give it a spin on Wednesday.


> >I hope this is of some use to you. I'll do more testing later.
> 
> Which SoC do you use and do you have DMA enabled?

am335x, DMA is enabled, unless I need to do something extra in the
device tree. We depend on am335x.dtsi, so I would think that would be
automatic if CONFIG_SERIAL_8250_DMA=y.

Thanks,
Frans

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 03/18] tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks
  2014-09-05 19:02 ` [PATCH 03/18] tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks Sebastian Andrzej Siewior
@ 2014-09-08 23:33   ` Greg Kroah-Hartman
  2014-09-09  0:41     ` Tony Lindgren
  0 siblings, 1 reply; 35+ messages in thread
From: Greg Kroah-Hartman @ 2014-09-08 23:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Sep 05, 2014 at 09:02:38PM +0200, Sebastian Andrzej Siewior wrote:
> The OMAP UART provides support for HW assisted flow control. What is
> missing is the support to throttle / unthrottle callbacks which are used
> by the omap-serial driver at the moment.
> This patch adds the callbacks. It should be safe to add them since they
> are only invoked from the serial_core (uart_throttle()) if the feature
> flags are set.
> 
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Due to other recent changes, this patch fails to apply :(

I've applied your first two, can you respin the rest and resend as a new
series?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 03/18] tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks
  2014-09-08 23:33   ` Greg Kroah-Hartman
@ 2014-09-09  0:41     ` Tony Lindgren
  0 siblings, 0 replies; 35+ messages in thread
From: Tony Lindgren @ 2014-09-09  0:41 UTC (permalink / raw)
  To: linux-arm-kernel

* Greg Kroah-Hartman <gregkh@linuxfoundation.org> [140908 16:33]:
> On Fri, Sep 05, 2014 at 09:02:38PM +0200, Sebastian Andrzej Siewior wrote:
> > The OMAP UART provides support for HW assisted flow control. What is
> > missing is the support to throttle / unthrottle callbacks which are used
> > by the omap-serial driver at the moment.
> > This patch adds the callbacks. It should be safe to add them since they
> > are only invoked from the serial_core (uart_throttle()) if the feature
> > flags are set.
> > 
> > Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> 
> Due to other recent changes, this patch fails to apply :(
> 
> I've applied your first two, can you respin the rest and resend as a new
> series?

It seems that we've reached pretty much omap-serial compability and
the remaining bugs are either already there with omap-serial or are
SoC specific and/or DMA related. So Sebastian, please feel also free
to add this when reposting:

Reviewed-by: Tony Lindgren <tony@atomide.com>
Tested-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-08 17:55   ` Tony Lindgren
@ 2014-09-09  7:33     ` Sebastian Andrzej Siewior
  0 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-09  7:33 UTC (permalink / raw)
  To: linux-arm-kernel

On 09/08/2014 07:55 PM, Tony Lindgren wrote:
> * Tony Lindgren <tony@atomide.com> [140908 10:41]:
>> * Sebastian Andrzej Siewior <bigeasy@linutronix.de> [140905 12:03]:
>>> This is my complete queue fo the omap serial driver based on the 8250 core
>>> code. I played with it on beagle bone, am335x-evm and dra7xx including DMA.
>>> The runtime-pm pieces look now bug-compatible with the omap-serial driver.
>>> Besides the runtime-om improvement I also fixed a few corner cases for the
>>> TX-DMA problem. The DMA fixes (in edma and omap-dma) were dropped and the
>>> problem has been in 8250-dma via patch #13.
>>>
>>> The whole queue is available at
>>>   git://git.breakpoint.cc/bigeasy/linux.git uart_v8
>>
>> Gave the above branch a quick try, but it again does not idle for
>> my omap3 test cases. It seems that now the cm_idlest1_core sdma
>> bit is blocking deeper idle states. Is that the correct branch
>> to use?
> 
> Never mind. User error. I forgot to apply my own patch for force
> idling the UARTs for the omap hwmod. And the sdma bit clears if
> I do sleep 5 before attempting to read the sysfs entry..

Okay. There is also this "uart_v8_hacks" branch where I splitted out
the hacks I had like the pstore one or the force idle for instance.

> Regards,
> 
> Tony

Sebastian

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-08 18:33     ` Frans Klaver
@ 2014-09-09 19:41       ` Sebastian Andrzej Siewior
  2014-09-10 14:15         ` Frans Klaver
  0 siblings, 1 reply; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-09 19:41 UTC (permalink / raw)
  To: linux-arm-kernel

On 09/08/2014 08:33 PM, Frans Klaver wrote:
> Thanks. I'll give it a spin on Wednesday.

Could you please pull the upcoming v9 first?

 git://git.breakpoint.cc/bigeasy/linux.git uart_v9_pre1

This solves a few of my am335x related issues.

The problem that the uart freezes on beagle board xm is still there.
>From what I can say is that the DMA transfer is started but not
completed and I can't reproduce it on dra7xx

> Thanks,
> Frans
> 

Sebastian

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-09 19:41       ` Sebastian Andrzej Siewior
@ 2014-09-10 14:15         ` Frans Klaver
  2014-09-10 16:56           ` Sebastian Andrzej Siewior
  0 siblings, 1 reply; 35+ messages in thread
From: Frans Klaver @ 2014-09-10 14:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, Sep 09, 2014 at 09:41:20PM +0200, Sebastian Andrzej Siewior wrote:
> On 09/08/2014 08:33 PM, Frans Klaver wrote:
> > Thanks. I'll give it a spin on Wednesday.
> 
> Could you please pull the upcoming v9 first?
> 
>  git://git.breakpoint.cc/bigeasy/linux.git uart_v9_pre1
> 
> This solves a few of my am335x related issues.

Using v9_pre1, I get a kernel panic in edma_dma_pause() on echan->edesc
being NULL. I do get data before this happens. This is at high speed,
high rate. No mention of the irq having too much to do, though. The more
data I transmit, the more likely this is to occur.

I don't currently have the setup to lower the baudrate. I would probably
need to reproduce this on a beaglebone, instead of our custom board.
I'll see if I can do that.

If you need more info, just let me know.

Thanks,
Frans

^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA
  2014-09-10 14:15         ` Frans Klaver
@ 2014-09-10 16:56           ` Sebastian Andrzej Siewior
  0 siblings, 0 replies; 35+ messages in thread
From: Sebastian Andrzej Siewior @ 2014-09-10 16:56 UTC (permalink / raw)
  To: linux-arm-kernel

On 09/10/2014 04:15 PM, Frans Klaver wrote:
> On Tue, Sep 09, 2014 at 09:41:20PM +0200, Sebastian Andrzej Siewior wrote:
>> On 09/08/2014 08:33 PM, Frans Klaver wrote:
>>> Thanks. I'll give it a spin on Wednesday.
>>
>> Could you please pull the upcoming v9 first?
>>
>>  git://git.breakpoint.cc/bigeasy/linux.git uart_v9_pre1
>>
>> This solves a few of my am335x related issues.
> 
> Using v9_pre1, I get a kernel panic in edma_dma_pause() on echan->edesc
> being NULL. I do get data before this happens. This is at high speed,
> high rate. No mention of the irq having too much to do, though. The more
> data I transmit, the more likely this is to occur.

Hmm. This shouldn't happen because if dma->rx_running is set, then
there should be a descriptor to pause.
Could check how this could happen? (and which event tries to do so)

In the meantime, the lower part of [0] should fix the NULL bug.

[0] https://lkml.org/lkml/2014/7/29/506

> I don't currently have the setup to lower the baudrate. I would probably
> need to reproduce this on a beaglebone, instead of our custom board.
> I'll see if I can do that.
> 
> If you need more info, just let me know.

I just pushed uart_v9_pre2 and I will post it once I rebased it on top
Greg's latest.
There was a bug where THRE could be active longer than needed caused a
few not needed interrupts until the FIFO was empty again.

My "less on file" test case did not fail anymore with this.

> Thanks,
> Frans

Sebastian

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, back to index

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-05 19:02 [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 01/18] tty: serial: 8250_core: provide a function to export uart_8250_port Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 02/18] tty: serial: 8250_core: allow to overwrite & export serial8250_startup() Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 03/18] tty: serial: 8250_core: allow to set ->throttle / ->unthrottle callbacks Sebastian Andrzej Siewior
2014-09-08 23:33   ` Greg Kroah-Hartman
2014-09-09  0:41     ` Tony Lindgren
2014-09-05 19:02 ` [PATCH 04/18] tty: serial: 8250_core: add run time pm Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 05/18] tty: serial: 8250_core: read only RX if there is something in the FIFO Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 06/18] tty: serial: 8250_core: user the ->line argument as a hint in serial8250_find_match_or_unused() Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 07/18] tty: serial: 8250_core: remove UART_IER_RDI in serial8250_stop_rx() Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 08/18] tty: serial: Add 8250-core based omap driver Sebastian Andrzej Siewior
2014-09-08 16:39   ` Tony Lindgren
2014-09-05 19:02 ` [PATCH 09/18] tty: serial: 8250_dma: handle error on TX submit Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 10/18] tty: serial: 8250_dma: enqueue RX dma again on completion Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 11/18] tty: serial: 8250_dma: Add a TX trigger workaround for AM33xx Sebastian Andrzej Siewior
2014-09-08 16:41   ` Tony Lindgren
2014-09-08 16:45     ` Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 12/18] tty: serial: 8250_dma: optimize the xmit path due to UART_BUG_DMA_TX Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 13/18] tty: serial: 8250_dma: keep own book keeping about RX transfers Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 14/18] tty: serial: 8250_dma: handle the when UART response while DMA remains idle Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 15/18] tty: serial: 8250_dma: add pm runtime Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 16/18] arm: dts: am33xx: add DMA properties for UART Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 17/18] arm: dts: dra7: " Sebastian Andrzej Siewior
2014-09-05 19:02 ` [PATCH 18/18] tty: serial: 8250: omap: add dma support Sebastian Andrzej Siewior
2014-09-08 14:46 ` [PATCH v8 00/18] 8250-core based serial driver for OMAP + DMA Frans Klaver
2014-09-08 15:15   ` Sebastian Andrzej Siewior
2014-09-08 16:33     ` Sebastian Andrzej Siewior
2014-09-08 18:25       ` Frans Klaver
2014-09-08 18:33     ` Frans Klaver
2014-09-09 19:41       ` Sebastian Andrzej Siewior
2014-09-10 14:15         ` Frans Klaver
2014-09-10 16:56           ` Sebastian Andrzej Siewior
2014-09-08 17:40 ` Tony Lindgren
2014-09-08 17:55   ` Tony Lindgren
2014-09-09  7:33     ` Sebastian Andrzej Siewior

Linux-ARM-Kernel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-arm-kernel/0 linux-arm-kernel/git/0.git
	git clone --mirror https://lore.kernel.org/linux-arm-kernel/1 linux-arm-kernel/git/1.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-arm-kernel linux-arm-kernel/ https://lore.kernel.org/linux-arm-kernel \
		linux-arm-kernel@lists.infradead.org
	public-inbox-index linux-arm-kernel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.infradead.lists.linux-arm-kernel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git