From patchwork Mon Nov 15 08:41:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tony Lindgren X-Patchwork-Id: 517389 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2AED3C433F5 for ; Mon, 15 Nov 2021 08:42:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0FEC561BCF for ; Mon, 15 Nov 2021 08:42:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234749AbhKOIpP (ORCPT ); Mon, 15 Nov 2021 03:45:15 -0500 Received: from muru.com ([72.249.23.125]:56378 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230145AbhKOIpN (ORCPT ); Mon, 15 Nov 2021 03:45:13 -0500 Received: from hillo.muru.com (localhost [127.0.0.1]) by muru.com (Postfix) with ESMTP id 0958F8111; Mon, 15 Nov 2021 08:42:53 +0000 (UTC) From: Tony Lindgren To: Greg Kroah-Hartman Cc: Andy Shevchenko , Jiri Slaby , Johan Hovold , Sebastian Andrzej Siewior , Vignesh Raghavendra , linux-serial@vger.kernel.org, linux-omap@vger.kernel.org, linux-kernel@vger.kernel.org, Andy Shevchenko Subject: [PATCH 1/7] serial: core: Add support of runtime PM Date: Mon, 15 Nov 2021 10:41:57 +0200 Message-Id: <20211115084203.56478-2-tony@atomide.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115084203.56478-1-tony@atomide.com> References: <20211115084203.56478-1-tony@atomide.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Andy Shevchenko 8250 driver has wrong implementation of runtime power management, i.e. it uses an irq_safe flag. The irq_safe flag takes a permanent usage count on the parent device preventing the parent from idling. This patch prepares for making runtime power management generic by adding runtime PM calls to serial core once for all UART drivers. As we have serial drivers that do not enable runtime PM, and drivers that enable runtime PM, we add new functions for serial_pm_resume_and_get() and serial_pm_autosuspend() functions to handle errors and allow the use also for cases when runtime PM is not enabled. The other option considered was to not check for runtime PM enable errors. But some CPUs can hang when the clocks are not enabled for the device, so ignoring the errors is not a good option. Eventually with the serial port drivers updated, we should be able to just switch to using the standard runtime PM calls with no need for the wrapper functions. Note that this patch only adds runtime PM calls to the functions where we can call them for synchronous wake-up without the need for irq_safe flag. Additionally we also need asynchronous wake-up support for __uart_start(), that is added in a separate patch. Signed-off-by: Andy Shevchenko Co-developed-by: Tony Lindgren Signed-off-by: Tony Lindgren Reported-by: kernel test robot --- drivers/tty/serial/serial_core.c | 150 ++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 10 deletions(-) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -91,6 +92,27 @@ static inline struct uart_port *uart_port_check(struct uart_state *state) return state->uart_port; } +/* + * Enables runtime PM suspended serial port. If runtime PM is not + * enabled by the serial port driver, only increments the runtime PM + * usage count. May sleep. + */ +static int serial_pm_resume_and_get(struct device *dev) +{ + if (!pm_runtime_enabled(dev)) { + pm_runtime_get_noresume(dev); + return 0; + } + + return pm_runtime_resume_and_get(dev); +} + +static void serial_pm_autosuspend(struct device *dev) +{ + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} + /* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. @@ -143,13 +165,18 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) { unsigned long flags; unsigned int old; + int err; + err = serial_pm_resume_and_get(port->dev); + if (err < 0) + return; spin_lock_irqsave(&port->lock, flags); old = port->mctrl; port->mctrl = (old & ~clear) | set; if (old != port->mctrl) port->ops->set_mctrl(port, port->mctrl); spin_unlock_irqrestore(&port->lock, flags); + serial_pm_autosuspend(port->dev); } #define uart_set_mctrl(port, set) uart_update_mctrl(port, set, 0) @@ -218,7 +245,12 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, free_page(page); } + retval = serial_pm_resume_and_get(uport->dev); + if (retval < 0) + goto out; retval = uport->ops->startup(uport); + serial_pm_autosuspend(uport->dev); + if (retval == 0) { if (uart_console(uport) && uport->cons->cflag) { tty->termios.c_cflag = uport->cons->cflag; @@ -244,7 +276,7 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, */ if (retval && capable(CAP_SYS_ADMIN)) return 1; - +out: return retval; } @@ -481,6 +513,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct uart_port *uport = uart_port_check(state); struct ktermios *termios; int hw_stopped; + int err; /* * If we have no tty, termios, or the port does not exist, @@ -490,6 +523,11 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, return; termios = &tty->termios; + + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + return; + uport->ops->set_termios(uport, termios, old_termios); /* @@ -518,6 +556,7 @@ static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, __uart_start(tty); } spin_unlock_irq(&uport->lock); + serial_pm_autosuspend(uport->dev); } static int uart_put_char(struct tty_struct *tty, unsigned char c) @@ -1015,7 +1054,14 @@ static int uart_get_lsr_info(struct tty_struct *tty, struct uart_port *uport = uart_port_check(state); unsigned int result; + result = serial_pm_resume_and_get(uport->dev); + if (result < 0) { + result = TIOCSER_TEMT; + goto out; + } + result = uport->ops->tx_empty(uport); + serial_pm_autosuspend(uport->dev); /* * If we're about to load something into the transmit @@ -1027,7 +1073,7 @@ static int uart_get_lsr_info(struct tty_struct *tty, ((uart_circ_chars_pending(&state->xmit) > 0) && !uart_tx_stopped(uport))) result &= ~TIOCSER_TEMT; - +out: return put_user(result, value); } @@ -1045,9 +1091,14 @@ static int uart_tiocmget(struct tty_struct *tty) if (!tty_io_error(tty)) { result = uport->mctrl; + + result = serial_pm_resume_and_get(uport->dev); + if (result < 0) + goto out; spin_lock_irq(&uport->lock); result |= uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); + serial_pm_autosuspend(uport->dev); } out: mutex_unlock(&port->mutex); @@ -1088,8 +1139,12 @@ static int uart_break_ctl(struct tty_struct *tty, int break_state) if (!uport) goto out; + ret = serial_pm_resume_and_get(uport->dev); + if (ret < 0) + goto out; if (uport->type != PORT_UNKNOWN && uport->ops->break_ctl) uport->ops->break_ctl(uport, break_state); + serial_pm_autosuspend(uport->dev); ret = 0; out: mutex_unlock(&port->mutex); @@ -1138,7 +1193,11 @@ static int uart_do_autoconfig(struct tty_struct *tty, struct uart_state *state) * This will claim the ports resources if * a port is found. */ + ret = serial_pm_resume_and_get(uport->dev); + if (ret < 0) + goto out; uport->ops->config_port(uport, flags); + serial_pm_autosuspend(uport->dev); ret = uart_startup(tty, state, 1); if (ret == 0) @@ -1443,14 +1502,21 @@ static void uart_set_ldisc(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *uport; struct tty_port *port = &state->port; + int err; if (!tty_port_initialized(port)) return; mutex_lock(&state->port.mutex); uport = uart_port_check(state); - if (uport && uport->ops->set_ldisc) + if (uport && uport->ops->set_ldisc) { + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + goto out; uport->ops->set_ldisc(uport, &tty->termios); + serial_pm_autosuspend(uport->dev); + } +out: mutex_unlock(&state->port.mutex); } @@ -1542,6 +1608,7 @@ static void uart_tty_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = uart_port_check(state); + int err; /* * At this point, we stop accepting input. To do this, we @@ -1550,9 +1617,13 @@ static void uart_tty_port_shutdown(struct tty_port *port) if (WARN(!uport, "detached port still initialized!\n")) return; + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + return; spin_lock_irq(&uport->lock); uport->ops->stop_rx(uport); spin_unlock_irq(&uport->lock); + serial_pm_autosuspend(uport->dev); uart_port_shutdown(port); @@ -1668,6 +1739,7 @@ static void uart_port_shutdown(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = uart_port_check(state); + int err; /* * clear delta_msr_wait queue to avoid mem leaks: we may free @@ -1681,8 +1753,13 @@ static void uart_port_shutdown(struct tty_port *port) /* * Free the IRQ and disable the port. */ - if (uport) + if (uport) { + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + return; uport->ops->shutdown(uport); + serial_pm_autosuspend(uport->dev); + } /* * Ensure that the IRQ handler isn't running on another CPU. @@ -1803,7 +1880,7 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) struct uart_port *uport; char stat_buf[32]; unsigned int status; - int mmio; + int mmio, err; mutex_lock(&port->mutex); uport = uart_port_check(state); @@ -1824,12 +1901,16 @@ static void uart_line_info(struct seq_file *m, struct uart_driver *drv, int i) } if (capable(CAP_SYS_ADMIN)) { + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + goto out; pm_state = state->pm_state; if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, UART_PM_STATE_ON); spin_lock_irq(&uport->lock); status = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); + serial_pm_autosuspend(uport->dev); if (pm_state != UART_PM_STATE_ON) uart_change_pm(state, pm_state); @@ -2050,6 +2131,7 @@ uart_set_options(struct uart_port *port, struct console *co, { struct ktermios termios; static struct ktermios dummy; + int ret; /* * Ensure that the serial-console lock is initialised early. @@ -2089,7 +2171,17 @@ uart_set_options(struct uart_port *port, struct console *co, */ port->mctrl |= TIOCM_DTR; - port->ops->set_termios(port, &termios, &dummy); + /* At early stage device is not created yet, we can't do PM */ + if (port->dev) { + ret = serial_pm_resume_and_get(port->dev); + if (ret < 0) + return ret; + port->ops->set_termios(port, &termios, &dummy); + serial_pm_autosuspend(port->dev); + } else { + port->ops->set_termios(port, &termios, &dummy); + } + /* * Allow the setting of the UART parameters with a NULL console * too: @@ -2143,6 +2235,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) struct tty_port *port = &state->port; struct device *tty_dev; struct uart_match match = {uport, drv}; + int err; mutex_lock(&port->mutex); @@ -2168,11 +2261,15 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) tty_port_set_suspended(port, 1); tty_port_set_initialized(port, 0); + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + goto unlock; spin_lock_irq(&uport->lock); ops->stop_tx(uport); ops->set_mctrl(uport, 0); ops->stop_rx(uport); spin_unlock_irq(&uport->lock); + serial_pm_autosuspend(uport->dev); /* * Wait for the transmitter to empty. @@ -2183,7 +2280,11 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport) dev_err(uport->dev, "%s: Unable to drain transmitter\n", uport->name); + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + goto unlock; ops->shutdown(uport); + serial_pm_autosuspend(uport->dev); } /* @@ -2206,6 +2307,7 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) struct device *tty_dev; struct uart_match match = {uport, drv}; struct ktermios termios; + int ret; mutex_lock(&port->mutex); @@ -2236,33 +2338,50 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) if (port->tty && termios.c_cflag == 0) termios = port->tty->termios; + ret = serial_pm_resume_and_get(uport->dev); + if (ret < 0) + goto unlock; if (console_suspend_enabled) uart_change_pm(state, UART_PM_STATE_ON); uport->ops->set_termios(uport, &termios, NULL); + serial_pm_autosuspend(uport->dev); + if (console_suspend_enabled) console_start(uport->cons); } if (tty_port_suspended(port)) { const struct uart_ops *ops = uport->ops; - int ret; + ret = serial_pm_resume_and_get(uport->dev); + if (ret < 0) + goto unlock; uart_change_pm(state, UART_PM_STATE_ON); spin_lock_irq(&uport->lock); ops->set_mctrl(uport, 0); spin_unlock_irq(&uport->lock); + serial_pm_autosuspend(uport->dev); + if (console_suspend_enabled || !uart_console(uport)) { /* Protected by port mutex for now */ struct tty_struct *tty = port->tty; + ret = serial_pm_resume_and_get(uport->dev); + if (ret < 0) + goto unlock; ret = ops->startup(uport); + serial_pm_autosuspend(uport->dev); if (ret == 0) { if (tty) uart_change_speed(tty, state, NULL); + ret = serial_pm_resume_and_get(uport->dev); + if (ret < 0) + goto unlock; spin_lock_irq(&uport->lock); ops->set_mctrl(uport, uport->mctrl); ops->start_tx(uport); spin_unlock_irq(&uport->lock); + serial_pm_autosuspend(uport->dev); tty_port_set_initialized(port, 1); } else { /* @@ -2276,10 +2395,10 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) tty_port_set_suspended(port, 0); } - +unlock: mutex_unlock(&port->mutex); - return 0; + return ret; } static inline void @@ -2329,6 +2448,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, struct uart_port *port) { unsigned int flags; + int err; /* * If there isn't a port here, don't do anything further. @@ -2356,6 +2476,10 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, uart_report_port(drv, port); + err = serial_pm_resume_and_get(port->dev); + if (err < 0) + return; + /* Power up port for set_mctrl() */ uart_change_pm(state, UART_PM_STATE_ON); @@ -2367,6 +2491,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state, spin_lock_irqsave(&port->lock, flags); port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR); spin_unlock_irqrestore(&port->lock, flags); + serial_pm_autosuspend(port->dev); /* * If this driver supports console, and it hasn't been @@ -3084,11 +3209,16 @@ EXPORT_SYMBOL_GPL(uart_handle_dcd_change); */ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) { + int err; + lockdep_assert_held_once(&uport->lock); uport->icount.cts++; if (uart_softcts_mode(uport)) { + err = serial_pm_resume_and_get(uport->dev); + if (err < 0) + return; if (uport->hw_stopped) { if (status) { uport->hw_stopped = 0; @@ -3101,7 +3231,7 @@ void uart_handle_cts_change(struct uart_port *uport, unsigned int status) uport->ops->stop_tx(uport); } } - + serial_pm_autosuspend(uport->dev); } } EXPORT_SYMBOL_GPL(uart_handle_cts_change); From patchwork Mon Nov 15 08:41:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tony Lindgren X-Patchwork-Id: 518541 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3A65CC433EF for ; Mon, 15 Nov 2021 08:42:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 23E2161BF5 for ; Mon, 15 Nov 2021 08:42:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230237AbhKOIp0 (ORCPT ); Mon, 15 Nov 2021 03:45:26 -0500 Received: from muru.com ([72.249.23.125]:56390 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235183AbhKOIpQ (ORCPT ); Mon, 15 Nov 2021 03:45:16 -0500 Received: from hillo.muru.com (localhost [127.0.0.1]) by muru.com (Postfix) with ESMTP id 82B118139; Mon, 15 Nov 2021 08:42:56 +0000 (UTC) From: Tony Lindgren To: Greg Kroah-Hartman Cc: Andy Shevchenko , Jiri Slaby , Johan Hovold , Sebastian Andrzej Siewior , Vignesh Raghavendra , linux-serial@vger.kernel.org, linux-omap@vger.kernel.org, linux-kernel@vger.kernel.org, Andy Shevchenko Subject: [PATCH 2/7] serial: core: Add wakeup() and start_pending_tx() for asynchronous wake Date: Mon, 15 Nov 2021 10:41:58 +0200 Message-Id: <20211115084203.56478-3-tony@atomide.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115084203.56478-1-tony@atomide.com> References: <20211115084203.56478-1-tony@atomide.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org If the serial driver implements PM runtime with autosuspend, the port may be powered down on TX. To wake up the port, let's add new wakeup() call for serial drivers to implement as needed. We can call wakeup() from __uart_start() before attempting to write to the serial port registers. Let's keep track of the serial port with a new runtime_suspended flag that the device driver runtime PM suspend and resume can manage with port->lock held. This is because only the device driver knows what the device runtime PM state as in Documentation/power/runtime_pm.rst under "9. Autosuspend, or automatically-delayed suspend" for locking. To allow the serial port drivers to send out pending tx on runtime PM resume, let's add start_pending_tx() as suggested by Johan Hovold . Suggested-by: Johan Hovold Signed-off-by: Tony Lindgren --- Documentation/driver-api/serial/driver.rst | 9 ++++ drivers/tty/serial/serial_core.c | 49 +++++++++++++++++++++- include/linux/serial_core.h | 3 ++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-api/serial/driver.rst b/Documentation/driver-api/serial/driver.rst --- a/Documentation/driver-api/serial/driver.rst +++ b/Documentation/driver-api/serial/driver.rst @@ -234,6 +234,15 @@ hardware. Interrupts: caller dependent. + wakeup(port) + Wake up port if it has been runtime PM suspended. + + Locking: port->lock taken. + + Interrupts: locally disabled. + + This call must not sleep + flush_buffer(port) Flush any write buffers, reset any DMA state and stop any ongoing DMA transfers. diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -107,12 +107,35 @@ static int serial_pm_resume_and_get(struct device *dev) return pm_runtime_resume_and_get(dev); } +/* Only increments the runtime PM usage count */ +static void serial_pm_get_noresume(struct device *dev) +{ + pm_runtime_get_noresume(dev); +} + static void serial_pm_autosuspend(struct device *dev) { pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); } +/* + * This routine can be used before register access to wake up a serial + * port that has been runtime PM suspended by the serial port driver. + * Note that the runtime_suspended flag is managed by the serial port + * device driver runtime PM. + */ +static int __uart_port_wakeup(struct uart_port *port) +{ + if (!port->runtime_suspended) + return 0; + + if (port->ops->wakeup) + return port->ops->wakeup(port); + + return 0; +} + /* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. @@ -145,8 +168,15 @@ static void __uart_start(struct tty_struct *tty) struct uart_state *state = tty->driver_data; struct uart_port *port = state->uart_port; - if (port && !uart_tx_stopped(port)) - port->ops->start_tx(port); + if (!port || uart_tx_stopped(port)) + return; + + if (__uart_port_wakeup(port) < 0) + return; + + serial_pm_get_noresume(port->dev); + port->ops->start_tx(port); + serial_pm_autosuspend(port->dev); } static void uart_start(struct tty_struct *tty) @@ -160,6 +190,21 @@ static void uart_start(struct tty_struct *tty) uart_port_unlock(port, flags); } +/* + * This routine can be called from the serial driver runtime PM resume function + * to transmit buffered data if the serial port was not active on uart_write(). + */ +void uart_start_pending_tx(struct uart_port *port) +{ + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (!uart_tx_stopped(port) && uart_circ_chars_pending(&port->state->xmit)) + port->ops->start_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} +EXPORT_SYMBOL(uart_start_pending_tx); + static void uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear) { diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -40,6 +40,7 @@ struct uart_ops { void (*set_mctrl)(struct uart_port *, unsigned int mctrl); unsigned int (*get_mctrl)(struct uart_port *); void (*stop_tx)(struct uart_port *); + int (*wakeup)(struct uart_port *); void (*start_tx)(struct uart_port *); void (*throttle)(struct uart_port *); void (*unthrottle)(struct uart_port *); @@ -250,6 +251,7 @@ struct uart_port { unsigned char suspended; unsigned char console_reinit; const char *name; /* port name */ + unsigned int runtime_suspended:1; /* port runtime state set by port driver */ struct attribute_group *attr_group; /* port specific attributes */ const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ struct serial_rs485 rs485; @@ -414,6 +416,7 @@ bool uart_match_port(const struct uart_port *port1, /* * Power Management */ +void uart_start_pending_tx(struct uart_port *port); int uart_suspend_port(struct uart_driver *reg, struct uart_port *port); int uart_resume_port(struct uart_driver *reg, struct uart_port *port); From patchwork Mon Nov 15 08:41:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tony Lindgren X-Patchwork-Id: 518540 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 62A39C433EF for ; Mon, 15 Nov 2021 08:42:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4D25361BCF for ; Mon, 15 Nov 2021 08:42:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230165AbhKOIpa (ORCPT ); Mon, 15 Nov 2021 03:45:30 -0500 Received: from muru.com ([72.249.23.125]:56406 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S235130AbhKOIpS (ORCPT ); Mon, 15 Nov 2021 03:45:18 -0500 Received: from hillo.muru.com (localhost [127.0.0.1]) by muru.com (Postfix) with ESMTP id D6236813A; Mon, 15 Nov 2021 08:42:58 +0000 (UTC) From: Tony Lindgren To: Greg Kroah-Hartman Cc: Andy Shevchenko , Jiri Slaby , Johan Hovold , Sebastian Andrzej Siewior , Vignesh Raghavendra , linux-serial@vger.kernel.org, linux-omap@vger.kernel.org, linux-kernel@vger.kernel.org, Andy Shevchenko Subject: [PATCH 3/7] serial: 8250_port: properly handle runtime PM in IRQ Date: Mon, 15 Nov 2021 10:41:59 +0200 Message-Id: <20211115084203.56478-4-tony@atomide.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115084203.56478-1-tony@atomide.com> References: <20211115084203.56478-1-tony@atomide.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Andy Shevchenko We can't and basically don't need to call runtime PM in IRQ handler. If IRQ is ours, device must be powered on. Otherwise check if the device is powered off and return immediately. Signed-off-by: Andy Shevchenko [tony@atomide.com: use port->runtime_suspended] Signed-off-by: Tony Lindgren --- drivers/tty/serial/8250/8250_port.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -1939,17 +1939,19 @@ EXPORT_SYMBOL_GPL(serial8250_handle_irq); static int serial8250_default_handle_irq(struct uart_port *port) { - struct uart_8250_port *up = up_to_u8250p(port); unsigned int iir; - int ret; - serial8250_rpm_get(up); + /* + * The IRQ might be shared with other peripherals so we must first + * check that are we RPM suspended or not. If we are we assume that + * the IRQ was not for us (we shouldn't be RPM suspended when the + * interrupt is enabled). + */ + if (port->runtime_suspended) + return 0; iir = serial_port_in(port, UART_IIR); - ret = serial8250_handle_irq(port, iir); - - serial8250_rpm_put(up); - return ret; + return serial8250_handle_irq(port, iir); } /* From patchwork Mon Nov 15 08:42:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tony Lindgren X-Patchwork-Id: 518539 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D1485C433EF for ; Mon, 15 Nov 2021 08:42:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C07D861C48 for ; Mon, 15 Nov 2021 08:42:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236062AbhKOIpd (ORCPT ); Mon, 15 Nov 2021 03:45:33 -0500 Received: from muru.com ([72.249.23.125]:56420 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236127AbhKOIpU (ORCPT ); Mon, 15 Nov 2021 03:45:20 -0500 Received: from hillo.muru.com (localhost [127.0.0.1]) by muru.com (Postfix) with ESMTP id 044828140; Mon, 15 Nov 2021 08:43:00 +0000 (UTC) From: Tony Lindgren To: Greg Kroah-Hartman Cc: Andy Shevchenko , Jiri Slaby , Johan Hovold , Sebastian Andrzej Siewior , Vignesh Raghavendra , linux-serial@vger.kernel.org, linux-omap@vger.kernel.org, linux-kernel@vger.kernel.org, Andy Shevchenko Subject: [PATCH 4/7] serial: 8250: Implement wakeup for TX and use it for 8250_omap Date: Mon, 15 Nov 2021 10:42:00 +0200 Message-Id: <20211115084203.56478-5-tony@atomide.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115084203.56478-1-tony@atomide.com> References: <20211115084203.56478-1-tony@atomide.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org We can use the wakeup() and uart_start_pending_tx() calls to wake up an idle serial port and send out the pending TX buffer on runtime PM resume. This allows us to remove the dependency to pm_runtime_irq_safe() for 8250_omap driver in the following patches. We manage the port runtime_suspended flag in the serial port driver as only the driver knows when the hardware is runtime PM suspended. Note that The current flag for rpm_tx_active cannot be used as it is TX specific for 8250_port. We already have serial8250_start_tx() call serial8250_rpm_get_tx(), and serial8250_stop_tx() call serial8250_rpm_put_tx() to take care of the runtime PM usage count for TX. To have the serial port driver call uart_start_pending_tx() on runtime resume, we must now use just pm_runtime_get() for serial8250_start_tx() instead of the sync version. With these changes we must now also flip the 8250_omap driver over to call uart_start_pending_tx(). That's currently the only user of UART_CAP_RPM. Signed-off-by: Tony Lindgren --- drivers/tty/serial/8250/8250_omap.c | 19 ++++++++++++++ drivers/tty/serial/8250/8250_port.c | 39 ++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1593,12 +1593,16 @@ static int omap8250_runtime_suspend(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up; + struct uart_port *port; + unsigned long flags; /* In case runtime-pm tries this before we are setup */ if (!priv) return 0; up = serial8250_get_port(priv->line); + port = &up->port; + /* * When using 'no_console_suspend', the console UART must not be * suspended. Since driver suspend is managed by runtime suspend, @@ -1610,6 +1614,10 @@ static int omap8250_runtime_suspend(struct device *dev) return -EBUSY; } + spin_lock_irqsave(&port->lock, flags); + port->runtime_suspended = 1; + spin_unlock_irqrestore(&port->lock, flags); + if (priv->habit & UART_ERRATA_CLOCK_DISABLE) { int ret; @@ -1636,13 +1644,18 @@ static int omap8250_runtime_resume(struct device *dev) { struct omap8250_priv *priv = dev_get_drvdata(dev); struct uart_8250_port *up; + struct uart_port *port; + unsigned long flags; /* In case runtime-pm tries this before we are setup */ if (!priv) return 0; up = serial8250_get_port(priv->line); + port = &up->port; + /* Restore state with interrupts disabled */ + spin_lock_irqsave(&port->lock, flags); if (omap8250_lost_context(up)) omap8250_restore_regs(up); @@ -1651,6 +1664,12 @@ static int omap8250_runtime_resume(struct device *dev) priv->latency = priv->calc_latency; schedule_work(&priv->qos_work); + + port->runtime_suspended = 0; + spin_unlock_irqrestore(&port->lock, flags); + + uart_start_pending_tx(port); + return 0; } #endif diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -724,7 +724,7 @@ void serial8250_rpm_get_tx(struct uart_8250_port *p) rpm_active = xchg(&p->rpm_tx_active, 1); if (rpm_active) return; - pm_runtime_get_sync(p->port.dev); + pm_runtime_get(p->port.dev); } EXPORT_SYMBOL_GPL(serial8250_rpm_get_tx); @@ -2509,6 +2509,42 @@ static void serial8250_shutdown(struct uart_port *port) serial8250_do_shutdown(port); } +/* + * Wakes up the serial port if it has been runtime PM suspended. + * + * Note that we rely on the serial8250_rpm functions to manage the + * runtime PM usage count. We also currently depend on the runtime + * PM autosuspend timeout to keep the port awake until start_tx(). + * Eventually we should just use runtime PM functions and not rely + * on the autosuspend timeout. + * + * Caller must hold port->lock for port->runtime_suspended status. + * Also the port drivers must hold port->lock when changing the + * state for port->runtime_suspended in runtime PM functions. + */ +static int serial8250_wakeup(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct device *dev = up->port.dev; + int err; + + if (!pm_runtime_enabled(dev)) + return 0; + + if (!port->runtime_suspended) { + pm_runtime_mark_last_busy(dev); + return 0; + } + + err = pm_request_resume(dev); + if (err < 0) { + dev_warn(dev, "wakeup failed: %d\n", err); + return err; + } + + return -EINPROGRESS; +} + /* Nuvoton NPCM UARTs have a custom divisor calculation */ static unsigned int npcm_get_divisor(struct uart_8250_port *up, unsigned int baud) @@ -3237,6 +3273,7 @@ static const struct uart_ops serial8250_pops = { .break_ctl = serial8250_break_ctl, .startup = serial8250_startup, .shutdown = serial8250_shutdown, + .wakeup = serial8250_wakeup, .set_termios = serial8250_set_termios, .set_ldisc = serial8250_set_ldisc, .pm = serial8250_pm, From patchwork Mon Nov 15 08:42:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tony Lindgren X-Patchwork-Id: 517387 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B0E54C433F5 for ; Mon, 15 Nov 2021 08:42:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9813561BF5 for ; Mon, 15 Nov 2021 08:42:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237149AbhKOIpr (ORCPT ); Mon, 15 Nov 2021 03:45:47 -0500 Received: from muru.com ([72.249.23.125]:56428 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236367AbhKOIpW (ORCPT ); Mon, 15 Nov 2021 03:45:22 -0500 Received: from hillo.muru.com (localhost [127.0.0.1]) by muru.com (Postfix) with ESMTP id 4C3A4814C; Mon, 15 Nov 2021 08:43:03 +0000 (UTC) From: Tony Lindgren To: Greg Kroah-Hartman Cc: Andy Shevchenko , Jiri Slaby , Johan Hovold , Sebastian Andrzej Siewior , Vignesh Raghavendra , linux-serial@vger.kernel.org, linux-omap@vger.kernel.org, linux-kernel@vger.kernel.org, Andy Shevchenko Subject: [PATCH 5/7] serial: 8250_omap: Require a valid wakeirq for deeper idle states Date: Mon, 15 Nov 2021 10:42:01 +0200 Message-Id: <20211115084203.56478-6-tony@atomide.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115084203.56478-1-tony@atomide.com> References: <20211115084203.56478-1-tony@atomide.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org For deeper idle states the 8250 device gets powered off. The wakeup is handled with a separate wakeirq controller monitoring the RX pin. Let's check for a valid wakeirq before enabling deeper idle states. Signed-off-by: Tony Lindgren --- drivers/tty/serial/8250/8250_omap.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -133,6 +133,7 @@ struct omap8250_priv { spinlock_t rx_dma_lock; bool rx_dma_broken; bool throttled; + unsigned int allow_rpm:1; }; struct omap8250_dma_params { @@ -676,6 +677,7 @@ static int omap_8250_startup(struct uart_port *port) ret = dev_pm_set_dedicated_wake_irq(port->dev, priv->wakeirq); if (ret) return ret; + priv->allow_rpm = 1; } pm_runtime_get_sync(port->dev); @@ -722,6 +724,10 @@ static int omap_8250_startup(struct uart_port *port) if (up->dma && !(priv->habit & UART_HAS_EFR2)) up->dma->rx_dma(up); + /* Block runtime PM if no wakeirq, paired with shutdown */ + if (!priv->allow_rpm) + pm_runtime_get_noresume(port->dev); + pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); return 0; @@ -760,6 +766,10 @@ static void omap_8250_shutdown(struct uart_port *port) serial_out(up, UART_LCR, up->lcr & ~UART_LCR_SBC); serial_out(up, UART_FCR, UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + /* Clear possible PM runtime block to pair with startup */ + if (!priv->allow_rpm) + pm_runtime_put_noidle(port->dev); + pm_runtime_mark_last_busy(port->dev); pm_runtime_put_autosuspend(port->dev); free_irq(port->irq, port); From patchwork Mon Nov 15 08:42:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tony Lindgren X-Patchwork-Id: 518538 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D5F35C433EF for ; Mon, 15 Nov 2021 08:42:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C02F261BF5 for ; Mon, 15 Nov 2021 08:42:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236575AbhKOIpo (ORCPT ); Mon, 15 Nov 2021 03:45:44 -0500 Received: from muru.com ([72.249.23.125]:56448 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236385AbhKOIpY (ORCPT ); Mon, 15 Nov 2021 03:45:24 -0500 Received: from hillo.muru.com (localhost [127.0.0.1]) by muru.com (Postfix) with ESMTP id 715118111; Mon, 15 Nov 2021 08:43:05 +0000 (UTC) From: Tony Lindgren To: Greg Kroah-Hartman Cc: Andy Shevchenko , Jiri Slaby , Johan Hovold , Sebastian Andrzej Siewior , Vignesh Raghavendra , linux-serial@vger.kernel.org, linux-omap@vger.kernel.org, linux-kernel@vger.kernel.org, Andy Shevchenko Subject: [PATCH 6/7] serial: 8250_omap: Drop the use of pm_runtime_irq_safe() Date: Mon, 15 Nov 2021 10:42:02 +0200 Message-Id: <20211115084203.56478-7-tony@atomide.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115084203.56478-1-tony@atomide.com> References: <20211115084203.56478-1-tony@atomide.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org We can finally drop the pm_runtime_irq_safe() usage for 8250_omap driver. We already have the serial layer RX wake path fixed for power management. We no longer allow deeper idle states unless the kernel console has been detached, and we require that the RX wakeirq is configured. For TX path, we now use the prep_tx() and uart_flush_tx() calls. To drop pm_runtime_irq_safe(), we remove all PM runtime calls from the interrupt context. If we ever see an interrupt for an idled port, we just bail out. Signed-off-by: Tony Lindgren --- drivers/tty/serial/8250/8250_omap.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -621,6 +621,9 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) unsigned int iir, lsr; int ret; + if (port->runtime_suspended) + return IRQ_NONE; + #ifdef CONFIG_SERIAL_8250_DMA if (up->dma) { ret = omap_8250_dma_handle_irq(port); @@ -628,7 +631,6 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) } #endif - serial8250_rpm_get(up); lsr = serial_port_in(port, UART_LSR); iir = serial_port_in(port, UART_IIR); ret = serial8250_handle_irq(port, iir); @@ -662,8 +664,6 @@ static irqreturn_t omap8250_irq(int irq, void *dev_id) schedule_delayed_work(&up->overrun_backoff, delay); } - serial8250_rpm_put(up); - return IRQ_RETVAL(ret); } @@ -1191,13 +1191,9 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) unsigned char status; u8 iir; - serial8250_rpm_get(up); - iir = serial_port_in(port, UART_IIR); - if (iir & UART_IIR_NO_INT) { - serial8250_rpm_put(up); + if (iir & UART_IIR_NO_INT) return IRQ_HANDLED; - } spin_lock(&port->lock); @@ -1226,7 +1222,6 @@ static int omap_8250_dma_handle_irq(struct uart_port *port) uart_unlock_and_check_sysrq(port); - serial8250_rpm_put(up); return 1; } @@ -1420,8 +1415,6 @@ static int omap8250_probe(struct platform_device *pdev) if (!of_get_available_child_count(pdev->dev.of_node)) pm_runtime_set_autosuspend_delay(&pdev->dev, -1); - pm_runtime_irq_safe(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(&up, priv); From patchwork Mon Nov 15 08:42:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tony Lindgren X-Patchwork-Id: 517388 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id F0C5AC433F5 for ; Mon, 15 Nov 2021 08:42:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DED5E61BF8 for ; Mon, 15 Nov 2021 08:42:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233752AbhKOIpc (ORCPT ); Mon, 15 Nov 2021 03:45:32 -0500 Received: from muru.com ([72.249.23.125]:56456 "EHLO muru.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236413AbhKOIp1 (ORCPT ); Mon, 15 Nov 2021 03:45:27 -0500 Received: from hillo.muru.com (localhost [127.0.0.1]) by muru.com (Postfix) with ESMTP id 8FBEE826C; Mon, 15 Nov 2021 08:43:07 +0000 (UTC) From: Tony Lindgren To: Greg Kroah-Hartman Cc: Andy Shevchenko , Jiri Slaby , Johan Hovold , Sebastian Andrzej Siewior , Vignesh Raghavendra , linux-serial@vger.kernel.org, linux-omap@vger.kernel.org, linux-kernel@vger.kernel.org, Andy Shevchenko Subject: [PATCH 7/7] serial: 8250_port: Remove calls to runtime PM Date: Mon, 15 Nov 2021 10:42:03 +0200 Message-Id: <20211115084203.56478-8-tony@atomide.com> X-Mailer: git-send-email 2.33.1 In-Reply-To: <20211115084203.56478-1-tony@atomide.com> References: <20211115084203.56478-1-tony@atomide.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Andy Shevchenko Since we now have runtime PM calls in serial_core.c the individual drivers do not need them anymore for the struct uart_ops related functions. Remove runtime PM calls in 8250 driver. This still leaves the flag for UART_CAP_RPM for serial8250_rpm_get_tx(), serial8250_rpm_put_tx() and serial8250_wakeup() to manage the reference count for serial TX. Signed-off-by: Andy Shevchenko [tony@atomide.com: updated to remove the exported functions too] Signed-off-by: Tony Lindgren --- drivers/tty/serial/8250/8250.h | 3 - drivers/tty/serial/8250/8250_port.c | 98 ++++++++--------------------- 2 files changed, 27 insertions(+), 74 deletions(-) diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -152,9 +152,6 @@ static inline bool serial8250_clear_THRI(struct uart_8250_port *up) struct uart_8250_port *serial8250_get_port(int line); -void serial8250_rpm_get(struct uart_8250_port *p); -void serial8250_rpm_put(struct uart_8250_port *p); - void serial8250_rpm_get_tx(struct uart_8250_port *p); void serial8250_rpm_put_tx(struct uart_8250_port *p); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -573,23 +573,6 @@ void serial8250_clear_and_reinit_fifos(struct uart_8250_port *p) } EXPORT_SYMBOL_GPL(serial8250_clear_and_reinit_fifos); -void serial8250_rpm_get(struct uart_8250_port *p) -{ - if (!(p->capabilities & UART_CAP_RPM)) - return; - pm_runtime_get_sync(p->port.dev); -} -EXPORT_SYMBOL_GPL(serial8250_rpm_get); - -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); -} -EXPORT_SYMBOL_GPL(serial8250_rpm_put); - /** * serial8250_em485_init() - put uart_8250_port into rs485 emulating * @p: uart_8250_port port instance @@ -724,7 +707,14 @@ void serial8250_rpm_get_tx(struct uart_8250_port *p) rpm_active = xchg(&p->rpm_tx_active, 1); if (rpm_active) return; - pm_runtime_get(p->port.dev); + + /* + * Device has to be powered on at this point. Here we just increase + * reference count to prevent autosuspend until the TX FIFO becomes + * empty. Paired with serial8250_rpm_put_tx(). See also a comment + * in serial8250_tx_chars(). + */ + pm_runtime_get_noresume(p->port.dev); } EXPORT_SYMBOL_GPL(serial8250_rpm_get_tx); @@ -752,8 +742,6 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) { unsigned char lcr = 0, efr = 0; - serial8250_rpm_get(p); - if (p->capabilities & UART_CAP_SLEEP) { if (p->capabilities & UART_CAP_EFR) { lcr = serial_in(p, UART_LCR); @@ -769,8 +757,6 @@ static void serial8250_set_sleep(struct uart_8250_port *p, int sleep) serial_out(p, UART_LCR, lcr); } } - - serial8250_rpm_put(p); } #ifdef CONFIG_SERIAL_8250_RSA @@ -1432,13 +1418,9 @@ 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 | UART_IER_RDI); up->port.read_status_mask &= ~UART_LSR_DR; serial_port_out(port, UART_IER, up->ier); - - serial8250_rpm_put(up); } /** @@ -1477,8 +1459,11 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) stop_tx_timer); struct uart_8250_port *p = em485->port; unsigned long flags; + int err; - serial8250_rpm_get(p); + err = pm_runtime_resume_and_get(p->port.dev); + if (err < 0) + goto out_rpm_err; spin_lock_irqsave(&p->port.lock, flags); if (em485->active_timer == &em485->stop_tx_timer) { p->rs485_stop_tx(p); @@ -1486,8 +1471,9 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) em485->tx_stopped = true; } spin_unlock_irqrestore(&p->port.lock, flags); - serial8250_rpm_put(p); - + pm_runtime_mark_last_busy(p->port.dev); + pm_runtime_put_autosuspend(p->port.dev); +out_rpm_err: return HRTIMER_NORESTART; } @@ -1545,7 +1531,6 @@ 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); /* @@ -1555,7 +1540,6 @@ 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 inline void __start_tx(struct uart_port *port) @@ -1703,9 +1687,7 @@ static void serial8250_enable_ms(struct uart_port *port) up->ier |= UART_IER_MSI; - serial8250_rpm_get(up); serial_port_out(port, UART_IER, up->ier); - serial8250_rpm_put(up); } void serial8250_read_char(struct uart_8250_port *up, unsigned char lsr) @@ -1984,15 +1966,11 @@ 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; } @@ -2002,9 +1980,7 @@ unsigned int serial8250_do_get_mctrl(struct uart_port *port) unsigned int status; unsigned int val; - serial8250_rpm_get(up); status = serial8250_modem_status(up); - serial8250_rpm_put(up); val = serial8250_MSR_to_TIOCM(status); if (up->gpios) @@ -2054,7 +2030,6 @@ 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; @@ -2062,7 +2037,6 @@ 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); } /* @@ -2107,33 +2081,21 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits) static int serial8250_get_poll_char(struct uart_port *port) { - struct uart_8250_port *up = up_to_u8250p(port); unsigned char lsr; - int status; - - serial8250_rpm_get(up); lsr = serial_port_in(port, UART_LSR); + if (!(lsr & UART_LSR_DR)) + return NO_POLL_CHAR; - 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; + return serial_port_in(port, UART_RX); } - static void serial8250_put_poll_char(struct uart_port *port, unsigned char c) { unsigned int ier; struct uart_8250_port *up = up_to_u8250p(port); - serial8250_rpm_get(up); /* * First save the IER then disable the interrupts */ @@ -2155,7 +2117,6 @@ 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 */ @@ -2178,7 +2139,6 @@ 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; @@ -2244,8 +2204,7 @@ int serial8250_do_startup(struct uart_port *port) if (!(port->flags & UPF_BUGGY_UART) && (serial_port_in(port, UART_LSR) == 0xff)) { dev_info_ratelimited(port->dev, "LSR safety check engaged!\n"); - retval = -ENODEV; - goto out; + return -ENODEV; } /* @@ -2333,7 +2292,7 @@ int serial8250_do_startup(struct uart_port *port) retval = up->ops->setup_irq(up); if (retval) - goto out; + return retval; /* * Now, initialize the UART @@ -2432,10 +2391,7 @@ int serial8250_do_startup(struct uart_port *port) outb_p(0x80, icp); inb_p(icp); } - retval = 0; -out: - serial8250_rpm_put(up); - return retval; + return 0; } EXPORT_SYMBOL_GPL(serial8250_do_startup); @@ -2451,7 +2407,6 @@ 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 */ @@ -2495,7 +2450,6 @@ void serial8250_do_shutdown(struct uart_port *port) * the IRQ chain. */ serial_port_in(port, UART_RX); - serial8250_rpm_put(up); up->ops->release_irq(up); } @@ -2737,6 +2691,7 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) unsigned int baud, quot, frac = 0; struct ktermios *termios; unsigned long flags; + int err; mutex_lock(&port->state->port.mutex); @@ -2753,7 +2708,9 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) baud = serial8250_get_baud_rate(port, termios, NULL); quot = serial8250_get_divisor(port, baud, &frac); - serial8250_rpm_get(up); + err = pm_runtime_resume_and_get(port->dev); + if (err < 0) + goto out_lock; spin_lock_irqsave(&port->lock, flags); uart_update_timeout(port, termios->c_cflag, baud); @@ -2762,7 +2719,8 @@ void serial8250_update_uartclk(struct uart_port *port, unsigned int uartclk) serial_port_out(port, UART_LCR, up->lcr); spin_unlock_irqrestore(&port->lock, flags); - serial8250_rpm_put(up); + pm_runtime_mark_last_busy(port->dev); + pm_runtime_put_autosuspend(port->dev); out_lock: mutex_unlock(&port->state->port.mutex); @@ -2793,7 +2751,6 @@ 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); up->lcr = cval; /* Save computed LCR */ @@ -2899,7 +2856,6 @@ 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))