From patchwork Fri Sep 5 19:02:39 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Sebastian Andrzej Siewior X-Patchwork-Id: 4854191 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 67292C0338 for ; Fri, 5 Sep 2014 19:06:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 16F6D2012B for ; Fri, 5 Sep 2014 19:06:49 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 90C67201E4 for ; Fri, 5 Sep 2014 19:06:47 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XPyoN-0000QB-VV; Fri, 05 Sep 2014 19:04:44 +0000 Received: from galois.linutronix.de ([2001:470:1f0b:db:abcd:42:0:1]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XPynE-0007hb-Ev for linux-arm-kernel@lists.infradead.org; Fri, 05 Sep 2014 19:03:36 +0000 Received: from localhost ([127.0.0.1] helo=bazinga.breakpoint.cc) by Galois.linutronix.de with esmtp (Exim 4.80) (envelope-from ) id 1XPymr-0002uU-VS; Fri, 05 Sep 2014 21:03:10 +0200 From: Sebastian Andrzej Siewior To: linux-serial@vger.kernel.org Subject: [PATCH 04/18] tty: serial: 8250_core: add run time pm Date: Fri, 5 Sep 2014 21:02:39 +0200 Message-Id: <1409943773-7874-5-git-send-email-bigeasy@linutronix.de> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1409943773-7874-1-git-send-email-bigeasy@linutronix.de> References: <1409943773-7874-1-git-send-email-bigeasy@linutronix.de> MIME-Version: 1.0 X-Linutronix-Spam-Score: -1.0 X-Linutronix-Spam-Level: - X-Linutronix-Spam-Status: No , -1.0 points, 5.0 required, ALL_TRUSTED=-1, SHORTCIRCUIT=-0.0001 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140905_120332_866212_E7A7698A X-CRM114-Status: GOOD ( 25.56 ) X-Spam-Score: -1.7 (-) Cc: tony@atomide.com, Greg Kroah-Hartman , Sebastian Andrzej Siewior , linux-kernel@vger.kernel.org, balbi@ti.com, linux-omap@vger.kernel.org, mika.westerberg@linux.intel.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-3.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP While comparing the OMAP-serial and the 8250 part of this I noticed that the latter does not use run time-pm. Here are the pieces. It is basically a get before first register access and a last_busy + put after last access. This has to be enabled from userland _and_ UART_CAP_RPM is required for this. The runtime PM can usually work transparently in the background however there is one exception to this: After serial8250_tx_chars() completes there still may be unsent bytes in the FIFO (depending on CPU speed vs baud rate + flow control). Even if the TTY-buffer is empty we do not want RPM to disable the device because it won't send the remaining bytes. Instead we leave serial8250_tx_chars() with RPM enabled and wait for the FIFO empty interrupt. Once we enter serial8250_tx_chars() with an empty buffer we know that the FIFO is empty and since we are not going to send anything, we can disable the device. That xchg() is to ensure that serial8250_tx_chars() can be called multiple times and only the first invocation will actually invoke the runtime PM function. So that the last invocation of __stop_tx() will disable runtime pm. NOTE: do not enable RPM on the device unless you know what you do! If the device goes idle, it won't be woken up by incomming RX data _unless_ there is a wakeup irq configured which is usually the RX pin configure for wakeup via the reset module. The RX activity will then wake up the device from idle. However the first character is garbage and lost. The following bytes will be received once the device is up in time. On the beagle board xm (omap3) it takes approx 13ms from the first wakeup byte until the first byte that is received properly if the device was in core-off. v5…v8: - drop RPM from serial8250_set_mctrl() it will be used in restore path which already has RPM active and holds dev->power.lock v4…v5: - add a wrapper around rpm function and introduce UART_CAP_RPM to ensure RPM put is invoked after the TX FIFO is empty. v3…v4: - added runtime to the console code - removed device_may_wakeup() from serial8250_set_sleep() Cc: mika.westerberg@linux.intel.com Signed-off-by: Sebastian Andrzej Siewior --- drivers/tty/serial/8250/8250.h | 1 + drivers/tty/serial/8250/8250_core.c | 133 ++++++++++++++++++++++++++++++++---- include/linux/serial_8250.h | 1 + 3 files changed, 122 insertions(+), 13 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 85bfec58d77c..1bcb4b2141a6 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -72,6 +72,7 @@ struct serial8250_config { #define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */ #define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */ #define UART_CAP_HFIFO (1 << 14) /* UART has a "hidden" FIFO */ +#define UART_CAP_RPM (1 << 15) /* Runtime PM is active while idle */ #define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */ #define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */ diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index e2703e8d4b87..f9dd20ebab52 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef CONFIG_SPARC #include #endif @@ -539,6 +540,53 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); +static void serial8250_rpm_get(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_get_sync(p->port.dev); +} + +static void serial8250_rpm_put(struct uart_8250_port *p) +{ + if (!(p->capabilities & UART_CAP_RPM)) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} + +/* + * This two wrapper ensure, that enable_runtime_pm_tx() can be called more than + * once and disable_runtime_pm_tx() will still disable RPM because the fifo is + * empty and the HW can idle again. + */ +static void serial8250_rpm_get_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 1); + if (rpm_active) + return; + pm_runtime_get_sync(p->port.dev); +} + +static void serial8250_rpm_put_tx(struct uart_8250_port *p) +{ + unsigned char rpm_active; + + if (!(p->capabilities & UART_CAP_RPM)) + return; + + rpm_active = xchg(&p->rpm_tx_active, 0); + if (!rpm_active) + return; + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +} + /* * IER sleep support. UARTs which have EFRs need the "extended * capability" bit enabled. Note that on XR16C850s, we need to @@ -553,10 +601,11 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) * offset but the UART channel may only write to the corresponding * bit. */ + serial8250_rpm_get(p); if ((p->port.type == PORT_XR17V35X) || (p->port.type == PORT_XR17D15X)) { serial_out(p, UART_EXAR_SLEEP, sleep ? 0xff : 0); - return; + goto out; } if (p->capabilities & UART_CAP_SLEEP) { @@ -572,6 +621,8 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) serial_out(p, UART_LCR, 0); } } +out: + serial8250_rpm_put(p); } #ifdef CONFIG_SERIAL_8250_RSA @@ -1272,6 +1323,7 @@ static inline void __stop_tx(struct uart_8250_port *p) if (p->ier & UART_IER_THRI) { p->ier &= ~UART_IER_THRI; serial_out(p, UART_IER, p->ier); + serial8250_rpm_put_tx(p); } } @@ -1279,6 +1331,7 @@ static void serial8250_stop_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); __stop_tx(up); /* @@ -1288,12 +1341,14 @@ static void serial8250_stop_tx(struct uart_port *port) up->acr |= UART_ACR_TXDIS; serial_icr_write(up, UART_ACR, up->acr); } + serial8250_rpm_put(up); } static void serial8250_start_tx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get_tx(up); if (up->dma && !serial8250_tx_dma(up)) { return; } else if (!(up->ier & UART_IER_THRI)) { @@ -1332,9 +1387,13 @@ static void serial8250_stop_rx(struct uart_port *port) { struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); + up->ier &= ~UART_IER_RLSI; up->port.read_status_mask &= ~UART_LSR_DR; serial_port_out(port, UART_IER, up->ier); + + serial8250_rpm_put(up); } static void serial8250_enable_ms(struct uart_port *port) @@ -1346,7 +1405,10 @@ static void serial8250_enable_ms(struct uart_port *port) return; up->ier |= UART_IER_MSI; + + serial8250_rpm_get(up); serial_port_out(port, UART_IER, up->ier); + serial8250_rpm_put(up); } /* @@ -1468,7 +1530,12 @@ void serial8250_tx_chars(struct uart_8250_port *up) DEBUG_INTR("THRE..."); - if (uart_circ_empty(xmit)) + /* + * With RPM enabled, we have to wait once the FIFO is empty before the + * HW can go idle. So we get here once again with empty FIFO and disable + * the interrupt and RPM in __stop_tx() + */ + if (uart_circ_empty(xmit) && !(up->capabilities & UART_CAP_RPM)) __stop_tx(up); } EXPORT_SYMBOL_GPL(serial8250_tx_chars); @@ -1535,9 +1602,17 @@ EXPORT_SYMBOL_GPL(serial8250_handle_irq); static int serial8250_default_handle_irq(struct uart_port *port) { - unsigned int iir = serial_port_in(port, UART_IIR); + struct uart_8250_port *up = up_to_u8250p(port); + unsigned int iir; + int ret; + + serial8250_rpm_get(up); - return serial8250_handle_irq(port, iir); + iir = serial_port_in(port, UART_IIR); + ret = serial8250_handle_irq(port, iir); + + serial8250_rpm_put(up); + return ret; } /* @@ -1794,11 +1869,15 @@ static unsigned int serial8250_tx_empty(struct uart_port *port) unsigned long flags; unsigned int lsr; + serial8250_rpm_get(up); + spin_lock_irqsave(&port->lock, flags); lsr = serial_port_in(port, UART_LSR); up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS; spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0; } @@ -1808,7 +1887,9 @@ static unsigned int serial8250_get_mctrl(struct uart_port *port) unsigned int status; unsigned int ret; + serial8250_rpm_get(up); status = serial8250_modem_status(up); + serial8250_rpm_put(up); ret = 0; if (status & UART_MSR_DCD) @@ -1848,6 +1929,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; + serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); if (break_state == -1) up->lcr |= UART_LCR_SBC; @@ -1855,6 +1937,7 @@ static void serial8250_break_ctl(struct uart_port *port, int break_state) up->lcr &= ~UART_LCR_SBC; serial_port_out(port, UART_LCR, up->lcr); spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); } /* @@ -1899,12 +1982,23 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) static int serial8250_get_poll_char(struct uart_port *port) { - unsigned char lsr = serial_port_in(port, UART_LSR); + struct uart_8250_port *up = up_to_u8250p(port); + unsigned char lsr; + int status; + + serial8250_rpm_get(up); - if (!(lsr & UART_LSR_DR)) - return NO_POLL_CHAR; + lsr = serial_port_in(port, UART_LSR); - return serial_port_in(port, UART_RX); + if (!(lsr & UART_LSR_DR)) { + status = NO_POLL_CHAR; + goto out; + } + + status = serial_port_in(port, UART_RX); +out: + serial8250_rpm_put(up); + return status; } @@ -1914,6 +2008,7 @@ static void serial8250_put_poll_char(struct uart_port *port, unsigned int ier; struct uart_8250_port *up = up_to_u8250p(port); + serial8250_rpm_get(up); /* * First save the IER then disable the interrupts */ @@ -1935,6 +2030,7 @@ static void serial8250_put_poll_char(struct uart_port *port, */ wait_for_xmitr(up, BOTH_EMPTY); serial_port_out(port, UART_IER, ier); + serial8250_rpm_put(up); } #endif /* CONFIG_CONSOLE_POLL */ @@ -1960,6 +2056,7 @@ int serial8250_do_startup(struct uart_port *port) if (port->iotype != up->cur_iotype) set_io_from_upio(port); + serial8250_rpm_get(up); if (port->type == PORT_16C950) { /* Wake up and initialize UART */ up->acr = 0; @@ -1980,7 +2077,6 @@ int serial8250_do_startup(struct uart_port *port) */ enable_rsa(up); #endif - /* * Clear the FIFO buffers and disable them. * (they will be reenabled in set_termios()) @@ -2004,7 +2100,8 @@ int serial8250_do_startup(struct uart_port *port) (serial_port_in(port, UART_LSR) == 0xff)) { printk_ratelimited(KERN_INFO "ttyS%d: LSR safety check engaged!\n", serial_index(port)); - return -ENODEV; + retval = -ENODEV; + goto out; } /* @@ -2089,7 +2186,7 @@ int serial8250_do_startup(struct uart_port *port) } else { retval = serial_link_irq_chain(up); if (retval) - return retval; + goto out; } /* @@ -2187,8 +2284,10 @@ int serial8250_do_startup(struct uart_port *port) outb_p(0x80, icp); inb_p(icp); } - - return 0; + retval = 0; +out: + serial8250_rpm_put(up); + return retval; } EXPORT_SYMBOL_GPL(serial8250_do_startup); @@ -2204,6 +2303,7 @@ void serial8250_do_shutdown(struct uart_port *port) struct uart_8250_port *up = up_to_u8250p(port); unsigned long flags; + serial8250_rpm_get(up); /* * Disable interrupts from this port */ @@ -2243,6 +2343,7 @@ void serial8250_do_shutdown(struct uart_port *port) * the IRQ chain. */ serial_port_in(port, UART_RX); + serial8250_rpm_put(up); del_timer_sync(&up->timer); up->timer.function = serial8250_timeout; @@ -2360,6 +2461,7 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, * Ok, we're now changing the port state. Do it with * interrupts disabled. */ + serial8250_rpm_get(up); spin_lock_irqsave(&port->lock, flags); /* @@ -2481,6 +2583,8 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios, } serial8250_set_mctrl(port, port->mctrl); spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); + /* Don't rewrite B0 */ if (tty_termios_baud_rate(termios)) tty_termios_encode_baud_rate(termios, baud, baud); @@ -3054,6 +3158,8 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) touch_nmi_watchdog(); + serial8250_rpm_get(up); + if (port->sysrq || oops_in_progress) locked = spin_trylock_irqsave(&port->lock, flags); else @@ -3090,6 +3196,7 @@ serial8250_console_write(struct console *co, const char *s, unsigned int count) if (locked) spin_unlock_irqrestore(&port->lock, flags); + serial8250_rpm_put(up); } static int __init serial8250_console_setup(struct console *co, char *options) diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index c3aa0040c72e..b161eee46108 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -84,6 +84,7 @@ struct uart_8250_port { unsigned char mcr_mask; /* mask of user bits */ unsigned char mcr_force; /* mask of forced bits */ unsigned char cur_iotype; /* Running I/O type */ + unsigned char rpm_tx_active; /* * Some bits in registers are cleared on a read, so they must