Subject:

This is version three of the patch set. Unless something serious comes up
I would drop the RFC on the next post.

So far I should have everything covered up comparing to the omap-serial
driver except for the throttle callbacks. And now I would slowly start
looking into DMA support…

Sebastian


Subject: [PATCH 2/6] tty: serial: 8250 core: allow to overwrite & export serial8250_startup()

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

v1…v2: add shutdown callback

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
drivers/tty/serial/8250/8250_core.c | 22 ++++++++++++++++++++--
include/linux/serial_8250.h | 2 ++
include/linux/serial_core.h | 2 ++
3 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 6db2ee2..d37eb08 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1939,7 +1939,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 =
container_of(port, struct uart_8250_port, port);
@@ -2191,8 +2191,17 @@ 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);
+ else
+ return serial8250_do_startup(port);
+}
+
+void serial8250_do_shutdown(struct uart_port *port)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
@@ -2243,6 +2252,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)
{
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index af47a8a..0ec21ec 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -114,6 +114,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 5bbb809..6f20ff0 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.0.1

Subject: [PATCH 3/6] tty: serial: 8250 core: add runtime pm

While comparing the OMAP-serial and the 8250 part of this I noticed that
the the latter does not use runtime-pm. Here are the pieces. It is
basically a get before first register access and a last_busy + put after
last access.
If I understand this correct, it should do nothing as long as
pm_runtime_use_autosuspend() + pm_runtime_enable() isn't invoked on the
device.

Cc: [email protected]
Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
drivers/tty/serial/8250/8250_core.c | 101 +++++++++++++++++++++++++++++++-----
1 file changed, 88 insertions(+), 13 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index d37eb08..1a91a89 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -38,6 +38,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
@@ -553,10 +554,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.
*/
+ pm_runtime_get_sync(p->port.dev);
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) {
@@ -571,7 +573,17 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
serial_out(p, UART_EFR, 0);
serial_out(p, UART_LCR, 0);
}
+
+ if (!device_may_wakeup(p->port.dev)) {
+ if (sleep)
+ pm_runtime_forbid(p->port.dev);
+ else
+ pm_runtime_allow(p->port.dev);
+ }
}
+out:
+ pm_runtime_mark_last_busy(p->port.dev);
+ pm_runtime_put_autosuspend(p->port.dev);
}

#ifdef CONFIG_SERIAL_8250_RSA
@@ -1280,6 +1292,7 @@ static void serial8250_stop_tx(struct uart_port *port)
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);

+ pm_runtime_get_sync(port->dev);
__stop_tx(up);

/*
@@ -1289,6 +1302,8 @@ static void serial8250_stop_tx(struct uart_port *port)
up->acr |= UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}

static void serial8250_start_tx(struct uart_port *port)
@@ -1296,8 +1311,9 @@ static void serial8250_start_tx(struct uart_port *port)
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);

+ pm_runtime_get_sync(port->dev);
if (up->dma && !serial8250_tx_dma(up)) {
- return;
+ goto out;
} else if (!(up->ier & UART_IER_THRI)) {
up->ier |= UART_IER_THRI;
serial_port_out(port, UART_IER, up->ier);
@@ -1318,6 +1334,9 @@ static void serial8250_start_tx(struct uart_port *port)
up->acr &= ~UART_ACR_TXDIS;
serial_icr_write(up, UART_ACR, up->acr);
}
+out:
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}

static void serial8250_stop_rx(struct uart_port *port)
@@ -1325,9 +1344,14 @@ static void serial8250_stop_rx(struct uart_port *port)
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);

+ pm_runtime_get_sync(port->dev);
+
up->ier &= ~UART_IER_RLSI;
up->port.read_status_mask &= ~UART_LSR_DR;
serial_port_out(port, UART_IER, up->ier);
+
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}

static void serial8250_enable_ms(struct uart_port *port)
@@ -1340,7 +1364,10 @@ static void serial8250_enable_ms(struct uart_port *port)
return;

up->ier |= UART_IER_MSI;
+ pm_runtime_get_sync(port->dev);
serial_port_out(port, UART_IER, up->ier);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}

/*
@@ -1530,9 +1557,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);
+ unsigned int iir;
+ int ret;

- return serial8250_handle_irq(port, iir);
+ pm_runtime_get_sync(port->dev);
+
+ iir = serial_port_in(port, UART_IIR);
+ ret = serial8250_handle_irq(port, iir);
+
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+ return ret;
}

/*
@@ -1790,11 +1825,16 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
unsigned long flags;
unsigned int lsr;

+ pm_runtime_get_sync(port->dev);
+
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);

+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+
return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
}

@@ -1805,7 +1845,10 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
unsigned int status;
unsigned int ret;

+ pm_runtime_get_sync(port->dev);
status = serial8250_modem_status(up);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);

ret = 0;
if (status & UART_MSR_DCD)
@@ -1838,7 +1881,10 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)

mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;

+ pm_runtime_get_sync(port->dev);
serial_port_out(port, UART_MCR, mcr);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}

static void serial8250_break_ctl(struct uart_port *port, int break_state)
@@ -1847,6 +1893,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
container_of(port, struct uart_8250_port, port);
unsigned long flags;

+ pm_runtime_get_sync(port->dev);
spin_lock_irqsave(&port->lock, flags);
if (break_state == -1)
up->lcr |= UART_LCR_SBC;
@@ -1854,6 +1901,8 @@ 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);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
}

/*
@@ -1898,12 +1947,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);
+ unsigned char lsr;
+ int status;
+
+ pm_runtime_get_sync(port->dev);

- 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:
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
+ return status;
}


@@ -1914,6 +1974,7 @@ static void serial8250_put_poll_char(struct uart_port *port,
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);

+ pm_runtime_get_sync(up->dev);
/*
* First save the IER then disable the interrupts
*/
@@ -1935,6 +1996,9 @@ static void serial8250_put_poll_char(struct uart_port *port,
*/
wait_for_xmitr(up, BOTH_EMPTY);
serial_port_out(port, UART_IER, ier);
+ pm_runtime_mark_last_busy(up->dev);
+ pm_runtime_put_autosuspend(up->dev);
+
}

#endif /* CONFIG_CONSOLE_POLL */
@@ -1961,6 +2025,7 @@ int serial8250_do_startup(struct uart_port *port)
if (port->iotype != up->cur_iotype)
set_io_from_upio(port);

+ pm_runtime_get_sync(port->dev);
if (port->type == PORT_16C950) {
/* Wake up and initialize UART */
up->acr = 0;
@@ -1981,7 +2046,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())
@@ -2005,7 +2069,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;
}

/*
@@ -2090,7 +2155,7 @@ int serial8250_do_startup(struct uart_port *port)
} else {
retval = serial_link_irq_chain(up);
if (retval)
- return retval;
+ goto out;
}

/*
@@ -2188,8 +2253,11 @@ int serial8250_do_startup(struct uart_port *port)
outb_p(0x80, icp);
inb_p(icp);
}
-
- return 0;
+ retval = 0;
+out:
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+ return retval;
}
EXPORT_SYMBOL_GPL(serial8250_do_startup);

@@ -2207,6 +2275,7 @@ void serial8250_do_shutdown(struct uart_port *port)
container_of(port, struct uart_8250_port, port);
unsigned long flags;

+ pm_runtime_get_sync(port->dev);
/*
* Disable interrupts from this port
*/
@@ -2246,6 +2315,8 @@ void serial8250_do_shutdown(struct uart_port *port)
* the IRQ chain.
*/
serial_port_in(port, UART_RX);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);

del_timer_sync(&up->timer);
up->timer.function = serial8250_timeout;
@@ -2365,6 +2436,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.
*/
+ pm_runtime_get_sync(port->dev);
spin_lock_irqsave(&port->lock, flags);

/*
@@ -2486,6 +2558,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
}
serial8250_set_mctrl(port, port->mctrl);
spin_unlock_irqrestore(&port->lock, flags);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+
/* Don't rewrite B0 */
if (tty_termios_baud_rate(termios))
tty_termios_encode_baud_rate(termios, baud, baud);
--
2.0.1

Subject: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

This patch provides a 8250-core based UART driver for the internal OMAP
UART. The longterm goal is to provide the same functionality as the
current OMAP uart driver and hopefully DMA support which could borrowed
from the 8250-core.

It has been only tested as console UART on am335x-evm and dra7-evm.
The device name is ttyS based instead of ttyO. If a ttyO based node name
is required please ask udev for it.

Missing:
- throttle callback

v2…v3:
- wire up startup & shutdown for wakeup-irq handling.
- RS485 handling (well the core does).

