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: 517653 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 86186C433F5 for ; Mon, 15 Nov 2021 08:42:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6BBCB61BCF for ; Mon, 15 Nov 2021 08:42:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229776AbhKOIpP (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-serial@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 --- 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: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: 517652 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 E3731C4332F 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 CC85A63219 for ; Mon, 15 Nov 2021 08:42:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234669AbhKOIpc (ORCPT ); Mon, 15 Nov 2021 03:45:32 -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-serial@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: 517651 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 9FFDDC433EF for ; Mon, 15 Nov 2021 08:42:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 87FE663218 for ; Mon, 15 Nov 2021 08:42:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236684AbhKOIpf (ORCPT ); Mon, 15 Nov 2021 03:45:35 -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-serial@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: 517650 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 4857FC433EF for ; Mon, 15 Nov 2021 08:43:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 31FBC61BF5 for ; Mon, 15 Nov 2021 08:43:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237137AbhKOIpr (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-serial@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);