v1…v2:
- added runtime PM. Could somebody could plese double check
this? I seems to be enabled and nothing explodes. However
serial_omap_get_context_loss_count() & enable_wakeup() are
NULL pointer (in the omap-serial driver). So I am not sure how
this supposed to work :)
- added omap_8250_set_termios()

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
drivers/tty/serial/8250/8250_core.c | 8 +
drivers/tty/serial/8250/8250_omap.c | 881 ++++++++++++++++++++++++++++++++++++
drivers/tty/serial/8250/Kconfig | 9 +
drivers/tty/serial/8250/Makefile | 1 +
include/uapi/linux/serial_core.h | 3 +-
5 files changed, 901 insertions(+), 1 deletion(-)
create mode 100644 drivers/tty/serial/8250/8250_omap.c

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index bf06a4c..1cbfc8c 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -263,6 +263,12 @@ static const struct serial8250_config uart_config[] = {
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
.flags = UART_CAP_FIFO | UART_CAP_AFE,
},
+ [PORT_OMAP_16750] = {
+ .name = "OMAP",
+ .fifo_size = 64,
+ .tx_loadsz = 64,
+ .flags = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
+ },
[PORT_TEGRA] = {
.name = "Tegra",
.fifo_size = 32,
@@ -1340,6 +1346,8 @@ static void serial8250_stop_rx(struct uart_port *port)
pm_runtime_get_sync(port->dev);

up->ier &= ~UART_IER_RLSI;
+ if (port->type == PORT_OMAP_16750)
+ up->ier &= ~UART_IER_RDI;
up->port.read_status_mask &= ~UART_LSR_DR;
serial_port_out(port, UART_IER, up->ier);

diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
new file mode 100644
index 0000000..b7fdbf6
--- /dev/null
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -0,0 +1,881 @@
+/*
+ * 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)
+
+/* 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)
+
+/* 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
+
+/* 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_SW_CLR 0xf0
+#define OMAP_UART_TCR_TRIG 0x0f
+
+#define OMAP_UART_WER_MOD_WKUP 0x7f
+#define OMAP_UART_TX_WAKEUP_EN (1 << 7)
+
+#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 serial8250_omap_priv {
+ int line;
+ u32 habit;
+ u32 fcr;
+ u32 mdr1;
+ u32 efr;
+ u32 quot;
+ u32 scr;
+ u32 wer;
+
+ 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;
+};
+
+static u32 uart_read(struct uart_8250_port *up, u32 reg)
+{
+ return readl(up->port.membase + (reg << up->port.regshift));
+}
+
+static void uart_write(struct uart_8250_port *up, u32 reg, u32 val)
+{
+ writel(val, 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)
+{
+ struct serial8250_omap_priv *priv = up->port.private_data;
+ u8 timeout = 255;
+
+ serial_out(up, UART_OMAP_MDR1, mdr1);
+ udelay(2);
+ serial_out(up, UART_FCR, priv->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 serial8250_omap_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;
+ }
+}
+
+/*
+ * 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 serial8250_omap_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;
+ serial_out(up, UART_IER, up->ier);
+
+ serial_out(up, UART_LCR, cval); /* reset DLAB */
+ up->lcr = cval;
+
+ /* Up to here it was mostly serial8250_do_set_termios() */
+
+ /* FCR can be changed only when the
+ * baud clock is not running
+ * DLL_REG and DLH_REG set to 0.
+ */
+ 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, 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, up->mcr | UART_MCR_TCRTLR);
+
+ priv->scr = OMAP_UART_SCR_RX_TRIG_GRANU1_MASK | OMAP_UART_SCR_TX_EMPTY;
+ /*
+ * NOTE: Setting OMAP_UART_SCR_RX_TRIG_GRANU1_MASK sets Enables the
+ * granularity of 1 for TRIGGER RX level. Along with setting RX FIFO
+ * trigger level to 1 (as noted below, 16 characters) and TLR[3:0] to
+ * zero this will result RX FIFO threshold level to 1 character.
+ * Transmit FIFO threshold is set to 32 spaces. With SCR_TX_EMPTY set,
+ * we receive an interrupt once TX FIFO (and shift) is empty as this is
+ * what The irq routine currently expects to happen.
+ */
+ priv->fcr = UART_FCR6_R_TRIGGER_16 | UART_FCR6_T_TRIGGER_24 |
+ UART_FCR_ENABLE_FIFO;
+
+ serial_out(up, UART_FCR, priv->fcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+ serial_out(up, UART_OMAP_SCR, priv->scr);
+
+ /* Reset UART_MCR_TCRTLR: this must be done with the EFR_ECB bit set */
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_out(up, UART_MCR, up->mcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, 0);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+
+ /* 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_out(up, UART_EFR, UART_EFR_ECB);
+
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_IER, 0);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+ serial_dl_write(up, priv->quot);
+
+ serial_out(up, UART_LCR, 0);
+ serial_out(up, UART_IER, up->ier);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+ serial_out(up, UART_EFR, 0);
+ serial_out(up, UART_LCR, up->lcr);
+
+ if (priv->habit & UART_ERRATA_i202_MDR1_ACCESS)
+ omap_8250_mdr1_errataset(up, priv->mdr1);
+ else
+ serial_out(up, UART_OMAP_MDR1, priv->mdr1);
+
+ /* Configure flow control */
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+
+ /* XON1/XOFF1 accessible mode B, TCRTLR=0, ECB=0 */
+ serial_out(up, UART_XON1, termios->c_cc[VSTART]);
+ serial_out(up, UART_XOFF1, termios->c_cc[VSTOP]);
+
+ /* Enable access to TCR/TLR */
+ serial_out(up, UART_EFR, UART_EFR_ECB);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_out(up, UART_MCR, up->mcr | UART_MCR_TCRTLR);
+
+ serial_out(up, UART_TI752_TCR, OMAP_UART_TCR_TRIG);
+
+ 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;
+ }
+ serial_out(up, UART_MCR, up->mcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
+ serial_out(up, UART_EFR, 0);
+ serial_out(up, UART_LCR, up->lcr);
+
+ port->ops->set_mctrl(port, port->mctrl);
+
+ 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 * up->port.fifosize) / (baud / 8);
+ 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 serial8250_omap_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);
+
+ if (!device_may_wakeup(port->dev)) {
+ if (!state)
+ pm_runtime_forbid(port->dev);
+ else
+ pm_runtime_allow(port->dev);
+ }
+
+ 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 serial8250_omap_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 serial_omap_uart_qos_work(struct work_struct *work)
+{
+ struct serial8250_omap_priv *priv;
+
+ priv = container_of(work, struct serial8250_omap_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 serial8250_omap_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);
+ }
+
+ ret = serial8250_do_startup(port);
+ if (ret)
+ goto err;
+
+ /* 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);
+ return 0;
+err:
+ 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 serial8250_omap_priv *priv = port->private_data;
+
+ 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 int serial8250_omap_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 serial8250_omap_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) {
+ dev_err(&pdev->dev, "unable to allocate private data\n");
+ 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 is
+ * enabled via EFR instead of MCR.
+ */
+ up.port.type = PORT_OMAP_16750;
+ up.port.iotype = UPIO_MEM32;
+ up.port.flags = UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | UPF_FIXED_TYPE;
+ up.port.private_data = priv;
+
+ up.port.regshift = 2;
+ up.port.fifosize = 64;
+
+ 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;
+ /*
+ * XXX
+ * throttle
+ * 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);
+ ret = serial8250_probe_rs485(&up, &pdev->dev);
+ if (ret)
+ return ret;
+ } 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, serial_omap_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 serial8250_omap_remove(struct platform_device *pdev)
+{
+ struct serial8250_omap_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 serial_omap_enable_wakeirq(struct serial8250_omap_priv *priv,
+ bool enable)
+{
+ if (!priv->wakeirq)
+ return;
+
+ if (enable)
+ enable_irq(priv->wakeirq);
+ else
+ disable_irq_nosync(priv->wakeirq);
+}
+
+static void serial_omap_enable_wakeup(struct serial8250_omap_priv *priv,
+ bool enable)
+{
+ if (enable == priv->wakeups_enabled)
+ return;
+
+ serial_omap_enable_wakeirq(priv, enable);
+ priv->wakeups_enabled = enable;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int serial_omap_prepare(struct device *dev)
+{
+ struct serial8250_omap_priv *priv = dev_get_drvdata(dev);
+
+ pr_err("%s(%d)\n", __func__, __LINE__);
+ if (!priv)
+ return 0;
+ priv->is_suspending = true;
+ return 0;
+}
+
+static void serial_omap_complete(struct device *dev)
+{
+ struct serial8250_omap_priv *priv = dev_get_drvdata(dev);
+
+ pr_err("%s(%d)\n", __func__, __LINE__);
+ if (!priv)
+ return;
+ priv->is_suspending = false;
+}
+
+static int serial_omap_suspend(struct device *dev)
+{
+ struct serial8250_omap_priv *priv = dev_get_drvdata(dev);
+
+ serial8250_suspend_port(priv->line);
+ flush_work(&priv->qos_work);
+
+ if (device_may_wakeup(dev))
+ serial_omap_enable_wakeup(priv, true);
+ else
+ serial_omap_enable_wakeup(priv, false);
+ return 0;
+}
+
+static int serial_omap_resume(struct device *dev)
+{
+ struct serial8250_omap_priv *priv = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ serial_omap_enable_wakeup(priv, false);
+
+ serial8250_resume_port(priv->line);
+ return 0;
+}
+#else
+#define serial_omap_prepare NULL
+#define serial_omap_complete NULL
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int serial_omap_lost_context(struct serial8250_omap_priv *priv)
+{
+ struct uart_8250_port *up;
+ u32 val;
+
+ up = serial8250_get_port(priv->line);
+ 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 serial_omap_runtime_suspend(struct device *dev)
+{
+ struct serial8250_omap_priv *priv = dev_get_drvdata(dev);
+
+ /*
+ * 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) {
+ struct uart_8250_port *up;
+
+ up = serial8250_get_port(priv->line);
+ if (uart_console(&up->port))
+ return -EBUSY;
+ }
+
+ serial_omap_enable_wakeup(priv, true);
+
+ priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
+ schedule_work(&priv->qos_work);
+ return 0;
+}
+
+static void serial_omap_restore_context(struct serial8250_omap_priv *priv)
+{
+ struct uart_8250_port *up;
+
+ up = serial8250_get_port(priv->line);
+
+ 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); /* Config B mode */
+ serial_out(up, UART_EFR, UART_EFR_ECB);
+ serial_out(up, UART_LCR, 0x0); /* Operational mode */
+ serial_out(up, UART_IER, 0x0);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
+
+ serial_dl_write(up, priv->quot);
+
+ serial_out(up, UART_LCR, 0x0); /* Operational mode */
+ serial_out(up, UART_IER, up->ier);
+ serial_out(up, UART_FCR, priv->fcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_A);
+ serial_out(up, UART_MCR, up->mcr);
+ serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B); /* Config B mode */
+ serial_out(up, UART_OMAP_SCR, priv->scr);
+ serial_out(up, UART_EFR, priv->efr);
+ serial_out(up, UART_LCR, up->lcr);
+ 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_OMAP_WER, priv->wer);
+}
+
+static int serial_omap_runtime_resume(struct device *dev)
+{
+ struct serial8250_omap_priv *priv = dev_get_drvdata(dev);
+ int loss_cntx;
+
+ /* In case runtime-pm tries this before we are setup */
+ if (!priv)
+ return 0;
+ serial_omap_enable_wakeup(priv, false);
+ loss_cntx = serial_omap_lost_context(priv);
+
+ if (loss_cntx)
+ serial_omap_restore_context(priv);
+
+ priv->latency = priv->calc_latency;
+ schedule_work(&priv->qos_work);
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops serial8250_omap_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume)
+ SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend,
+ serial_omap_runtime_resume, NULL)
+ .prepare = serial_omap_prepare,
+ .complete = serial_omap_complete,
+};
+
+static const struct of_device_id serial8250_omap_dt_ids[] = {
+ { .compatible = "ti,omap2-uart" },
+ { .compatible = "ti,omap3-uart" },
+ { .compatible = "ti,omap4-uart" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, serial8250_omap_dt_ids);
+
+static struct platform_driver serial8250_omap_platform_driver = {
+ .driver = {
+ .name = "serial8250-omap",
+ .pm = &serial8250_omap_dev_pm_ops,
+ .of_match_table = serial8250_omap_dt_ids,
+ .owner = THIS_MODULE,
+ },
+ .probe = serial8250_omap_probe,
+ .remove = serial8250_omap_remove,
+};
+module_platform_driver(serial8250_omap_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 349ee59..7a5073b 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 36d68d0..4bac392 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
diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
index 5820269..74f9b11 100644
--- a/include/uapi/linux/serial_core.h
+++ b/include/uapi/linux/serial_core.h
@@ -54,7 +54,8 @@
#define PORT_ALTR_16550_F32 26 /* Altera 16550 UART with 32 FIFOs */
#define PORT_ALTR_16550_F64 27 /* Altera 16550 UART with 64 FIFOs */
#define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
-#define PORT_MAX_8250 28 /* max port ID */
+#define PORT_OMAP_16750 29 /* TI's OMAP internal 16C750 compatible UART */
+#define PORT_MAX_8250 29 /* max port ID */

/*
* ARM specific type numbers. These are not currently guaranteed
--
2.0.1

Subject: [PATCH 5/6] tty: serial: 8250-core: add rs485 support

So after I stuffed the rs485 support from the omap-serial into
8250-omap, I've been looking at it and the only omap specific part was
the OMAP_UART_SCR_TX_EMPTY part. The driver has always TX_EMPTY set
because the 8250 core expects an interrupt after the TX fifo + shift
register is empty. The rs485 parts seems to wait for half fifo and then
again for the empty fifo. With this change gone it fits fine as generic
code and here it is.

It is expected that the potential rs485 user invokes
serial_omap_probe_rs485() to configure atleast RTS gpio which is a must
have property. The code has been taken from omap-serial driver and
received limited tested due to -ENODEV (the compiler said it works).

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
drivers/tty/serial/8250/8250_core.c | 171 ++++++++++++++++++++++++++++++++++++
include/linux/serial_8250.h | 6 ++
2 files changed, 177 insertions(+)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index c7c3bf7..bf06a4c 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -39,6 +39,8 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#ifdef CONFIG_SPARC
#include <linux/sunserialcore.h>
#endif
@@ -1281,10 +1283,34 @@ static void autoconfig_irq(struct uart_8250_port *up)

static inline void __stop_tx(struct uart_8250_port *p)
{
+ if (p->rs485.flags & SER_RS485_ENABLED) {
+ int ret;
+
+ ret = (p->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
+ if (gpio_get_value(p->rts_gpio) != ret) {
+ if (p->rs485.delay_rts_after_send > 0)
+ mdelay(p->rs485.delay_rts_after_send);
+ gpio_set_value(p->rts_gpio, ret);
+ }
+ }
+
if (p->ier & UART_IER_THRI) {
p->ier &= ~UART_IER_THRI;
serial_out(p, UART_IER, p->ier);
}
+
+ if ((p->rs485.flags & SER_RS485_ENABLED) &&
+ !(p->rs485.flags & SER_RS485_RX_DURING_TX)) {
+ /*
+ * Empty the RX FIFO, we are not interested in anything
+ * received during the half-duplex transmission.
+ */
+ serial_out(p, UART_FCR, p->fcr | UART_FCR_CLEAR_RCVR);
+ /* Re-enable RX interrupts */
+ p->ier |= UART_IER_RLSI | UART_IER_RDI;
+ p->port.read_status_mask |= UART_LSR_DR;
+ serial_out(p, UART_IER, p->ier);
+ }
}

static void serial8250_stop_tx(struct uart_port *port)
@@ -1330,6 +1356,20 @@ static void serial8250_start_tx(struct uart_port *port)
if (up->dma && !serial8250_tx_dma(up)) {
goto out;
} else if (!(up->ier & UART_IER_THRI)) {
+
+ if (up->rs485.flags & SER_RS485_ENABLED) {
+ int ret;
+
+ ret = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0;
+ if (gpio_get_value(up->rts_gpio) != ret) {
+ gpio_set_value(up->rts_gpio, ret);
+ if (up->rs485.delay_rts_before_send > 0)
+ mdelay(up->rs485.delay_rts_before_send);
+ }
+ if (!(up->rs485.flags & SER_RS485_RX_DURING_TX))
+ serial8250_stop_rx(port);
+ }
+
up->ier |= UART_IER_THRI;
serial_port_out(port, UART_IER, up->ier);

@@ -2556,6 +2596,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
serial_port_out(port, UART_FCR, UART_FCR_ENABLE_FIFO);
serial_port_out(port, UART_FCR, fcr); /* set fcr */
}
+ up->fcr = fcr;
serial8250_set_mctrl(port, port->mctrl);
spin_unlock_irqrestore(&port->lock, flags);
pm_runtime_mark_last_busy(port->dev);
@@ -2811,6 +2852,124 @@ serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
return 0;
}

+int serial8250_probe_rs485(struct uart_8250_port *up,
+ struct device *dev)
+{
+ struct serial_rs485 *rs485conf = &up->rs485;
+ struct device_node *np = dev->of_node;
+ u32 rs485_delay[2];
+ enum of_gpio_flags flags;
+ int ret;
+
+ rs485conf->flags = 0;
+ if (!np)
+ return 0;
+
+ /* check for tx enable gpio */
+ up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags);
+ if (up->rts_gpio == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ if (!gpio_is_valid(up->rts_gpio))
+ return 0;
+
+ ret = devm_gpio_request(dev, up->rts_gpio, "serial_rts");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(up->rts_gpio,
+ flags & SER_RS485_RTS_AFTER_SEND);
+ if (ret < 0)
+ return ret;
+
+ up->rts_gpio_valid = true;
+
+ if (of_property_read_bool(np, "rs485-rts-active-high"))
+ rs485conf->flags |= SER_RS485_RTS_ON_SEND;
+ else
+ rs485conf->flags |= SER_RS485_RTS_AFTER_SEND;
+
+ if (of_property_read_u32_array(np, "rs485-rts-delay",
+ rs485_delay, 2) == 0) {
+ rs485conf->delay_rts_before_send = rs485_delay[0];
+ rs485conf->delay_rts_after_send = rs485_delay[1];
+ }
+
+ if (of_property_read_bool(np, "rs485-rx-during-tx"))
+ rs485conf->flags |= SER_RS485_RX_DURING_TX;
+
+ if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time"))
+ rs485conf->flags |= SER_RS485_ENABLED;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(serial8250_probe_rs485);
+
+static void serial8250_config_rs485(struct uart_port *port,
+ struct serial_rs485 *rs485conf)
+{
+ struct uart_8250_port *up =
+ container_of(port, struct uart_8250_port, port);
+ unsigned long flags;
+ unsigned int mode;
+ int val;
+
+ pm_runtime_get_sync(port->dev);
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /* Disable interrupts from this port */
+ mode = up->ier;
+ up->ier = 0;
+ serial_out(up, UART_IER, 0);
+
+ /* store new config */
+ up->rs485 = *rs485conf;
+
+ /* enable / disable rts */
+ val = (up->rs485.flags & SER_RS485_ENABLED) ?
+ SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND;
+ val = (up->rs485.flags & val) ? 1 : 0;
+ gpio_set_value(up->rts_gpio, val);
+
+ /* Enable interrupts */
+ up->ier = mode;
+ serial_out(up, UART_IER, up->ier);
+
+ spin_unlock_irqrestore(&up->port.lock, flags);
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+}
+
+static int serial8250_ioctl(struct uart_port *port, unsigned int cmd,
+ unsigned long arg)
+{
+ struct serial_rs485 rs485conf;
+ struct uart_8250_port *up;
+
+ up = container_of(port, struct uart_8250_port, port);
+ switch (cmd) {
+ case TIOCSRS485:
+ if (!gpio_is_valid(up->rts_gpio))
+ return -ENODEV;
+ if (copy_from_user(&rs485conf, (void __user *) arg,
+ sizeof(rs485conf)))
+ return -EFAULT;
+
+ serial8250_config_rs485(port, &rs485conf);
+ break;
+
+ case TIOCGRS485:
+ if (!gpio_is_valid(up->rts_gpio))
+ return -ENODEV;
+ if (copy_to_user((void __user *) arg, &up->rs485,
+ sizeof(rs485conf)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
static const char *
serial8250_type(struct uart_port *port)
{
@@ -2840,6 +2999,7 @@ static struct uart_ops serial8250_pops = {
.request_port = serial8250_request_port,
.config_port = serial8250_config_port,
.verify_port = serial8250_verify_port,
+ .ioctl = serial8250_ioctl,
#ifdef CONFIG_CONSOLE_POLL
.poll_get_char = serial8250_get_poll_char,
.poll_put_char = serial8250_put_poll_char,
@@ -3375,6 +3535,17 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
uart->tx_loadsz = up->tx_loadsz;
uart->capabilities = up->capabilities;

+ /*
+ * gpio_is_valid() is nice but if this struct wasn't initialized
+ * then it is 0 which is considered as valid. With the ioctl()
+ * which can enable the rs485 routine we could abuse a gpio.
+ */
+ if (up->rts_gpio_valid) {
+ uart->rs485 = up->rs485;
+ uart->rts_gpio = up->rts_gpio;
+ } else
+ uart->rts_gpio = -EINVAL;
+
/* Take tx_loadsz from fifosize if it wasn't set separately */
if (uart->port.fifosize && !uart->tx_loadsz)
uart->tx_loadsz = uart->port.fifosize;
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index 0ec21ec..056a73f 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -78,6 +78,7 @@ struct uart_8250_port {
unsigned char acr;
unsigned char ier;
unsigned char lcr;
+ unsigned char fcr;
unsigned char mcr;
unsigned char mcr_mask; /* mask of user bits */
unsigned char mcr_force; /* mask of forced bits */
@@ -94,6 +95,9 @@ struct uart_8250_port {
unsigned char msr_saved_flags;

struct uart_8250_dma *dma;
+ struct serial_rs485 rs485;
+ int rts_gpio;
+ bool rts_gpio_valid;

/* 8250 specific callbacks */
int (*dl_read)(struct uart_8250_port *);
@@ -107,6 +111,8 @@ void serial8250_resume_port(int line);

extern int early_serial_setup(struct uart_port *port);

+extern int serial8250_probe_rs485(struct uart_8250_port *up,
+ struct device *dev);
extern int serial8250_find_port(struct uart_port *p);
extern int serial8250_find_port_for_earlycon(void);
extern unsigned int serial8250_early_in(struct uart_port *port, int offset);
--
2.0.1

Subject: [PATCH 4/6] tty: serial: 8250-core: reorder serial8250_stop_rx() & serial8250_start_tx()

With the upcomming rs485 support it is required that
serial8250_stop_rx() is called from serial8250_start_tx(). With this
reordering there is no need for a forward declaration of the former
function.

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
drivers/tty/serial/8250/8250_core.c | 30 +++++++++++++++---------------
1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 1a91a89..c7c3bf7 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -1306,6 +1306,21 @@ static void serial8250_stop_tx(struct uart_port *port)
pm_runtime_put_autosuspend(port->dev);
}

+static void serial8250_stop_rx(struct uart_port *port)
+{
+ struct uart_8250_port *up =
+ container_of(port, struct uart_8250_port, port);
+
+ pm_runtime_get_sync(port->dev);
+
+ up->ier &= ~UART_IER_RLSI;
+ up->port.read_status_mask &= ~UART_LSR_DR;
+ serial_port_out(port, UART_IER, up->ier);
+
+ pm_runtime_mark_last_busy(port->dev);
+ pm_runtime_put_autosuspend(port->dev);
+}
+
static void serial8250_start_tx(struct uart_port *port)
{
struct uart_8250_port *up =
@@ -1339,21 +1354,6 @@ static void serial8250_start_tx(struct uart_port *port)
pm_runtime_put_autosuspend(port->dev);
}

-static void serial8250_stop_rx(struct uart_port *port)
-{
- struct uart_8250_port *up =
- container_of(port, struct uart_8250_port, port);
-
- pm_runtime_get_sync(port->dev);
-
- up->ier &= ~UART_IER_RLSI;
- up->port.read_status_mask &= ~UART_LSR_DR;
- serial_port_out(port, UART_IER, up->ier);
-
- pm_runtime_mark_last_busy(port->dev);
- pm_runtime_put_autosuspend(port->dev);
-}
-
static void serial8250_enable_ms(struct uart_port *port)
{
struct uart_8250_port *up =
--
2.0.1

Subject: [PATCH 1/6] tty: serial: 8250 core: provide a function to export uart_8250_port

There is no access 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
wrapper like serial8250_unregister_port() which passes the struct
to the proper function based on the line argument.

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

Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
---
drivers/tty/serial/8250/8250.h | 2 ++
drivers/tty/serial/8250/8250_core.c | 17 +++++++++++++++++
2 files changed, 19 insertions(+)

diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 1ebf853..34c3cd1 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -110,6 +110,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 7a91c6d..6db2ee2 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -2755,6 +2755,23 @@ 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 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.0.1

Subject: [RFC v3] 8250-core based serial driver for OMAP

+Subject
On 2014-07-09 19:49:31 [+0200], Sebastian Andrzej Siewior wrote:
> This is version three of the patch set. Unless something serious comes up
> I would drop the RFC on the next post.
>
> So far I should have everything covered up comparing to the omap-serial
> driver except for the throttle callbacks. And now I would slowly start
> looking into DMA support…
>
Sebastian

2014-07-09 19:01:53

by Lennart Sorensen

[permalink] [raw]
Subject: Re: [PATCH 5/6] tty: serial: 8250-core: add rs485 support

On Wed, Jul 09, 2014 at 07:49:36PM +0200, Sebastian Andrzej Siewior wrote:
> So after I stuffed the rs485 support from the omap-serial into
> 8250-omap, I've been looking at it and the only omap specific part was
> the OMAP_UART_SCR_TX_EMPTY part. The driver has always TX_EMPTY set
> because the 8250 core expects an interrupt after the TX fifo + shift
> register is empty. The rs485 parts seems to wait for half fifo and then
> again for the empty fifo. With this change gone it fits fine as generic
> code and here it is.
>
> It is expected that the potential rs485 user invokes
> serial_omap_probe_rs485() to configure atleast RTS gpio which is a must
> have property. The code has been taken from omap-serial driver and
> received limited tested due to -ENODEV (the compiler said it works).
>
> Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
> ---
> drivers/tty/serial/8250/8250_core.c | 171 ++++++++++++++++++++++++++++++++++++
> include/linux/serial_8250.h | 6 ++
> 2 files changed, 177 insertions(+)
>
> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
> index c7c3bf7..bf06a4c 100644
> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -39,6 +39,8 @@
> #include <linux/mutex.h>
> #include <linux/slab.h>
> #include <linux/pm_runtime.h>
> +#include <linux/gpio.h>
> +#include <linux/of_gpio.h>
> #ifdef CONFIG_SPARC
> #include <linux/sunserialcore.h>
> #endif
> @@ -1281,10 +1283,34 @@ static void autoconfig_irq(struct uart_8250_port *up)
>
> static inline void __stop_tx(struct uart_8250_port *p)
> {
> + if (p->rs485.flags & SER_RS485_ENABLED) {
> + int ret;
> +
> + ret = (p->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
> + if (gpio_get_value(p->rts_gpio) != ret) {
> + if (p->rs485.delay_rts_after_send > 0)
> + mdelay(p->rs485.delay_rts_after_send);
> + gpio_set_value(p->rts_gpio, ret);

Usually the delay for RS485 is done in bit times, not msec. Not sure
how you expect this to work. Not sure doing it in software is precise
enough either. It probably should be calculated based on the current
baudrate with a bit time rather than msec in the DT data. No one wants
to have to change the DT data to change the baud rate. After all this
is very often used with modbus and the modbus rules specify turn around
times in bit times.

I hope TI puts this into the UART in future designs where it belongs
(similar to what Exar and many others already did).

--
Len Sorensen

2014-07-10 06:28:14

by Tony Lindgren

[permalink] [raw]
Subject: Re: [PATCH 3/6] tty: serial: 8250 core: add runtime pm

* Sebastian Andrzej Siewior <[email protected]> [140709 10:52]:
> While comparing the OMAP-serial and the 8250 part of this I noticed that
> the the latter does not use runtime-pm. Here are the pieces. It is
> basically a get before first register access and a last_busy + put after
> last access.
> If I understand this correct, it should do nothing as long as
> pm_runtime_use_autosuspend() + pm_runtime_enable() isn't invoked on the
> device.
...

> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -571,7 +573,17 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
> serial_out(p, UART_EFR, 0);
> serial_out(p, UART_LCR, 0);
> }
> +
> + if (!device_may_wakeup(p->port.dev)) {
> + if (sleep)
> + pm_runtime_forbid(p->port.dev);
> + else
> + pm_runtime_allow(p->port.dev);
> + }
> }
> +out:
> + pm_runtime_mark_last_busy(p->port.dev);
> + pm_runtime_put_autosuspend(p->port.dev);
> }

The device_may_wakeup logic here is wrong as I described in the
earlier thread. For runtime PM, the wake-up events should be
always enabled. So the device_may_wakeup checks should be only
done for suspend and resume.

Regards,

Tony

2014-07-10 07:09:51

by Tony Lindgren

[permalink] [raw]
Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

* Sebastian Andrzej Siewior <[email protected]> [140709 10:52]:
>
> v1…v2:
> - added runtime PM. Could somebody could plese double check
> this? I seems to be enabled and nothing explodes. However
> serial_omap_get_context_loss_count() & enable_wakeup() are
> NULL pointer (in the omap-serial driver). So I am not sure how
> this supposed to work :)
> - added omap_8250_set_termios()

You can test this pretty easily on beagleboard xm for example
using v3.16-r4:

1. Compile the kernel using omap2plus_defconfig and enable your
driver. USB EHCI needs to be disabled and OTG port should
not have a USB cable connected.

2. Boot with init=/bin/sh to keep user space timers to minimum
at least until you have verified to hit off-idle

3. Enable UART timeouts with something like this. You may need
to update it for ttyS, I just changed ttyO to ttyS here:

#!/bin/bash
uarts=$(find /sys/class/tty/ttyS*/device/power/ -type d)
for uart in $uarts; do
echo 3000 > $uart/autosuspend_delay_ms
done

uarts=$(find /sys/class/tty/ttyS*/power/ -type d)
for uart in $uarts; do
echo enabled > $uart/wakeup
echo auto > $uart/control
done

echo 1 > /sys/kernel/debug/pm_debug/enable_off_mode

4. Wait for UART to time out and verify you hit off-idle by
looking at the debugfs entry:

# cat /sys/kernel/debug/pm_debug/count
...
core_pwrdm (ON),OFF:6,RET:0,INA:0,ON:7,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
...

I just tried testing this, but did not get far on my omap3 evm:

[ 5.445953] Unhandled fault: external abort on non-linefetch (0x1028) at 0xfb020000
[ 5.453674] Internal error: : 1028 [#1] SMP ARM
[ 5.458221] Modules linked in:
[ 5.461334] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.16.0-rc4-00006-gaab2c6a #98
[ 5.469024] task: ce058b00 ti: ce05a000 task.ti: ce05a000
[ 5.474456] PC is at mem32_serial_in+0xc/0x1c
[ 5.478851] LR is at serial8250_do_startup+0xc8/0x89c
[ 5.483917] pc : [<c0346f90>] lr : [<c034ab2c>] psr: 60000113
[ 5.483917] sp : ce05bd10 ip : c0a0aba8 fp : ce275400
[ 5.495452] r10: 00000000 r9 : cda7a680 r8 : ce27568c
[ 5.500701] r7 : ce275400 r6 : 00000000 r5 : ce280408 r4 : c10b6234
[ 5.507263] r3 : fb020000 r2 : 00000002 r1 : fb020000 r0 : c10b6234
[ 5.513854] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
[ 5.521179] Control: 10c5387d Table: 80004019 DAC: 00000015
[ 5.526977] Process swapper/0 (pid: 1, stack limit = 0xce05a248)
[ 5.532989] Stack: (0xce05bd10 to 0xce05c000)
...
[ 5.734771] [<c0346f90>] (mem32_serial_in) from [<c034ab2c>] (serial8250_do_startup+0xc8/0x89c)
[ 5.743530] [<c034ab2c>] (serial8250_do_startup) from [<c03461f8>] (uart_startup.part.3+0x7c/0x1dc)
[ 5.752624] [<c03461f8>] (uart_startup.part.3) from [<c0346d68>] (uart_open+0xe4/0x124)
[ 5.760681] [<c0346d68>] (uart_open) from [<c032c19c>] (tty_open+0x130/0x58c)
[ 5.767852] [<c032c19c>] (tty_open) from [<c01216f4>] (chrdev_open+0x9c/0x174)
[ 5.775115] [<c01216f4>] (chrdev_open) from [<c011b8d4>] (do_dentry_open+0x1d0/0x310)
[ 5.783020] [<c011b8d4>] (do_dentry_open) from [<c011bd98>] (finish_open+0x34/0x4c)
[ 5.790710] [<c011bd98>] (finish_open) from [<c012a8f8>] (do_last.isra.27+0x5a4/0xb98)
[ 5.798675] [<c012a8f8>] (do_last.isra.27) from [<c012afa0>] (path_openat+0xb4/0x5e4)
[ 5.806549] [<c012afa0>] (path_openat) from [<c012b7dc>] (do_filp_open+0x2c/0x80)
[ 5.814086] [<c012b7dc>] (do_filp_open) from [<c011cd14>] (do_sys_open+0x100/0x1d0)
[ 5.821777] [<c011cd14>] (do_sys_open) from [<c07b7ce0>] (kernel_init_freeable+0x124/0x1c8)
[ 5.830200] [<c07b7ce0>] (kernel_init_freeable) from [<c054eb90>] (kernel_init+0x8/0xe4)
[ 5.838348] [<c054eb90>] (kernel_init) from [<c000e868>] (ret_from_fork+0x14/0x2c)

Sounds like the clocks are not enabled properly?

Regards,

Tony

2014-07-10 14:44:29

by Olivier Galibert

[permalink] [raw]
Subject: Re: [PATCH 1/6] tty: serial: 8250 core: provide a function to export uart_8250_port

On Wed, Jul 9, 2014 at 7:49 PM, Sebastian Andrzej Siewior
<[email protected]> wrote:
> + * The lock assumption made here is none because runtime-pm suspend/resume
> + * callbacks should not be invoked there is any operation performed on the port.

I think there's a missing "if"?

Best,

OG.

2014-07-10 14:53:17

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH 5/6] tty: serial: 8250-core: add rs485 support

> static inline void __stop_tx(struct uart_8250_port *p)
> {
> + if (p->rs485.flags & SER_RS485_ENABLED) {
> + int ret;
> +
> + ret = (p->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
> + if (gpio_get_value(p->rts_gpio) != ret) {
> + if (p->rs485.delay_rts_after_send > 0)
> + mdelay(p->rs485.delay_rts_after_send);
> + gpio_set_value(p->rts_gpio, ret);
> + }

RTS is not normally a GPIO. We should be controlling the UART RTS here,
and if the UART has a magic special case RTS wired to a GPIO then really
the hardware specific part should handle that gunge. I don't care whether
the drive does it via serial_out magic or a more explicit hook but it
doesn't belong here in core code.

Likewise the mdelay probably should be in the device specific bits or
controlled by a flag as not all hardware is so braindead.

> @@ -1330,6 +1356,20 @@ static void serial8250_start_tx(struct uart_port *port)
> if (up->dma && !serial8250_tx_dma(up)) {

Ditto

> +int serial8250_probe_rs485(struct uart_8250_port *up,
> + struct device *dev)
> +{
> + struct serial_rs485 *rs485conf = &up->rs485;
> + struct device_node *np = dev->of_node;
> + u32 rs485_delay[2];
> + enum of_gpio_flags flags;
> + int ret;
> +
> + rs485conf->flags = 0;
> + if (!np)
> + return 0;
> +
> + /* check for tx enable gpio */
> + up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags);

No of_ dependencies in core 8250.c either please. That looks a perfectly
good implementation of serial8250_of_probe_rs485 however, just belongs in
the right place.

> +static int serial8250_ioctl(struct uart_port *port, unsigned int cmd,
> + unsigned long arg)
> +{
> + struct serial_rs485 rs485conf;
> + struct uart_8250_port *up;
> +
> + up = container_of(port, struct uart_8250_port, port);
> + switch (cmd) {
> + case TIOCSRS485:
> + if (!gpio_is_valid(up->rts_gpio))
> + return -ENODEV;

GPIO assumption again needs to go


> diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
> index 0ec21ec..056a73f 100644
> --- a/include/linux/serial_8250.h
> +++ b/include/linux/serial_8250.h
> @@ -78,6 +78,7 @@ struct uart_8250_port {
> unsigned char acr;
> unsigned char ier;
> unsigned char lcr;
> + unsigned char fcr;
> unsigned char mcr;
> unsigned char mcr_mask; /* mask of user bits */
> unsigned char mcr_force; /* mask of forced bits */
> @@ -94,6 +95,9 @@ struct uart_8250_port {
> unsigned char msr_saved_flags;
>
> struct uart_8250_dma *dma;
> + struct serial_rs485 rs485;
> + int rts_gpio;
> + bool rts_gpio_valid;

Keeping the gpio here doesn't look unreasonable if one is in use.

Alan

2014-07-10 14:54:47

by Alan Cox

[permalink] [raw]
Subject: Re: [PATCH 2/6] tty: serial: 8250 core: allow to overwrite & export serial8250_startup()

On Wed, 9 Jul 2014 19:49:33 +0200
Sebastian Andrzej Siewior <[email protected]> wrote:

> The OMAP version of the 8250 can actually use 1:1 serial8250_startup().
> However it needs to be extended by a wakeup irq which should to be
> requested & enabled at ->startup() time and disabled at ->shutdown() time.
>
> v1…v2: add shutdown callback
>
> Signed-off-by: Sebastian Andrzej Siewior <[email protected]>

1,2 & 4

Acked by: Alan Cox <[email protected]>

Subject: Re: [PATCH 2/6] tty: serial: 8250 core: allow to overwrite & export serial8250_startup()

On 07/09/2014 07:49 PM, Sebastian Andrzej Siewior wrote:
> The OMAP version of the 8250 can actually use 1:1 serial8250_startup().
> However it needs to be extended by a wakeup irq which should to be
> requested & enabled at ->startup() time and disabled at ->shutdown() time.
>
> v1…v2: add shutdown callback

forgot to copy the callbacks in serial8250_register_8250_port().

Sebastian

Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

On 07/10/2014 09:09 AM, Tony Lindgren wrote:
> You can test this pretty easily on beagleboard xm for example
> using v3.16-r4:

I tried this with am335x-evm, dra7-evm and beaglebone (omap5-uevm and
am335x-evmsk didn't want to boot a kernel and omap4-blaze didn't even
want to show MLO/U-boot) with the same result.

> 1. Compile the kernel using omap2plus_defconfig and enable your
> driver. USB EHCI needs to be disabled and OTG port should
> not have a USB cable connected.

EHCI was already disabled in the config. OTG was not connected.

> 2. Boot with init=/bin/sh to keep user space timers to minimum
> at least until you have verified to hit off-idle

I had network up and configured. Was that okay? I also tried
"ifconfig eth0 down; sleep 10; ifconfig eth0 up" to see if it works.

> 3. Enable UART timeouts with something like this. You may need
> to update it for ttyS, I just changed ttyO to ttyS here:
>
> #!/bin/bash
> uarts=$(find /sys/class/tty/ttyS*/device/power/ -type d)
> for uart in $uarts; do
> echo 3000 > $uart/autosuspend_delay_ms
> done
>
> uarts=$(find /sys/class/tty/ttyS*/power/ -type d)
> for uart in $uarts; do
> echo enabled > $uart/wakeup
> echo auto > $uart/control
> done
>
> echo 1 > /sys/kernel/debug/pm_debug/enable_off_mode
>
> 4. Wait for UART to time out and verify you hit off-idle by
> looking at the debugfs entry:
>
> # cat /sys/kernel/debug/pm_debug/count
> ...
> core_pwrdm (ON),OFF:6,RET:0,INA:0,ON:7,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0

That core_pwrdm shows only up on dra7. However with both drivers (mine
and the current omap serial) the UART went down after three secs (as
expected) and didn't accept any characters while writing on the
console. If I wrote something on it via network (like echo a >
/dev/ttyO0) it came back and was working as long as I kept it busy. The
thing is that RX does not wake it up. Any idea?
Also, while it was I checked the core_pwrdm and I had ON:1 and OFF:0.
So something is not right.
Since Dra7 has some things missing I tried it on am335x with the same
behavior. Should it work here?

> ...
>
> I just tried testing this, but did not get far on my omap3 evm:
>
> [ 5.445953] Unhandled fault: external abort on non-linefetch (0x1028) at 0xfb020000
> [ 5.453674] Internal error: : 1028 [#1] SMP ARM
> [ 5.458221] Modules linked in:
> [ 5.461334] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.16.0-rc4-00006-gaab2c6a #98
> [ 5.469024] task: ce058b00 ti: ce05a000 task.ti: ce05a000
> [ 5.474456] PC is at mem32_serial_in+0xc/0x1c
> [ 5.478851] LR is at serial8250_do_startup+0xc8/0x89c
> [ 5.483917] pc : [<c0346f90>] lr : [<c034ab2c>] psr: 60000113
> [ 5.483917] sp : ce05bd10 ip : c0a0aba8 fp : ce275400
> [ 5.495452] r10: 00000000 r9 : cda7a680 r8 : ce27568c
> [ 5.500701] r7 : ce275400 r6 : 00000000 r5 : ce280408 r4 : c10b6234
> [ 5.507263] r3 : fb020000 r2 : 00000002 r1 : fb020000 r0 : c10b6234
> [ 5.513854] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
> [ 5.521179] Control: 10c5387d Table: 80004019 DAC: 00000015
> [ 5.526977] Process swapper/0 (pid: 1, stack limit = 0xce05a248)
> [ 5.532989] Stack: (0xce05bd10 to 0xce05c000)
> ...
> [ 5.734771] [<c0346f90>] (mem32_serial_in) from [<c034ab2c>] (serial8250_do_startup+0xc8/0x89c)
> [ 5.743530] [<c034ab2c>] (serial8250_do_startup) from [<c03461f8>] (uart_startup.part.3+0x7c/0x1dc)
> [ 5.752624] [<c03461f8>] (uart_startup.part.3) from [<c0346d68>] (uart_open+0xe4/0x124)
> [ 5.760681] [<c0346d68>] (uart_open) from [<c032c19c>] (tty_open+0x130/0x58c)
> [ 5.767852] [<c032c19c>] (tty_open) from [<c01216f4>] (chrdev_open+0x9c/0x174)
> [ 5.775115] [<c01216f4>] (chrdev_open) from [<c011b8d4>] (do_dentry_open+0x1d0/0x310)
> [ 5.783020] [<c011b8d4>] (do_dentry_open) from [<c011bd98>] (finish_open+0x34/0x4c)
> [ 5.790710] [<c011bd98>] (finish_open) from [<c012a8f8>] (do_last.isra.27+0x5a4/0xb98)
> [ 5.798675] [<c012a8f8>] (do_last.isra.27) from [<c012afa0>] (path_openat+0xb4/0x5e4)
> [ 5.806549] [<c012afa0>] (path_openat) from [<c012b7dc>] (do_filp_open+0x2c/0x80)
> [ 5.814086] [<c012b7dc>] (do_filp_open) from [<c011cd14>] (do_sys_open+0x100/0x1d0)
> [ 5.821777] [<c011cd14>] (do_sys_open) from [<c07b7ce0>] (kernel_init_freeable+0x124/0x1c8)
> [ 5.830200] [<c07b7ce0>] (kernel_init_freeable) from [<c054eb90>] (kernel_init+0x8/0xe4)
> [ 5.838348] [<c054eb90>] (kernel_init) from [<c000e868>] (ret_from_fork+0x14/0x2c)
>
> Sounds like the clocks are not enabled properly?

puh. So after staring a while at your backtrace I realized that
shutdown & startup callbacks are not overwritten properly. Well, thanks
for that. Anyway, even serial8250_do_startup() has
pm_runtime_get_sync() before first register access so I have no idea
where this is coming from.

>
> Regards,
>
> Tony
>

Sebastian

Subject: Re: [PATCH 5/6] tty: serial: 8250-core: add rs485 support

On 07/09/2014 09:01 PM, Lennart Sorensen wrote:
>> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
>> index c7c3bf7..bf06a4c 100644
>> --- a/drivers/tty/serial/8250/8250_core.c
>> +++ b/drivers/tty/serial/8250/8250_core.c
>> @@ -1281,10 +1283,34 @@ static void autoconfig_irq(struct uart_8250_port *up)
>>
>> static inline void __stop_tx(struct uart_8250_port *p)
>> {
>> + if (p->rs485.flags & SER_RS485_ENABLED) {
>> + int ret;
>> +
>> + ret = (p->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0;
>> + if (gpio_get_value(p->rts_gpio) != ret) {
>> + if (p->rs485.delay_rts_after_send > 0)
>> + mdelay(p->rs485.delay_rts_after_send);
>> + gpio_set_value(p->rts_gpio, ret);
>
> Usually the delay for RS485 is done in bit times, not msec. Not sure
> how you expect this to work. Not sure doing it in software is precise
> enough either. It probably should be calculated based on the current
> baudrate with a bit time rather than msec in the DT data. No one wants
> to have to change the DT data to change the baud rate. After all this
> is very often used with modbus and the modbus rules specify turn around
> times in bit times.

My understanding is that this is not baudrate related. This is the
delay that the hardware (the transceiver) to perform the change and
work.
There is the ioctl() interface which can change the delay, too. The
only required thing is the gpio.

> I hope TI puts this into the UART in future designs where it belongs
> (similar to what Exar and many others already did).
>

Sebastian

2014-07-10 16:04:05

by Carlos Hernandez

[permalink] [raw]
Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

On 07/10/2014 11:47 AM, Sebastian Andrzej Siewior wrote:
> That core_pwrdm shows only up on dra7. However with both drivers (mine
> and the current omap serial) the UART went down after three secs (as
> expected) and didn't accept any characters while writing on the
> console. If I wrote something on it via network (like echo a >
> /dev/ttyO0) it came back and was working as long as I kept it busy. The
> thing is that RX does not wake it up. Any idea?

To wakeup from UART you need:
1) Append no_console_suspend to bootargs
2) echo enabled > /sys/devices/ocp.3/4806a000.serial/tty/ttyO0/power/wakeup

2014-07-10 16:14:37

by Nishanth Menon

[permalink] [raw]
Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

On Thu, Jul 10, 2014 at 11:03 AM, Carlos Hernandez <[email protected]> wrote:
> 1) Append no_console_suspend to bootargs
Not needed. ideally with pinctrl wakeup capability, this should work.
if you do no_console_suspend, the module is not powered down.

2014-07-11 06:41:54

by Tony Lindgren

[permalink] [raw]
Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

* Sebastian Andrzej Siewior <[email protected]> [140710 08:50]:
> On 07/10/2014 09:09 AM, Tony Lindgren wrote:
> > You can test this pretty easily on beagleboard xm for example
> > using v3.16-r4:
>
> I tried this with am335x-evm, dra7-evm and beaglebone (omap5-uevm and
> am335x-evmsk didn't want to boot a kernel and omap4-blaze didn't even
> want to show MLO/U-boot) with the same result.

None of these SoCs support off-idle with mainline kernel so testing
with those is not enough :) Best to use some omap3 based device for
testing this.

So far I have verified that beagleboard xm, n900, and omap3730-evm
all hit off-idle with v3.16-rc4.

> > 1. Compile the kernel using omap2plus_defconfig and enable your
> > driver. USB EHCI needs to be disabled and OTG port should
> > not have a USB cable connected.
>
> EHCI was already disabled in the config. OTG was not connected.

OK

> > 2. Boot with init=/bin/sh to keep user space timers to minimum
> > at least until you have verified to hit off-idle
>
> I had network up and configured. Was that okay? I also tried
> "ifconfig eth0 down; sleep 10; ifconfig eth0 up" to see if it works.

That's fine for GPMC connected devices, devices with Ethernet on
EHCI won't idle properly AFAIK.

> > 3. Enable UART timeouts with something like this. You may need
> > to update it for ttyS, I just changed ttyO to ttyS here:
> >
> > #!/bin/bash
> > uarts=$(find /sys/class/tty/ttyS*/device/power/ -type d)
> > for uart in $uarts; do
> > echo 3000 > $uart/autosuspend_delay_ms
> > done
> >
> > uarts=$(find /sys/class/tty/ttyS*/power/ -type d)
> > for uart in $uarts; do
> > echo enabled > $uart/wakeup
> > echo auto > $uart/control
> > done
> >
> > echo 1 > /sys/kernel/debug/pm_debug/enable_off_mode
> >
> > 4. Wait for UART to time out and verify you hit off-idle by
> > looking at the debugfs entry:
> >
> > # cat /sys/kernel/debug/pm_debug/count
> > ...
> > core_pwrdm (ON),OFF:6,RET:0,INA:0,ON:7,RET-LOGIC-OFF:0,RET-MEMBANK1-OFF:0,RET-MEMBANK2-OFF:0
>
> That core_pwrdm shows only up on dra7. However with both drivers (mine
> and the current omap serial) the UART went down after three secs (as
> expected) and didn't accept any characters while writing on the
> console. If I wrote something on it via network (like echo a >
> /dev/ttyO0) it came back and was working as long as I kept it busy. The
> thing is that RX does not wake it up. Any idea?

If the RX pin does not wake it up, you need to configure the
pinctrl-single entry for it, and configure that pin as a wake-up
interrupt. See the interrupts-extended entry in omap3-beagle-xm.dts.

> Also, while it was I checked the core_pwrdm and I had ON:1 and OFF:0.
> So something is not right.
> Since Dra7 has some things missing I tried it on am335x with the same
> behavior. Should it work here?

Yes only omap3 currently has the pieces needed for off-idle in the
mainline kernel.

> > I just tried testing this, but did not get far on my omap3 evm:
> >
> > [ 5.445953] Unhandled fault: external abort on non-linefetch (0x1028) at 0xfb020000
> > [ 5.453674] Internal error: : 1028 [#1] SMP ARM
> > [ 5.458221] Modules linked in:
> > [ 5.461334] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.16.0-rc4-00006-gaab2c6a #98
> > [ 5.469024] task: ce058b00 ti: ce05a000 task.ti: ce05a000
> > [ 5.474456] PC is at mem32_serial_in+0xc/0x1c
> > [ 5.478851] LR is at serial8250_do_startup+0xc8/0x89c
> > [ 5.483917] pc : [<c0346f90>] lr : [<c034ab2c>] psr: 60000113
> > [ 5.483917] sp : ce05bd10 ip : c0a0aba8 fp : ce275400
> > [ 5.495452] r10: 00000000 r9 : cda7a680 r8 : ce27568c
> > [ 5.500701] r7 : ce275400 r6 : 00000000 r5 : ce280408 r4 : c10b6234
> > [ 5.507263] r3 : fb020000 r2 : 00000002 r1 : fb020000 r0 : c10b6234
> > [ 5.513854] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
> > [ 5.521179] Control: 10c5387d Table: 80004019 DAC: 00000015
> > [ 5.526977] Process swapper/0 (pid: 1, stack limit = 0xce05a248)
> > [ 5.532989] Stack: (0xce05bd10 to 0xce05c000)
> > ...
> > [ 5.734771] [<c0346f90>] (mem32_serial_in) from [<c034ab2c>] (serial8250_do_startup+0xc8/0x89c)
> > [ 5.743530] [<c034ab2c>] (serial8250_do_startup) from [<c03461f8>] (uart_startup.part.3+0x7c/0x1dc)
> > [ 5.752624] [<c03461f8>] (uart_startup.part.3) from [<c0346d68>] (uart_open+0xe4/0x124)
> > [ 5.760681] [<c0346d68>] (uart_open) from [<c032c19c>] (tty_open+0x130/0x58c)
> > [ 5.767852] [<c032c19c>] (tty_open) from [<c01216f4>] (chrdev_open+0x9c/0x174)
> > [ 5.775115] [<c01216f4>] (chrdev_open) from [<c011b8d4>] (do_dentry_open+0x1d0/0x310)
> > [ 5.783020] [<c011b8d4>] (do_dentry_open) from [<c011bd98>] (finish_open+0x34/0x4c)
> > [ 5.790710] [<c011bd98>] (finish_open) from [<c012a8f8>] (do_last.isra.27+0x5a4/0xb98)
> > [ 5.798675] [<c012a8f8>] (do_last.isra.27) from [<c012afa0>] (path_openat+0xb4/0x5e4)
> > [ 5.806549] [<c012afa0>] (path_openat) from [<c012b7dc>] (do_filp_open+0x2c/0x80)
> > [ 5.814086] [<c012b7dc>] (do_filp_open) from [<c011cd14>] (do_sys_open+0x100/0x1d0)
> > [ 5.821777] [<c011cd14>] (do_sys_open) from [<c07b7ce0>] (kernel_init_freeable+0x124/0x1c8)
> > [ 5.830200] [<c07b7ce0>] (kernel_init_freeable) from [<c054eb90>] (kernel_init+0x8/0xe4)
> > [ 5.838348] [<c054eb90>] (kernel_init) from [<c000e868>] (ret_from_fork+0x14/0x2c)
> >
> > Sounds like the clocks are not enabled properly?
>
> puh. So after staring a while at your backtrace I realized that
> shutdown & startup callbacks are not overwritten properly. Well, thanks
> for that. Anyway, even serial8250_do_startup() has
> pm_runtime_get_sync() before first register access so I have no idea
> where this is coming from.

Maybe because the console is enabled for that port?

Regards,

Tony

Subject: Re: [PATCH 1/6] tty: serial: 8250 core: provide a function to export uart_8250_port

On 07/10/2014 04:30 PM, Olivier Galibert wrote:
> On Wed, Jul 9, 2014 at 7:49 PM, Sebastian Andrzej Siewior
> <[email protected]> wrote:
>> + * The lock assumption made here is none because runtime-pm suspend/resume
>> + * callbacks should not be invoked there is any operation performed on the port.
>
> I think there's a missing "if"?

Thanks, fixed up.

> Best,
>
> OG.
>

Sebastian

Subject: Re: [PATCH 3/6] tty: serial: 8250 core: add runtime pm

On 07/10/2014 08:28 AM, Tony Lindgren wrote:
>> --- a/drivers/tty/serial/8250/8250_core.c
>> +++ b/drivers/tty/serial/8250/8250_core.c
>> @@ -571,7 +573,17 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
>> serial_out(p, UART_EFR, 0);
>> serial_out(p, UART_LCR, 0);
>> }
>> +
>> + if (!device_may_wakeup(p->port.dev)) {
>> + if (sleep)
>> + pm_runtime_forbid(p->port.dev);
>> + else
>> + pm_runtime_allow(p->port.dev);
>> + }
>> }
>> +out:
>> + pm_runtime_mark_last_busy(p->port.dev);
>> + pm_runtime_put_autosuspend(p->port.dev);
>> }
>
> The device_may_wakeup logic here is wrong as I described in the
> earlier thread. For runtime PM, the wake-up events should be
> always enabled. So the device_may_wakeup checks should be only
> done for suspend and resume.

Okay. I dropped it from here.

>
> Regards,
>
> Tony
>

Sebastian

Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

On 07/11/2014 08:41 AM, Tony Lindgren wrote:
>> I tried this with am335x-evm, dra7-evm and beaglebone (omap5-uevm and
>> am335x-evmsk didn't want to boot a kernel and omap4-blaze didn't even
>> want to show MLO/U-boot) with the same result.
>
> None of these SoCs support off-idle with mainline kernel so testing
> with those is not enough :) Best to use some omap3 based device for
> testing this.
>
> So far I have verified that beagleboard xm, n900, and omap3730-evm
> all hit off-idle with v3.16-rc4.

Unfortunately I don't have access to any of those devices.

>> I had network up and configured. Was that okay? I also tried
>> "ifconfig eth0 down; sleep 10; ifconfig eth0 up" to see if it works.
>
> That's fine for GPMC connected devices, devices with Ethernet on
> EHCI won't idle properly AFAIK.

am33xx has proper ethernet (cpsw IP core on SoC and not something
behind USB).

>> That core_pwrdm shows only up on dra7. However with both drivers (mine
>> and the current omap serial) the UART went down after three secs (as
>> expected) and didn't accept any characters while writing on the
>> console. If I wrote something on it via network (like echo a >
>> /dev/ttyO0) it came back and was working as long as I kept it busy. The
>> thing is that RX does not wake it up. Any idea?
>
> If the RX pin does not wake it up, you need to configure the
> pinctrl-single entry for it, and configure that pin as a wake-up
> interrupt. See the interrupts-extended entry in omap3-beagle-xm.dts.

This does not help. Checking the manual, there is not something like
PIN_OFF_WAKEUPENABLE for am33xx. There is just INPUT/OUTPUT, pull
up/down + enabled/disabled and the mux_mode. For the interrupt, the HW
referenced as omap3_pmx_core touches some bits in the pinmux register
which do not exists on am33xx.

So I have nothing on HW around that could test wakeup scenario.

>> Also, while it was I checked the core_pwrdm and I had ON:1 and OFF:0.
>> So something is not right.
>> Since Dra7 has some things missing I tried it on am335x with the same
>> behavior. Should it work here?
>
> Yes only omap3 currently has the pieces needed for off-idle in the
> mainline kernel.
>

Good to know.

>> puh. So after staring a while at your backtrace I realized that
>> shutdown & startup callbacks are not overwritten properly. Well, thanks
>> for that. Anyway, even serial8250_do_startup() has
>> pm_runtime_get_sync() before first register access so I have no idea
>> where this is coming from.
>
> Maybe because the console is enabled for that port?

Maybe. But I would expect to explode around the console code. Anyway I
fixed it up and prepare next batch.

>
> Regards,
>
> Tony


Sebastian

2014-07-16 12:33:39

by Tony Lindgren

[permalink] [raw]
Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

* Sebastian Andrzej Siewior <[email protected]> [140716 05:14]:
> On 07/11/2014 08:41 AM, Tony Lindgren wrote:
> >
> > If the RX pin does not wake it up, you need to configure the
> > pinctrl-single entry for it, and configure that pin as a wake-up
> > interrupt. See the interrupts-extended entry in omap3-beagle-xm.dts.
>
> This does not help. Checking the manual, there is not something like
> PIN_OFF_WAKEUPENABLE for am33xx. There is just INPUT/OUTPUT, pull
> up/down + enabled/disabled and the mux_mode. For the interrupt, the HW
> referenced as omap3_pmx_core touches some bits in the pinmux register
> which do not exists on am33xx.

Right, on am33xx there's no IO chain wake-up path. AFAIK the only way
to provide wake events on am33xx would be to mux the RX pin temporarily
to a GPIO input. And sounds like that may work only if the GPIO is
in the first GPIO bank that's always on. I don't know if the other
GPIO banks on am33xx can provide wake up events.

> So I have nothing on HW around that could test wakeup scenario.

Bummer :( I'll give it a try again for the next revision.

Regards,

Tony

2014-07-16 13:01:09

by Sekhar Nori

[permalink] [raw]
Subject: Re: [PATCH 6/6] tty: serial: Add 8250-core based omap driver

On Wednesday 16 July 2014 06:02 PM, Tony Lindgren wrote:
> * Sebastian Andrzej Siewior <[email protected]> [140716 05:14]:
>> On 07/11/2014 08:41 AM, Tony Lindgren wrote:
>>>
>>> If the RX pin does not wake it up, you need to configure the
>>> pinctrl-single entry for it, and configure that pin as a wake-up
>>> interrupt. See the interrupts-extended entry in omap3-beagle-xm.dts.
>>
>> This does not help. Checking the manual, there is not something like
>> PIN_OFF_WAKEUPENABLE for am33xx. There is just INPUT/OUTPUT, pull
>> up/down + enabled/disabled and the mux_mode. For the interrupt, the HW
>> referenced as omap3_pmx_core touches some bits in the pinmux register
>> which do not exists on am33xx.
>
> Right, on am33xx there's no IO chain wake-up path. AFAIK the only way
> to provide wake events on am33xx would be to mux the RX pin temporarily
> to a GPIO input. And sounds like that may work only if the GPIO is
> in the first GPIO bank that's always on. I don't know if the other
> GPIO banks on am33xx can provide wake up events.

No, they cannot.

Thanks,
Sekhar

2014-07-21 13:35:22

by Mika Westerberg

[permalink] [raw]
Subject: Re: [PATCH 3/6] tty: serial: 8250 core: add runtime pm

On Wed, Jul 09, 2014 at 07:49:34PM +0200, Sebastian Andrzej Siewior wrote:
> While comparing the OMAP-serial and the 8250 part of this I noticed that
> the the latter does not use runtime-pm. Here are the pieces. It is
> basically a get before first register access and a last_busy + put after
> last access.
> If I understand this correct, it should do nothing as long as
> pm_runtime_use_autosuspend() + pm_runtime_enable() isn't invoked on the
> device.
>
> Cc: [email protected]

Sorry for the delay, just came back from vacation.

Adding Heikki, who knows the 8250_dw driver much better than me.
Unfortunately he is still on vacation for next two weeks.

> Signed-off-by: Sebastian Andrzej Siewior <[email protected]>
> ---
> drivers/tty/serial/8250/8250_core.c | 101 +++++++++++++++++++++++++++++++-----
> 1 file changed, 88 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
> index d37eb08..1a91a89 100644
> --- a/drivers/tty/serial/8250/8250_core.c
> +++ b/drivers/tty/serial/8250/8250_core.c
> @@ -38,6 +38,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
> @@ -553,10 +554,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.
> */
> + pm_runtime_get_sync(p->port.dev);
> 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) {
> @@ -571,7 +573,17 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep)
> serial_out(p, UART_EFR, 0);
> serial_out(p, UART_LCR, 0);
> }
> +
> + if (!device_may_wakeup(p->port.dev)) {
> + if (sleep)
> + pm_runtime_forbid(p->port.dev);
> + else
> + pm_runtime_allow(p->port.dev);
> + }
> }
> +out:
> + pm_runtime_mark_last_busy(p->port.dev);
> + pm_runtime_put_autosuspend(p->port.dev);
> }
>
> #ifdef CONFIG_SERIAL_8250_RSA
> @@ -1280,6 +1292,7 @@ static void serial8250_stop_tx(struct uart_port *port)
> struct uart_8250_port *up =
> container_of(port, struct uart_8250_port, port);
>
> + pm_runtime_get_sync(port->dev);
> __stop_tx(up);
>
> /*
> @@ -1289,6 +1302,8 @@ static void serial8250_stop_tx(struct uart_port *port)
> up->acr |= UART_ACR_TXDIS;
> serial_icr_write(up, UART_ACR, up->acr);
> }
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> }
>
> static void serial8250_start_tx(struct uart_port *port)
> @@ -1296,8 +1311,9 @@ static void serial8250_start_tx(struct uart_port *port)
> struct uart_8250_port *up =
> container_of(port, struct uart_8250_port, port);
>
> + pm_runtime_get_sync(port->dev);
> if (up->dma && !serial8250_tx_dma(up)) {
> - return;
> + goto out;
> } else if (!(up->ier & UART_IER_THRI)) {
> up->ier |= UART_IER_THRI;
> serial_port_out(port, UART_IER, up->ier);
> @@ -1318,6 +1334,9 @@ static void serial8250_start_tx(struct uart_port *port)
> up->acr &= ~UART_ACR_TXDIS;
> serial_icr_write(up, UART_ACR, up->acr);
> }
> +out:
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> }
>
> static void serial8250_stop_rx(struct uart_port *port)
> @@ -1325,9 +1344,14 @@ static void serial8250_stop_rx(struct uart_port *port)
> struct uart_8250_port *up =
> container_of(port, struct uart_8250_port, port);
>
> + pm_runtime_get_sync(port->dev);
> +
> up->ier &= ~UART_IER_RLSI;
> up->port.read_status_mask &= ~UART_LSR_DR;
> serial_port_out(port, UART_IER, up->ier);
> +
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> }
>
> static void serial8250_enable_ms(struct uart_port *port)
> @@ -1340,7 +1364,10 @@ static void serial8250_enable_ms(struct uart_port *port)
> return;
>
> up->ier |= UART_IER_MSI;
> + pm_runtime_get_sync(port->dev);
> serial_port_out(port, UART_IER, up->ier);
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> }
>
> /*
> @@ -1530,9 +1557,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);
> + unsigned int iir;
> + int ret;
>
> - return serial8250_handle_irq(port, iir);
> + pm_runtime_get_sync(port->dev);

Is this function executed in interrupt context? Calling _sync() variant
might sleep here. At least if the RTPM of the device is backed by ACPI
methods.

> +
> + iir = serial_port_in(port, UART_IIR);
> + ret = serial8250_handle_irq(port, iir);
> +
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> + return ret;
> }
>
> /*
> @@ -1790,11 +1825,16 @@ static unsigned int serial8250_tx_empty(struct uart_port *port)
> unsigned long flags;
> unsigned int lsr;
>
> + pm_runtime_get_sync(port->dev);
> +
> 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);
>
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> +
> return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
> }
>
> @@ -1805,7 +1845,10 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port)
> unsigned int status;
> unsigned int ret;
>
> + pm_runtime_get_sync(port->dev);
> status = serial8250_modem_status(up);
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
>
> ret = 0;
> if (status & UART_MSR_DCD)
> @@ -1838,7 +1881,10 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
>
> mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;
>
> + pm_runtime_get_sync(port->dev);
> serial_port_out(port, UART_MCR, mcr);
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> }
>
> static void serial8250_break_ctl(struct uart_port *port, int break_state)
> @@ -1847,6 +1893,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state)
> container_of(port, struct uart_8250_port, port);
> unsigned long flags;
>
> + pm_runtime_get_sync(port->dev);
> spin_lock_irqsave(&port->lock, flags);
> if (break_state == -1)
> up->lcr |= UART_LCR_SBC;
> @@ -1854,6 +1901,8 @@ 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);
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> }
>
> /*
> @@ -1898,12 +1947,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);
> + unsigned char lsr;
> + int status;
> +
> + pm_runtime_get_sync(port->dev);
>
> - 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:
> + pm_runtime_mark_last_busy(up->dev);
> + pm_runtime_put_autosuspend(up->dev);
> + return status;
> }
>
>
> @@ -1914,6 +1974,7 @@ static void serial8250_put_poll_char(struct uart_port *port,
> struct uart_8250_port *up =
> container_of(port, struct uart_8250_port, port);
>
> + pm_runtime_get_sync(up->dev);
> /*
> * First save the IER then disable the interrupts
> */
> @@ -1935,6 +1996,9 @@ static void serial8250_put_poll_char(struct uart_port *port,
> */
> wait_for_xmitr(up, BOTH_EMPTY);
> serial_port_out(port, UART_IER, ier);
> + pm_runtime_mark_last_busy(up->dev);
> + pm_runtime_put_autosuspend(up->dev);
> +
> }
>
> #endif /* CONFIG_CONSOLE_POLL */
> @@ -1961,6 +2025,7 @@ int serial8250_do_startup(struct uart_port *port)
> if (port->iotype != up->cur_iotype)
> set_io_from_upio(port);
>
> + pm_runtime_get_sync(port->dev);
> if (port->type == PORT_16C950) {
> /* Wake up and initialize UART */
> up->acr = 0;
> @@ -1981,7 +2046,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())
> @@ -2005,7 +2069,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;
> }
>
> /*
> @@ -2090,7 +2155,7 @@ int serial8250_do_startup(struct uart_port *port)
> } else {
> retval = serial_link_irq_chain(up);
> if (retval)
> - return retval;
> + goto out;
> }
>
> /*
> @@ -2188,8 +2253,11 @@ int serial8250_do_startup(struct uart_port *port)
> outb_p(0x80, icp);
> inb_p(icp);
> }
> -
> - return 0;
> + retval = 0;
> +out:
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> + return retval;
> }
> EXPORT_SYMBOL_GPL(serial8250_do_startup);
>
> @@ -2207,6 +2275,7 @@ void serial8250_do_shutdown(struct uart_port *port)
> container_of(port, struct uart_8250_port, port);
> unsigned long flags;
>
> + pm_runtime_get_sync(port->dev);
> /*
> * Disable interrupts from this port
> */
> @@ -2246,6 +2315,8 @@ void serial8250_do_shutdown(struct uart_port *port)
> * the IRQ chain.
> */
> serial_port_in(port, UART_RX);
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
>
> del_timer_sync(&up->timer);
> up->timer.function = serial8250_timeout;
> @@ -2365,6 +2436,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.
> */
> + pm_runtime_get_sync(port->dev);
> spin_lock_irqsave(&port->lock, flags);
>
> /*
> @@ -2486,6 +2558,9 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
> }
> serial8250_set_mctrl(port, port->mctrl);
> spin_unlock_irqrestore(&port->lock, flags);
> + pm_runtime_mark_last_busy(port->dev);
> + pm_runtime_put_autosuspend(port->dev);
> +
> /* Don't rewrite B0 */
> if (tty_termios_baud_rate(termios))
> tty_termios_encode_baud_rate(termios, baud, baud);
> --
> 2.0.1