Linux MIPS Architecture development
 help / color / mirror / Atom feed
* [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback
@ 2026-07-09  6:25 Praveen Talari
  2026-07-09  6:25 ` [PATCH 1/6] tty: serial: change uart_ops.pm callback to return int Praveen Talari
                   ` (7 more replies)
  0 siblings, 8 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  6:25 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru, Praveen Talari

The uart_ops.pm callback has been declared void since its introduction,
which means any error from a driver's power management implementation is
silently discarded by uart_change_pm(). Beyond losing the error
information, uart_change_pm() unconditionally updates state->pm_state
even when the underlying hardware transition failed. This causes the
serial core to track a power state that does not reflect reality:
subsequent calls to uart_change_pm() see the stale cached state as
matching the requested state and skip the callback entirely, leaving the
hardware permanently stuck with no further recovery attempt.

On modern platforms where the .pm callback performs real work —
enabling clock trees, interacting with runtime PM, asserting voltage
regulators — this is a correctness gap. Failures are invisible to the
PM framework, the port proceeds to call ops->startup() on potentially
unpowered hardware, and suspend/resume errors are hidden from the core
that needs to handle them.

This series fixes the problem in four steps:

  Patch 1 changes the uart_ops.pm callback signature from void to int,
  updates uart_change_pm() to propagate errors and only commit
  state->pm_state on success, and handles the return value at every
  call site in serial_core.c with appropriate policy per context
  (propagate, log, or skip-on-failure).

  Patch 2 updates the 8250 driver family: serial8250_do_pm() and
  serial8250_pm() are updated to return int (with the exported symbol
  declaration updated in serial_8250.h), and the 8250 sub-driver
  pm callbacks are updated to return 0.

  Patch 3 updates the remaining non-8250 serial drivers. All .pm
  implementations are updated to return 0. The sh-sci forward
  declaration shared with rsci is also updated.

  Patch 4 updates arch-level implementations: SA1100 (assabet, h3xxx),
  OMAP1/ams-delta (modem_pm, now propagates regulator errors), and
  MIPS/Alchemy (alchemy_8250_pm).

All existing .pm implementations return 0, so there is no functional
change for any current driver. The series purely adds the infrastructure
for drivers to report errors going forward, with the serial core ready
to handle them correctly.

Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
Praveen Talari (6):
      tty: serial: change uart_ops.pm callback to return int
      serial: 8250: update .pm callbacks to return int
      tty: serial: update .pm callbacks to return int
      arch: update uart pm callbacks to return int
      tty: serial: propagate uart_configure_port failure to uart_add_one_port
      serial: qcom-geni: check return value of pm_runtime_resume_and_get()

 arch/arm/mach-omap1/board-ams-delta.c       |  10 +--
 arch/arm/mach-sa1100/assabet.c              |   3 +-
 arch/arm/mach-sa1100/h3xxx.c                |   3 +-
 arch/mips/alchemy/common/platform.c         |   5 +-
 drivers/tty/serial/8250/8250_dw.c           |   3 +-
 drivers/tty/serial/8250/8250_exar.c         |   4 +-
 drivers/tty/serial/8250/8250_mtk.c          |   4 +-
 drivers/tty/serial/8250/8250_omap.c         |   6 +-
 drivers/tty/serial/8250/8250_port.c         |   9 ++-
 drivers/tty/serial/8250/8250_pxa.c          |   6 +-
 drivers/tty/serial/atmel_serial.c           |   5 +-
 drivers/tty/serial/fsl_lpuart.c             |   3 +-
 drivers/tty/serial/msm_serial.c             |   5 +-
 drivers/tty/serial/omap-serial.c            |   3 +-
 drivers/tty/serial/pxa.c                    |   3 +-
 drivers/tty/serial/qcom_geni_serial.c       |  16 +++--
 drivers/tty/serial/samsung_tty.c            |   5 +-
 drivers/tty/serial/sc16is7xx.c              |   5 +-
 drivers/tty/serial/serial_core.c            | 104 ++++++++++++++++++++--------
 drivers/tty/serial/serial_txx9.c            |   3 +-
 drivers/tty/serial/sh-sci-common.h          |   4 +-
 drivers/tty/serial/sh-sci.c                 |   5 +-
 drivers/tty/serial/sprd_serial.c            |   5 +-
 drivers/tty/serial/st-asc.c                 |   5 +-
 drivers/tty/serial/stm32-usart.c            |   5 +-
 drivers/tty/serial/uartlite.c               |   5 +-
 drivers/tty/serial/xilinx_uartps.c          |   5 +-
 include/linux/platform_data/sa11x0-serial.h |   2 +-
 include/linux/serial_8250.h                 |   6 +-
 include/linux/serial_core.h                 |  10 +--
 30 files changed, 171 insertions(+), 86 deletions(-)
---
base-commit: 8cdeaa50eae8dad34885515f62559ee83e7e8dda
change-id: 20260706-add_return_check_for_uart_change_pm-d2cb365202f3

Best regards,
--  
Praveen Talari <praveen.talari@oss.qualcomm.com>


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

* [PATCH 1/6] tty: serial: change uart_ops.pm callback to return int
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
@ 2026-07-09  6:25 ` Praveen Talari
  2026-07-09  6:25 ` [PATCH 2/6] serial: 8250: update .pm callbacks " Praveen Talari
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  6:25 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru, Praveen Talari

The uart_ops.pm callback is currently declared void, causing
uart_change_pm() to silently discard any error from a driver's power
management implementation. Worse, state->pm_state is unconditionally
updated even when the hardware transition failed, causing the serial core
to track a power state that does not reflect reality. Subsequent calls to
uart_change_pm() will then see the (stale) state as matching and skip the
callback entirely, leaving the hardware stuck in the wrong state with no
further recovery attempt.

Change the uart_ops.pm callback signature from void to int. Update
uart_change_pm() to propagate the driver's return value and only commit
state->pm_state on success, preserving consistency between software state
and hardware state across all transitions.

Update all call sites in serial_core.c:

  uart_port_startup(): propagate the error so that port open fails
  cleanly if the hardware cannot be powered on, rather than proceeding
  to call ops->startup() on an unpowered port.

  uart_suspend_port(): return the error directly so the PM framework
  is aware of the failed transition and can react accordingly.

  uart_resume_port(): propagate on both the console-resume and the
  suspended-port-restore paths so a failed power-on is not hidden
  from the PM core.

  uart_configure_port(): log and return early if power-on fails at
  probe time; log a warning if power-down fails after configuration,
  since the port is already registered at that point.

  uart_tty_port_shutdown(), uart_hangup(): log via dev_err() since
  these are void paths where propagation is not meaningful; teardown
  continues regardless.

  uart_line_info(): skip the modem-control status read if power-on
  fails to avoid accessing an unpowered port; log a warning if the
  original power state cannot be restored afterward.

  uart_poll_init(): propagate the power-on error; log a warning if
  the saved power state cannot be restored after a failed poll init.

Also update the uart_port.pm field (used by 8250 sub-drivers that
install a per-port pm function pointer directly) and its kernel-doc to
the new int-returning signature.

No functional change for any existing driver since all current .pm
implementations will be updated to return 0.

Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
 drivers/tty/serial/serial_core.c | 89 ++++++++++++++++++++++++++++------------
 include/linux/serial_core.h      | 10 +++--
 2 files changed, 69 insertions(+), 30 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index a530ad372b43..e624a67a9395 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -52,8 +52,8 @@ static struct lock_class_key port_lock_key;
  */
 #define RS485_MAX_RTS_DELAY	100 /* msecs */
 
-static void uart_change_pm(struct uart_state *state,
-			   enum uart_pm_state pm_state);
+static int uart_change_pm(struct uart_state *state,
+			  enum uart_pm_state pm_state);
 
 static void uart_port_shutdown(struct tty_port *port);
 
@@ -312,7 +312,9 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,
 	/*
 	 * Make sure the device is in D0 state.
 	 */
-	uart_change_pm(state, UART_PM_STATE_ON);
+	retval = uart_change_pm(state, UART_PM_STATE_ON);
+	if (retval)
+		return retval;
 
 	retval = uart_alloc_xmit_buf(&state->port);
 	if (retval)
@@ -1741,7 +1743,8 @@ static void uart_tty_port_shutdown(struct tty_port *port)
 
 	uart_free_xmit_buf(port);
 
-	uart_change_pm(state, UART_PM_STATE_OFF);
+	if (uart_change_pm(state, UART_PM_STATE_OFF))
+		dev_err(uport->dev, "failed to set power state off on shutdown\n");
 }
 
 static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
@@ -1831,8 +1834,13 @@ static void uart_hangup(struct tty_struct *tty)
 			port->count = 0;
 		tty_port_set_active(port, false);
 		tty_port_tty_set(port, NULL);
-		if (uport && !uart_console(uport))
-			uart_change_pm(state, UART_PM_STATE_OFF);
+		if (uport && !uart_console(uport)) {
+			int ret = uart_change_pm(state, UART_PM_STATE_OFF);
+
+			if (ret)
+				dev_err(uport->dev,
+					"failed to set power state off on hangup\n");
+		}
 		wake_up_interruptible(&port->open_wait);
 		wake_up_interruptible(&port->delta_msr_wait);
 	}
@@ -1994,12 +2002,17 @@ static void uart_line_info(struct seq_file *m, struct uart_state *state)
 
 	if (capable(CAP_SYS_ADMIN)) {
 		pm_state = state->pm_state;
-		if (pm_state != UART_PM_STATE_ON)
-			uart_change_pm(state, UART_PM_STATE_ON);
+		if (pm_state != UART_PM_STATE_ON) {
+			if (uart_change_pm(state, UART_PM_STATE_ON))
+				goto line_info_end;
+		}
 		scoped_guard(uart_port_lock_irq, uport)
 			status = uport->ops->get_mctrl(uport);
-		if (pm_state != UART_PM_STATE_ON)
-			uart_change_pm(state, pm_state);
+		if (pm_state != UART_PM_STATE_ON) {
+			if (uart_change_pm(state, pm_state))
+				dev_err(uport->dev,
+					"failed to restore power state after line info\n");
+		}
 
 		seq_printf(m, " tx:%u rx:%u",
 				uport->icount.tx, uport->icount.rx);
@@ -2036,6 +2049,7 @@ static void uart_line_info(struct seq_file *m, struct uart_state *state)
 
 		seq_puts(m, stat_buf);
 	}
+line_info_end:
 	seq_putc(m, '\n');
 #undef STATBIT
 #undef INFOBIT
@@ -2265,17 +2279,24 @@ EXPORT_SYMBOL_GPL(uart_set_options);
  * @pm_state: new state
  *
  * Locking: port->mutex has to be held
+ *
+ * Returns 0 on success, negative error code on failure.
  */
-static void uart_change_pm(struct uart_state *state,
-			   enum uart_pm_state pm_state)
+static int uart_change_pm(struct uart_state *state,
+			  enum uart_pm_state pm_state)
 {
 	struct uart_port *port = uart_port_check(state);
 
 	if (state->pm_state != pm_state) {
-		if (port && port->ops->pm)
-			port->ops->pm(port, pm_state, state->pm_state);
+		if (port && port->ops->pm) {
+			int ret = port->ops->pm(port, pm_state, state->pm_state);
+
+			if (ret)
+				return ret;
+		}
 		state->pm_state = pm_state;
 	}
+	return 0;
 }
 
 struct uart_match {
@@ -2364,9 +2385,7 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *uport)
 	if (uart_console(uport))
 		console_suspend(uport->cons);
 
-	uart_change_pm(state, UART_PM_STATE_OFF);
-
-	return 0;
+	return uart_change_pm(state, UART_PM_STATE_OFF);
 }
 EXPORT_SYMBOL(uart_suspend_port);
 
@@ -2408,8 +2427,12 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
 		if (port->tty && termios.c_cflag == 0)
 			termios = port->tty->termios;
 
-		if (console_suspend_enabled)
-			uart_change_pm(state, UART_PM_STATE_ON);
+		if (console_suspend_enabled) {
+			int ret = uart_change_pm(state, UART_PM_STATE_ON);
+
+			if (ret)
+				return ret;
+		}
 		uport->ops->set_termios(uport, &termios, NULL);
 		if (!console_suspend_enabled && uport->ops->start_rx) {
 			guard(uart_port_lock_irq)(uport);
@@ -2423,7 +2446,9 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport)
 		const struct uart_ops *ops = uport->ops;
 		int ret;
 
-		uart_change_pm(state, UART_PM_STATE_ON);
+		ret = uart_change_pm(state, UART_PM_STATE_ON);
+		if (ret)
+			return ret;
 		scoped_guard(uart_port_lock_irq, uport)
 			if (!(uport->rs485.flags & SER_RS485_ENABLED))
 				ops->set_mctrl(uport, 0);
@@ -2541,7 +2566,12 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 			console_lock();
 
 		/* Power up port for set_mctrl() */
-		uart_change_pm(state, UART_PM_STATE_ON);
+		if (uart_change_pm(state, UART_PM_STATE_ON)) {
+			dev_err(port->dev, "failed to power up port\n");
+			if (uart_console(port))
+				console_unlock();
+			return;
+		}
 
 		/*
 		 * Ensure that the modem control lines are de-activated.
@@ -2578,8 +2608,10 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 		 * Power down all ports by default, except the
 		 * console if we have one.
 		 */
-		if (!uart_console(port))
-			uart_change_pm(state, UART_PM_STATE_OFF);
+		if (!uart_console(port)) {
+			if (uart_change_pm(state, UART_PM_STATE_OFF))
+				dev_err(port->dev, "failed to power down port\n");
+		}
 	}
 }
 
@@ -2608,7 +2640,9 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
 		return -1;
 
 	pm_state = state->pm_state;
-	uart_change_pm(state, UART_PM_STATE_ON);
+	ret = uart_change_pm(state, UART_PM_STATE_ON);
+	if (ret)
+		return ret;
 
 	if (port->ops->poll_init) {
 		/*
@@ -2626,8 +2660,11 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
 		console_list_unlock();
 	}
 
-	if (ret)
-		uart_change_pm(state, pm_state);
+	if (ret) {
+		if (uart_change_pm(state, pm_state))
+			dev_err(port->dev,
+				"failed to restore power state after poll init failure\n");
+	}
 
 	return ret;
 }
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index bdc214386e4a..c82839028220 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -269,8 +269,8 @@ struct gpio_desc;
  *
  *	Locking: caller holds tty_port->mutex
  *
- * @pm: ``void ()(struct uart_port *port, unsigned int state,
- *		 unsigned int oldstate)``
+ * @pm: ``int ()(struct uart_port *port, unsigned int state,
+ *		unsigned int oldstate)``
  *
  *	Perform any power management related activities on the specified @port.
  *	@state indicates the new state (defined by enum uart_pm_state),
@@ -282,6 +282,8 @@ struct gpio_desc;
  *	closed, except when the @port is also the system console. This will
  *	occur even if %CONFIG_PM is not set.
  *
+ *	Returns 0 on success, negative error code on failure.
+ *
  *	Locking: none.
  *	Interrupts: caller dependent.
  *
@@ -391,7 +393,7 @@ struct uart_ops {
 	void		(*set_termios)(struct uart_port *, struct ktermios *new,
 				       const struct ktermios *old);
 	void		(*set_ldisc)(struct uart_port *, struct ktermios *);
-	void		(*pm)(struct uart_port *, unsigned int state,
+	int		(*pm)(struct uart_port *port, unsigned int state,
 			      unsigned int oldstate);
 	const char	*(*type)(struct uart_port *);
 	void		(*release_port)(struct uart_port *);
@@ -464,7 +466,7 @@ struct uart_port {
 	void			(*throttle)(struct uart_port *port);
 	void			(*unthrottle)(struct uart_port *port);
 	int			(*handle_irq)(struct uart_port *);
-	void			(*pm)(struct uart_port *, unsigned int state,
+	int			(*pm)(struct uart_port *port, unsigned int state,
 				      unsigned int old);
 	void			(*handle_break)(struct uart_port *);
 	int			(*rs485_config)(struct uart_port *,

-- 
2.34.1


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

* [PATCH 2/6] serial: 8250: update .pm callbacks to return int
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
  2026-07-09  6:25 ` [PATCH 1/6] tty: serial: change uart_ops.pm callback to return int Praveen Talari
@ 2026-07-09  6:25 ` Praveen Talari
  2026-07-09  6:25 ` [PATCH 3/6] tty: serial: " Praveen Talari
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  6:25 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru, Praveen Talari

The uart_ops.pm and uart_port.pm callback signatures have been changed
from void to int. Update all 8250 .pm implementations to match.

serial8250_do_pm() is the core 8250 power management helper, exported
for use by sub-drivers. Change its return type from void to int and
update its declaration in include/linux/serial_8250.h accordingly.
serial8250_do_pm() always returns 0.

serial8250_pm() is the uart_ops.pm entry point for the 8250 driver. It
delegates to a per-port pm function stored in uart_port.pm or falls back
to serial8250_do_pm(). Both paths are called for their side effects and
the function returns 0.

The remaining sub-driver callbacks perform their PM work through
platform-specific helpers (clocks, runtime PM, GPIO) and currently have
no error paths of their own. Update them to return 0 to satisfy the new
signature; individual error propagation can be added per-driver as
needed.

  8250_exar: exar_pm()
  8250_mtk:  mtk8250_do_pm()
  8250_omap: omap_8250_pm()
  8250_pxa:  serial_pxa_pm()

No functional change intended.

Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
 drivers/tty/serial/8250/8250_dw.c   | 3 ++-
 drivers/tty/serial/8250/8250_exar.c | 4 +++-
 drivers/tty/serial/8250/8250_mtk.c  | 4 +++-
 drivers/tty/serial/8250/8250_omap.c | 6 ++++--
 drivers/tty/serial/8250/8250_port.c | 9 ++++++---
 drivers/tty/serial/8250/8250_pxa.c  | 6 ++++--
 include/linux/serial_8250.h         | 6 +++---
 7 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 5fba913f3301..7d5fa11d685f 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -458,7 +458,7 @@ static int dw8250_handle_irq(struct uart_port *p)
 	return 1;
 }
 
-static void
+static int
 dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
 {
 	if (!state)
@@ -468,6 +468,7 @@ dw8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
 
 	if (state)
 		pm_runtime_put_sync_suspend(port->dev);
+	return 0;
 }
 
 static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index c682c0d0dffa..0d0026ea155f 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -419,7 +419,7 @@ static const struct serial_rs485 generic_rs485_supported = {
 	.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND,
 };
 
-static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old)
+static int exar_pm(struct uart_port *port, unsigned int state, unsigned int old)
 {
 	/*
 	 * Exar UARTs have a SLEEP register that enables or disables each UART
@@ -428,6 +428,8 @@ static void exar_pm(struct uart_port *port, unsigned int state, unsigned int old
 	 * the UART channel may only write to the corresponding bit.
 	 */
 	serial_port_out(port, UART_EXAR_SLEEP, state ? 0xff : 0);
+
+	return 0;
 }
 
 /*
diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index e6a56cf54ae0..9184b1eeddd7 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -452,7 +452,7 @@ static int __maybe_unused mtk8250_runtime_resume(struct device *dev)
 	return 0;
 }
 
-static void
+static int
 mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
 {
 	if (!state)
@@ -462,6 +462,8 @@ mtk8250_do_pm(struct uart_port *port, unsigned int state, unsigned int old)
 
 	if (state)
 		pm_runtime_put_sync_suspend(port->dev);
+
+	return 0;
 }
 
 #ifdef CONFIG_SERIAL_8250_DMA
diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c
index c552c6b9a037..8f099afd6ff8 100644
--- a/drivers/tty/serial/8250/8250_omap.c
+++ b/drivers/tty/serial/8250/8250_omap.c
@@ -527,8 +527,8 @@ static void omap_8250_set_termios(struct uart_port *port,
 }
 
 /* same as 8250 except that we may have extra flow bits set in EFR */
-static void omap_8250_pm(struct uart_port *port, unsigned int state,
-			 unsigned int oldstate)
+static int omap_8250_pm(struct uart_port *port, unsigned int state,
+			unsigned int oldstate)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 	u8 efr;
@@ -546,6 +546,8 @@ static void omap_8250_pm(struct uart_port *port, unsigned int state,
 	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
 	serial_out(up, UART_EFR, efr);
 	serial_out(up, UART_LCR, 0);
+
+	return 0;
 }
 
 static void omap_serial_fill_features_erratas(struct uart_8250_port *up,
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index 630deb7dd344..315e0a2fcc14 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -2848,16 +2848,18 @@ serial8250_set_ldisc(struct uart_port *port, struct ktermios *termios)
 		serial8250_do_set_ldisc(port, termios);
 }
 
-void serial8250_do_pm(struct uart_port *port, unsigned int state,
-		      unsigned int oldstate)
+int serial8250_do_pm(struct uart_port *port, unsigned int state,
+		     unsigned int oldstate)
 {
 	struct uart_8250_port *p = up_to_u8250p(port);
 
 	serial8250_set_sleep(p, state != 0);
+
+	return 0;
 }
 EXPORT_SYMBOL(serial8250_do_pm);
 
-static void
+static int
 serial8250_pm(struct uart_port *port, unsigned int state,
 	      unsigned int oldstate)
 {
@@ -2865,6 +2867,7 @@ serial8250_pm(struct uart_port *port, unsigned int state,
 		port->pm(port, state, oldstate);
 	else
 		serial8250_do_pm(port, state, oldstate);
+	return 0;
 }
 
 static unsigned int serial8250_port_size(struct uart_8250_port *pt)
diff --git a/drivers/tty/serial/8250/8250_pxa.c b/drivers/tty/serial/8250/8250_pxa.c
index 6dd0190b4843..02f0037e0a31 100644
--- a/drivers/tty/serial/8250/8250_pxa.c
+++ b/drivers/tty/serial/8250/8250_pxa.c
@@ -76,8 +76,8 @@ static void serial_pxa_dl_write(struct uart_8250_port *up, u32 value)
 }
 
 
-static void serial_pxa_pm(struct uart_port *port, unsigned int state,
-	      unsigned int oldstate)
+static int serial_pxa_pm(struct uart_port *port, unsigned int state,
+			 unsigned int oldstate)
 {
 	struct pxa8250_data *data = port->private_data;
 
@@ -85,6 +85,8 @@ static void serial_pxa_pm(struct uart_port *port, unsigned int state,
 		clk_prepare_enable(data->clk);
 	else
 		clk_disable_unprepare(data->clk);
+
+	return 0;
 }
 
 static int serial_pxa_probe(struct platform_device *pdev)
diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h
index a95b2d143d24..12916189c20c 100644
--- a/include/linux/serial_8250.h
+++ b/include/linux/serial_8250.h
@@ -57,7 +57,7 @@ struct plat_serial8250_port {
 				     struct ktermios *);
 	unsigned int	(*get_mctrl)(struct uart_port *);
 	int		(*handle_irq)(struct uart_port *);
-	void		(*pm)(struct uart_port *, unsigned int state,
+	int		(*pm)(struct uart_port *port, unsigned int state,
 			      unsigned old);
 	void		(*handle_break)(struct uart_port *);
 };
@@ -189,8 +189,8 @@ void serial8250_do_set_ldisc(struct uart_port *port, struct ktermios *termios);
 unsigned int serial8250_do_get_mctrl(struct uart_port *port);
 int serial8250_do_startup(struct uart_port *port);
 void serial8250_do_shutdown(struct uart_port *port);
-void serial8250_do_pm(struct uart_port *port, unsigned int state,
-		      unsigned int oldstate);
+int serial8250_do_pm(struct uart_port *port, unsigned int state,
+		     unsigned int oldstate);
 void serial8250_do_set_mctrl(struct uart_port *port, unsigned int mctrl);
 void serial8250_do_set_divisor(struct uart_port *port, unsigned int baud,
 			       unsigned int quot);

-- 
2.34.1


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

* [PATCH 3/6] tty: serial: update .pm callbacks to return int
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
  2026-07-09  6:25 ` [PATCH 1/6] tty: serial: change uart_ops.pm callback to return int Praveen Talari
  2026-07-09  6:25 ` [PATCH 2/6] serial: 8250: update .pm callbacks " Praveen Talari
@ 2026-07-09  6:25 ` Praveen Talari
  2026-07-09  6:25 ` [PATCH 4/6] arch: update uart pm " Praveen Talari
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  6:25 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru, Praveen Talari

The uart_ops.pm callback signature has been changed from void to int.
Update all remaining non-8250 serial driver .pm implementations to match
the new signature by returning 0.

The sh-sci driver exports sci_pm() for reuse by rsci.c; update the
forward declaration in sh-sci-common.h accordingly.

Drivers updated:
  atmel_serial:    atmel_serial_pm()
  dz:              dz_pm()
  fsl_lpuart:      lpuart_uart_pm()
  msm_serial:      msm_power()
  omap-serial:     serial_omap_pm()
  pxa:             serial_pxa_pm()
  qcom_geni_serial: qcom_geni_serial_pm()
  samsung_tty:     s3c24xx_serial_pm()
  sc16is7xx:       sc16is7xx_pm()
  serial_txx9:     serial_txx9_pm()
  sh-sci/rsci:     sci_pm()
  sprd_serial:     sprd_pm()
  st-asc:          asc_pm()
  stm32-usart:     stm32_usart_pm()
  uartlite:        ulite_pm()
  xilinx_uartps:   cdns_uart_pm()
  zs:              zs_pm()

No functional change intended.

Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
 drivers/tty/serial/atmel_serial.c     | 5 +++--
 drivers/tty/serial/fsl_lpuart.c       | 3 ++-
 drivers/tty/serial/msm_serial.c       | 5 +++--
 drivers/tty/serial/omap-serial.c      | 3 ++-
 drivers/tty/serial/pxa.c              | 3 ++-
 drivers/tty/serial/qcom_geni_serial.c | 5 +++--
 drivers/tty/serial/samsung_tty.c      | 5 +++--
 drivers/tty/serial/sc16is7xx.c        | 5 +++--
 drivers/tty/serial/serial_txx9.c      | 3 ++-
 drivers/tty/serial/sh-sci-common.h    | 4 ++--
 drivers/tty/serial/sh-sci.c           | 5 +++--
 drivers/tty/serial/sprd_serial.c      | 5 +++--
 drivers/tty/serial/st-asc.c           | 5 +++--
 drivers/tty/serial/stm32-usart.c      | 5 +++--
 drivers/tty/serial/uartlite.c         | 5 +++--
 drivers/tty/serial/xilinx_uartps.c    | 5 +++--
 16 files changed, 43 insertions(+), 28 deletions(-)

diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 5d8c1cfc1c60..bcbcb77d4df8 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2063,8 +2063,8 @@ static void atmel_shutdown(struct uart_port *port)
 /*
  * Power / Clock management.
  */
-static void atmel_serial_pm(struct uart_port *port, unsigned int state,
-			    unsigned int oldstate)
+static int atmel_serial_pm(struct uart_port *port, unsigned int state,
+			   unsigned int oldstate)
 {
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 
@@ -2095,6 +2095,7 @@ static void atmel_serial_pm(struct uart_port *port, unsigned int state,
 	default:
 		dev_err(port->dev, "atmel_serial: unknown pm %d\n", state);
 	}
+	return 0;
 }
 
 /*
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index b7919c05f0fb..a6b6fb7aca88 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -820,7 +820,7 @@ static void lpuart32_start_tx(struct uart_port *port)
 	}
 }
 
-static void
+static int
 lpuart_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
 {
 	switch (state) {
@@ -832,6 +832,7 @@ lpuart_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate
 		pm_runtime_get_sync(port->dev);
 		break;
 	}
+	return 0;
 }
 
 /* return TIOCSER_TEMT when transmitter is not busy */
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 2e999cb9c974..33b663a3514c 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -1421,8 +1421,8 @@ static int msm_verify_port(struct uart_port *port, struct serial_struct *ser)
 	return 0;
 }
 
-static void msm_power(struct uart_port *port, unsigned int state,
-		      unsigned int oldstate)
+static int msm_power(struct uart_port *port, unsigned int state,
+		     unsigned int oldstate)
 {
 	struct msm_port *msm_port = to_msm_port(port);
 
@@ -1440,6 +1440,7 @@ static void msm_power(struct uart_port *port, unsigned int state,
 	default:
 		pr_err("msm_serial: Unknown PM state %d\n", state);
 	}
+	return 0;
 }
 
 #ifdef CONFIG_CONSOLE_POLL
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index a689d190940c..5029821e3559 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1017,7 +1017,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
 	dev_dbg(up->port.dev, "serial_omap_set_termios+%d\n", up->port.line);
 }
 
-static void
+static int
 serial_omap_pm(struct uart_port *port, unsigned int state,
 	       unsigned int oldstate)
 {
@@ -1035,6 +1035,7 @@ serial_omap_pm(struct uart_port *port, unsigned int state,
 	serial_out(up, UART_LCR, UART_LCR_CONF_MODE_B);
 	serial_out(up, UART_EFR, efr);
 	serial_out(up, UART_LCR, 0);
+	return 0;
 }
 
 static void serial_omap_release_port(struct uart_port *port)
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index 10fc8990579b..1f6541f251f9 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -507,7 +507,7 @@ serial_pxa_set_termios(struct uart_port *port, struct ktermios *termios,
 	uart_port_unlock_irqrestore(&up->port, flags);
 }
 
-static void
+static int
 serial_pxa_pm(struct uart_port *port, unsigned int state,
 	      unsigned int oldstate)
 {
@@ -517,6 +517,7 @@ serial_pxa_pm(struct uart_port *port, unsigned int state,
 		clk_prepare_enable(up->clk);
 	else
 		clk_disable_unprepare(up->clk);
+	return 0;
 }
 
 static void serial_pxa_release_port(struct uart_port *port)
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 7ead87b4eb65..17ab8acb3b8e 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -1724,8 +1724,8 @@ static int geni_serial_resource_init(struct uart_port *uport)
 	return 0;
 }
 
-static void qcom_geni_serial_pm(struct uart_port *uport,
-		unsigned int new_state, unsigned int old_state)
+static int qcom_geni_serial_pm(struct uart_port *uport,
+			       unsigned int new_state, unsigned int old_state)
 {
 
 	/* If we've never been called, treat it as off */
@@ -1738,6 +1738,7 @@ static void qcom_geni_serial_pm(struct uart_port *uport,
 		 old_state == UART_PM_STATE_ON)
 		pm_runtime_put_sync(uport->dev);
 
+	return 0;
 }
 
 /**
diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
index 63d0232dffc2..4d35112cb153 100644
--- a/drivers/tty/serial/samsung_tty.c
+++ b/drivers/tty/serial/samsung_tty.c
@@ -1290,8 +1290,8 @@ static int apple_s5l_serial_startup(struct uart_port *port)
 	return ret;
 }
 
-static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
-			      unsigned int old)
+static int s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
+			     unsigned int old)
 {
 	struct s3c24xx_uart_port *ourport = to_ourport(port);
 	int timeout = 10000;
@@ -1318,6 +1318,7 @@ static void s3c24xx_serial_pm(struct uart_port *port, unsigned int level,
 	default:
 		dev_err(port->dev, "s3c24xx_serial: unknown pm %d\n", level);
 	}
+	return 0;
 }
 
 /* baud rate calculation
diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c
index daebd92f32c7..47fec6c5e3c2 100644
--- a/drivers/tty/serial/sc16is7xx.c
+++ b/drivers/tty/serial/sc16is7xx.c
@@ -1216,10 +1216,11 @@ static int sc16is7xx_verify_port(struct uart_port *port,
 	return 0;
 }
 
-static void sc16is7xx_pm(struct uart_port *port, unsigned int state,
-			 unsigned int oldstate)
+static int sc16is7xx_pm(struct uart_port *port, unsigned int state,
+			unsigned int oldstate)
 {
 	sc16is7xx_power(port, (state == UART_PM_STATE_ON) ? 1 : 0);
+	return 0;
 }
 
 static void sc16is7xx_null_void(struct uart_port *port)
diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c
index 4ae9a45c8e3a..e97dea43708a 100644
--- a/drivers/tty/serial/serial_txx9.c
+++ b/drivers/tty/serial/serial_txx9.c
@@ -680,7 +680,7 @@ serial_txx9_set_termios(struct uart_port *up, struct ktermios *termios,
 	uart_port_unlock_irqrestore(up, flags);
 }
 
-static void
+static int
 serial_txx9_pm(struct uart_port *port, unsigned int state,
 	      unsigned int oldstate)
 {
@@ -694,6 +694,7 @@ serial_txx9_pm(struct uart_port *port, unsigned int state,
 	 */
 	if (state == 0 && oldstate != -1)
 		serial_txx9_initialize(port);
+	return 0;
 }
 
 static int serial_txx9_request_resource(struct uart_port *up)
diff --git a/drivers/tty/serial/sh-sci-common.h b/drivers/tty/serial/sh-sci-common.h
index 01ff9fced803..2f03750094df 100644
--- a/drivers/tty/serial/sh-sci-common.h
+++ b/drivers/tty/serial/sh-sci-common.h
@@ -44,8 +44,8 @@ void sci_release_port(struct uart_port *port);
 int sci_request_port(struct uart_port *port);
 void sci_config_port(struct uart_port *port, int flags);
 int sci_verify_port(struct uart_port *port, struct serial_struct *ser);
-void sci_pm(struct uart_port *port, unsigned int state,
-		   unsigned int oldstate);
+int sci_pm(struct uart_port *port, unsigned int state,
+	   unsigned int oldstate);
 
 struct plat_sci_reg {
 	u8 offset;
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 787e7cdc5e9c..36a08fd6677f 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -2931,8 +2931,8 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
 		sci_enable_ms(port);
 }
 
-void sci_pm(struct uart_port *port, unsigned int state,
-		   unsigned int oldstate)
+int sci_pm(struct uart_port *port, unsigned int state,
+	   unsigned int oldstate)
 {
 	struct sci_port *sci_port = to_sci_port(port);
 
@@ -2944,6 +2944,7 @@ void sci_pm(struct uart_port *port, unsigned int state,
 		sci_port_enable(sci_port);
 		break;
 	}
+	return 0;
 }
 EXPORT_SYMBOL_NS_GPL(sci_pm, "SH_SCI");
 
diff --git a/drivers/tty/serial/sprd_serial.c b/drivers/tty/serial/sprd_serial.c
index 092755f35683..571123f58232 100644
--- a/drivers/tty/serial/sprd_serial.c
+++ b/drivers/tty/serial/sprd_serial.c
@@ -900,8 +900,8 @@ static int sprd_verify_port(struct uart_port *port, struct serial_struct *ser)
 	return 0;
 }
 
-static void sprd_pm(struct uart_port *port, unsigned int state,
-		unsigned int oldstate)
+static int sprd_pm(struct uart_port *port, unsigned int state,
+		   unsigned int oldstate)
 {
 	struct sprd_uart_port *sup =
 		container_of(port, struct sprd_uart_port, port);
@@ -914,6 +914,7 @@ static void sprd_pm(struct uart_port *port, unsigned int state,
 		clk_disable_unprepare(sup->clk);
 		break;
 	}
+	return 0;
 }
 
 #ifdef CONFIG_CONSOLE_POLL
diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c
index 6ed9a327702b..39404bd94592 100644
--- a/drivers/tty/serial/st-asc.c
+++ b/drivers/tty/serial/st-asc.c
@@ -435,8 +435,8 @@ static void asc_shutdown(struct uart_port *port)
 	free_irq(port->irq, port);
 }
 
-static void asc_pm(struct uart_port *port, unsigned int state,
-		unsigned int oldstate)
+static int asc_pm(struct uart_port *port, unsigned int state,
+		  unsigned int oldstate)
 {
 	struct asc_port *ascport = to_asc_port(port);
 	unsigned long flags;
@@ -459,6 +459,7 @@ static void asc_pm(struct uart_port *port, unsigned int state,
 		clk_disable_unprepare(ascport->clk);
 		break;
 	}
+	return 0;
 }
 
 static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index ad06b760cfca..a4a93d3911f8 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -1418,8 +1418,8 @@ stm32_usart_verify_port(struct uart_port *port, struct serial_struct *ser)
 	return -EINVAL;
 }
 
-static void stm32_usart_pm(struct uart_port *port, unsigned int state,
-			   unsigned int oldstate)
+static int stm32_usart_pm(struct uart_port *port, unsigned int state,
+			  unsigned int oldstate)
 {
 	struct stm32_port *stm32port = container_of(port,
 			struct stm32_port, port);
@@ -1438,6 +1438,7 @@ static void stm32_usart_pm(struct uart_port *port, unsigned int state,
 		pm_runtime_put_sync(port->dev);
 		break;
 	}
+	return 0;
 }
 
 #if defined(CONFIG_CONSOLE_POLL)
diff --git a/drivers/tty/serial/uartlite.c b/drivers/tty/serial/uartlite.c
index 6240c3d4dfd7..8e2d1d4dc10a 100644
--- a/drivers/tty/serial/uartlite.c
+++ b/drivers/tty/serial/uartlite.c
@@ -412,8 +412,8 @@ static int ulite_verify_port(struct uart_port *port, struct serial_struct *ser)
 	return -EINVAL;
 }
 
-static void ulite_pm(struct uart_port *port, unsigned int state,
-		     unsigned int oldstate)
+static int ulite_pm(struct uart_port *port, unsigned int state,
+		    unsigned int oldstate)
 {
 	int ret;
 
@@ -425,6 +425,7 @@ static void ulite_pm(struct uart_port *port, unsigned int state,
 		pm_runtime_mark_last_busy(port->dev);
 		pm_runtime_put_autosuspend(port->dev);
 	}
+	return 0;
 }
 
 #ifdef CONFIG_CONSOLE_POLL
diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index a072b75dbaf2..c81a57e0c77e 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1231,8 +1231,8 @@ static void cdns_uart_poll_put_char(struct uart_port *port, unsigned char c)
 }
 #endif
 
-static void cdns_uart_pm(struct uart_port *port, unsigned int state,
-		   unsigned int oldstate)
+static int cdns_uart_pm(struct uart_port *port, unsigned int state,
+			unsigned int oldstate)
 {
 	switch (state) {
 	case UART_PM_STATE_OFF:
@@ -1243,6 +1243,7 @@ static void cdns_uart_pm(struct uart_port *port, unsigned int state,
 		pm_runtime_get_sync(port->dev);
 		break;
 	}
+	return 0;
 }
 
 static const struct uart_ops cdns_uart_ops = {

-- 
2.34.1


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

* [PATCH 4/6] arch: update uart pm callbacks to return int
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
                   ` (2 preceding siblings ...)
  2026-07-09  6:25 ` [PATCH 3/6] tty: serial: " Praveen Talari
@ 2026-07-09  6:25 ` Praveen Talari
  2026-07-09  6:25 ` [PATCH 5/6] tty: serial: propagate uart_configure_port failure to uart_add_one_port Praveen Talari
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  6:25 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru, Praveen Talari

The uart_ops.pm and plat_serial8250_port.pm callback signatures have
been changed from void to int. Update all arch-level implementations
that register a uart pm callback to match.

SA1100 (arch/arm/mach-sa1100/):
  Update sa1100_port_fns.pm in include/linux/platform_data/sa11x0-serial.h
  to return int. Update the two board-level implementations:
    assabet.c: assabet_uart_pm() - controls RS-232 transceiver via GPIO
    h3xxx.c:   h3xxx_uart_pm()   - controls RS-232 transceiver via GPIO
  Both have no error path; they return 0.

OMAP1 (arch/arm/mach-omap1/board-ams-delta.c):
  modem_pm() controls a regulator and already captures the regulator
  enable/disable return value. Update it to return int and propagate
  the regulator error instead of only logging it.

Alchemy MIPS (arch/mips/alchemy/common/platform.c):
  alchemy_8250_pm() wraps serial8250_do_pm() with UART clock gating.
  Update it to return int; return 0 since the clock and pm operations
  have no error path here.

Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
 arch/arm/mach-omap1/board-ams-delta.c       | 10 ++++++----
 arch/arm/mach-sa1100/assabet.c              |  3 ++-
 arch/arm/mach-sa1100/h3xxx.c                |  3 ++-
 arch/mips/alchemy/common/platform.c         |  5 +++--
 include/linux/platform_data/sa11x0-serial.h |  2 +-
 5 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/arch/arm/mach-omap1/board-ams-delta.c b/arch/arm/mach-omap1/board-ams-delta.c
index 1bec4fa0bd5e..5cc8274013b1 100644
--- a/arch/arm/mach-omap1/board-ams-delta.c
+++ b/arch/arm/mach-omap1/board-ams-delta.c
@@ -758,19 +758,20 @@ static void __init ams_delta_init(void)
 	omapfb_set_lcd_config(&ams_delta_lcd_config);
 }
 
-static void modem_pm(struct uart_port *port, unsigned int state, unsigned old)
+static int modem_pm(struct uart_port *port, unsigned int state,
+		    unsigned int old)
 {
 	struct modem_private_data *priv = port->private_data;
 	int ret;
 
 	if (!priv)
-		return;
+		return 0;
 
 	if (IS_ERR(priv->regulator))
-		return;
+		return 0;
 
 	if (state == old)
-		return;
+		return 0;
 
 	if (state == 0)
 		ret = regulator_enable(priv->regulator);
@@ -783,6 +784,7 @@ static void modem_pm(struct uart_port *port, unsigned int state, unsigned old)
 		dev_warn(port->dev,
 			 "ams_delta modem_pm: failed to %sable regulator: %d\n",
 			 state ? "dis" : "en", ret);
+	return ret;
 }
 
 static struct plat_serial8250_port ams_delta_modem_ports[] = {
diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c
index 2b833aa0212b..48c3372a0f4f 100644
--- a/arch/arm/mach-sa1100/assabet.c
+++ b/arch/arm/mach-sa1100/assabet.c
@@ -649,7 +649,7 @@ fixup_assabet(struct tag *tags, char **cmdline)
 }
 
 
-static void assabet_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+static int assabet_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
 {
 	if (port->mapbase == _Ser1UTCR0) {
 		if (state)
@@ -657,6 +657,7 @@ static void assabet_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
 		else
 			ASSABET_BCR_set(ASSABET_BCR_RS232EN);
 	}
+	return 0;
 }
 
 static struct sa1100_port_fns assabet_port_fns __initdata = {
diff --git a/arch/arm/mach-sa1100/h3xxx.c b/arch/arm/mach-sa1100/h3xxx.c
index d685f03f51f3..8a307c7ad9de 100644
--- a/arch/arm/mach-sa1100/h3xxx.c
+++ b/arch/arm/mach-sa1100/h3xxx.c
@@ -83,7 +83,7 @@ static struct resource h3xxx_flash_resource =
 /*
  * H3xxx uart support
  */
-static void h3xxx_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
+static int h3xxx_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
 {
 	if (port->mapbase == _Ser3UTCR0) {
 		if (!gpio_request(H3XXX_EGPIO_RS232_ON, "RS232 transceiver")) {
@@ -94,6 +94,7 @@ static void h3xxx_uart_pm(struct uart_port *port, u_int state, u_int oldstate)
 				__func__);
 		}
 	}
+	return 0;
 }
 
 /*
diff --git a/arch/mips/alchemy/common/platform.c b/arch/mips/alchemy/common/platform.c
index 02bf02164752..ef39cf52b168 100644
--- a/arch/mips/alchemy/common/platform.c
+++ b/arch/mips/alchemy/common/platform.c
@@ -28,8 +28,8 @@
 
 #include <prom.h>
 
-static void alchemy_8250_pm(struct uart_port *port, unsigned int state,
-			    unsigned int old_state)
+static int alchemy_8250_pm(struct uart_port *port, unsigned int state,
+			   unsigned int old_state)
 {
 #ifdef CONFIG_SERIAL_8250
 	switch (state) {
@@ -46,6 +46,7 @@ static void alchemy_8250_pm(struct uart_port *port, unsigned int state,
 		break;
 	}
 #endif
+	return 0;
 }
 
 #define PORT(_base, _irq)					\
diff --git a/include/linux/platform_data/sa11x0-serial.h b/include/linux/platform_data/sa11x0-serial.h
index a88096bc74e4..be14a0152787 100644
--- a/include/linux/platform_data/sa11x0-serial.h
+++ b/include/linux/platform_data/sa11x0-serial.h
@@ -18,7 +18,7 @@ struct uart_port;
 struct sa1100_port_fns {
 	void	(*set_mctrl)(struct uart_port *, u_int);
 	u_int	(*get_mctrl)(struct uart_port *);
-	void	(*pm)(struct uart_port *, u_int, u_int);
+	int	(*pm)(struct uart_port *port, u_int state, u_int oldstate);
 	int	(*set_wake)(struct uart_port *, u_int);
 };
 

-- 
2.34.1


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

* [PATCH 5/6] tty: serial: propagate uart_configure_port failure to uart_add_one_port
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
                   ` (3 preceding siblings ...)
  2026-07-09  6:25 ` [PATCH 4/6] arch: update uart pm " Praveen Talari
@ 2026-07-09  6:25 ` Praveen Talari
  2026-07-09  6:25 ` [PATCH 6/6] serial: qcom-geni: check return value of pm_runtime_resume_and_get() Praveen Talari
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  6:25 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru, Praveen Talari

uart_configure_port() was declared void, so the uart_change_pm(ON)
failure introduced in the previous patch used a bare return that silently
dropped the error and allowed port registration to proceed regardless.

Update serial_core_add_one_port() to check the return value and return
immediately on failure. This propagates up through uart_add_one_port()
to the driver's probe function, allowing the driver to handle or report
the failure rather than silently registering a port that could not be
initialised.

Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
 drivers/tty/serial/serial_core.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index e624a67a9395..6cb9e870ba86 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2526,7 +2526,7 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port)
 			port->uartclk / 8, port->uartclk / 4);
 }
 
-static void
+static int
 uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 		    struct uart_port *port)
 {
@@ -2536,7 +2536,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 	 * If there isn't a port here, don't do anything further.
 	 */
 	if (!port->iobase && !port->mapbase && !port->membase)
-		return;
+		return 0;
 
 	/*
 	 * Now do the auto configuration stuff.  Note that config_port
@@ -2559,6 +2559,8 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 	}
 
 	if (port->type != PORT_UNKNOWN) {
+		int ret;
+
 		uart_report_port(drv, port);
 
 		/* Synchronize with possible boot console. */
@@ -2566,11 +2568,12 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 			console_lock();
 
 		/* Power up port for set_mctrl() */
-		if (uart_change_pm(state, UART_PM_STATE_ON)) {
+		ret = uart_change_pm(state, UART_PM_STATE_ON);
+		if (ret) {
 			dev_err(port->dev, "failed to power up port\n");
 			if (uart_console(port))
 				console_unlock();
-			return;
+			return ret;
 		}
 
 		/*
@@ -2613,6 +2616,8 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
 				dev_err(port->dev, "failed to power down port\n");
 		}
 	}
+
+	return 0;
 }
 
 #ifdef CONFIG_CONSOLE_POLL
@@ -3094,6 +3099,7 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
 	struct tty_port *port;
 	struct device *tty_dev;
 	int num_groups;
+	int ret;
 
 	if (uport->line >= drv->nr)
 		return -EINVAL;
@@ -3135,7 +3141,10 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
 	 * immediately after.
 	 */
 	tty_port_link_device(port, drv->tty_driver, uport->line);
-	uart_configure_port(drv, state, uport);
+
+	ret = uart_configure_port(drv, state, uport);
+	if (ret)
+		return ret;
 
 	port->console = uart_console(uport);
 

-- 
2.34.1


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

* [PATCH 6/6] serial: qcom-geni: check return value of pm_runtime_resume_and_get()
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
                   ` (4 preceding siblings ...)
  2026-07-09  6:25 ` [PATCH 5/6] tty: serial: propagate uart_configure_port failure to uart_add_one_port Praveen Talari
@ 2026-07-09  6:25 ` Praveen Talari
  2026-07-09  6:53 ` [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Jiri Slaby
  2026-07-09  7:31 ` Andy Shevchenko
  7 siblings, 0 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  6:25 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru, Praveen Talari

The .pm uart_ops callback calls pm_runtime_resume_and_get() but
discards its return value.  Failures such as -EAGAIN or -EACCES go
unnoticed and the driver continues as though the device is active,
which can lead to register accesses on an unsuspended device.

Check the return value and propagate the error to the caller.  The
.pm callback now returns int (since commit 6ffcacf023cb ("tty: serial:
change uart_ops.pm callback to return int")), so returning the error
code is sufficient for the serial core to handle the failure.

Signed-off-by: Praveen Talari <praveen.talari@oss.qualcomm.com>
---
 drivers/tty/serial/qcom_geni_serial.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index 17ab8acb3b8e..1ed09ac0af0c 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -1727,14 +1727,19 @@ static int geni_serial_resource_init(struct uart_port *uport)
 static int qcom_geni_serial_pm(struct uart_port *uport,
 			       unsigned int new_state, unsigned int old_state)
 {
+	int ret;
 
 	/* If we've never been called, treat it as off */
 	if (old_state == UART_PM_STATE_UNDEFINED)
 		old_state = UART_PM_STATE_OFF;
 
-	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
-		pm_runtime_resume_and_get(uport->dev);
-	else if (new_state == UART_PM_STATE_OFF &&
+	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) {
+		ret = pm_runtime_resume_and_get(uport->dev);
+		if (ret < 0) {
+			dev_err(uport->dev, "Failed to resume and get %d\n", ret);
+			return ret;
+		}
+	} else if (new_state == UART_PM_STATE_OFF &&
 		 old_state == UART_PM_STATE_ON)
 		pm_runtime_put_sync(uport->dev);
 

-- 
2.34.1


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

* Re: [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
                   ` (5 preceding siblings ...)
  2026-07-09  6:25 ` [PATCH 6/6] serial: qcom-geni: check return value of pm_runtime_resume_and_get() Praveen Talari
@ 2026-07-09  6:53 ` Jiri Slaby
  2026-07-09  8:52   ` Praveen Talari
  2026-07-09  7:31 ` Andy Shevchenko
  7 siblings, 1 reply; 13+ messages in thread
From: Jiri Slaby @ 2026-07-09  6:53 UTC (permalink / raw)
  To: Praveen Talari, Greg Kroah-Hartman, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru

On 09. 07. 26, 8:25, Praveen Talari wrote:
> The uart_ops.pm callback has been declared void since its introduction,
> which means any error from a driver's power management implementation is
> silently discarded by uart_change_pm(). Beyond losing the error
> information, uart_change_pm() unconditionally updates state->pm_state
> even when the underlying hardware transition failed. This causes the
> serial core to track a power state that does not reflect reality:
> subsequent calls to uart_change_pm() see the stale cached state as
> matching the requested state and skip the callback entirely, leaving the
> hardware permanently stuck with no further recovery attempt.
> 
> On modern platforms where the .pm callback performs real work —
> enabling clock trees, interacting with runtime PM, asserting voltage
> regulators — this is a correctness gap. Failures are invisible to the
> PM framework, the port proceeds to call ops->startup() on potentially
> unpowered hardware, and suspend/resume errors are hidden from the core
> that needs to handle them.
> 
> This series fixes the problem in four steps:
> 
>    Patch 1 changes the uart_ops.pm callback signature from void to int,
>    updates uart_change_pm() to propagate errors and only commit
>    state->pm_state on success, and handles the return value at every
>    call site in serial_core.c with appropriate policy per context
>    (propagate, log, or skip-on-failure).

So does this break build without the below applied? IOW: breaks 
bisectability?

>    Patch 2 updates the 8250 driver family: serial8250_do_pm() and
>    serial8250_pm() are updated to return int (with the exported symbol
>    declaration updated in serial_8250.h), and the 8250 sub-driver
>    pm callbacks are updated to return 0.
> 
>    Patch 3 updates the remaining non-8250 serial drivers. All .pm
>    implementations are updated to return 0. The sh-sci forward
>    declaration shared with rsci is also updated.
> 
>    Patch 4 updates arch-level implementations: SA1100 (assabet, h3xxx),
>    OMAP1/ams-delta (modem_pm, now propagates regulator errors), and
>    MIPS/Alchemy (alchemy_8250_pm).
> 
> All existing .pm implementations return 0, so there is no functional
> change for any current driver. The series purely adds the infrastructure
> for drivers to report errors going forward, with the serial core ready
> to handle them correctly.

OK, now I miss the rationale behind the patchset. Neither there is a 
possible code path to actually test this?

thanks,
-- 
js
suse labs

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

* Re: [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback
  2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
                   ` (6 preceding siblings ...)
  2026-07-09  6:53 ` [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Jiri Slaby
@ 2026-07-09  7:31 ` Andy Shevchenko
  2026-07-10  4:37   ` Tony Lindgren
  7 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-07-09  7:31 UTC (permalink / raw)
  To: Praveen Talari
  Cc: Greg Kroah-Hartman, Jiri Slaby, Ilpo Järvinen,
	Matthias Brugger, AngeloGioacchino Del Regno, Richard Genoud,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio, linux-kernel,
	linux-serial, linux-arm-kernel, linux-mediatek, linux-arm-msm,
	linux-samsung-soc, linux-stm32, linux-omap, linux-mips,
	Mukesh Kumar Savaliya, aniket.randive, chandana.chiluveru

On Thu, Jul 09, 2026 at 11:55:12AM +0530, Praveen Talari wrote:
> The uart_ops.pm callback has been declared void since its introduction,
> which means any error from a driver's power management implementation is
> silently discarded by uart_change_pm(). Beyond losing the error
> information, uart_change_pm() unconditionally updates state->pm_state
> even when the underlying hardware transition failed. This causes the
> serial core to track a power state that does not reflect reality:
> subsequent calls to uart_change_pm() see the stale cached state as
> matching the requested state and skip the callback entirely, leaving the
> hardware permanently stuck with no further recovery attempt.
> 
> On modern platforms where the .pm callback performs real work —
> enabling clock trees, interacting with runtime PM, asserting voltage
> regulators — this is a correctness gap. Failures are invisible to the
> PM framework, the port proceeds to call ops->startup() on potentially
> unpowered hardware, and suspend/resume errors are hidden from the core
> that needs to handle them.
> 
> This series fixes the problem in four steps:
> 
>   Patch 1 changes the uart_ops.pm callback signature from void to int,
>   updates uart_change_pm() to propagate errors and only commit
>   state->pm_state on success, and handles the return value at every
>   call site in serial_core.c with appropriate policy per context
>   (propagate, log, or skip-on-failure).
> 
>   Patch 2 updates the 8250 driver family: serial8250_do_pm() and
>   serial8250_pm() are updated to return int (with the exported symbol
>   declaration updated in serial_8250.h), and the 8250 sub-driver
>   pm callbacks are updated to return 0.
> 
>   Patch 3 updates the remaining non-8250 serial drivers. All .pm
>   implementations are updated to return 0. The sh-sci forward
>   declaration shared with rsci is also updated.
> 
>   Patch 4 updates arch-level implementations: SA1100 (assabet, h3xxx),
>   OMAP1/ams-delta (modem_pm, now propagates regulator errors), and
>   MIPS/Alchemy (alchemy_8250_pm).
> 
> All existing .pm implementations return 0, so there is no functional
> change for any current driver. The series purely adds the infrastructure
> for drivers to report errors going forward, with the serial core ready
> to handle them correctly.

Just no, please just properly implement runtime PM. The .pm() must die completely.
I used to have the PoC for that long time ago [1], but due to lack of time and
other priorities it went abandoned. Also Atlassian made a brain damages move to
limit repository to 1Gb, so I haven't able to update it for a few years.

Note, that Tony (you have him in the Cc list) did a lot for some corner cases
with that and we still have them IIRC. Tony, do you know what is the state of
affairs with runtime PM for UART?

[1]: https://bitbucket.org/andy-shev/linux/branch/topic/uart/rpm-plus

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback
  2026-07-09  6:53 ` [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Jiri Slaby
@ 2026-07-09  8:52   ` Praveen Talari
  2026-07-09 10:16     ` Andy Shevchenko
  0 siblings, 1 reply; 13+ messages in thread
From: Praveen Talari @ 2026-07-09  8:52 UTC (permalink / raw)
  To: Jiri Slaby, Greg Kroah-Hartman, Ilpo Järvinen,
	Andy Shevchenko, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio
  Cc: linux-kernel, linux-serial, linux-arm-kernel, linux-mediatek,
	linux-arm-msm, linux-samsung-soc, linux-stm32, linux-omap,
	linux-mips, Mukesh Kumar Savaliya, aniket.randive,
	chandana.chiluveru

HI Jiri

On 09-07-2026 12:23, Jiri Slaby wrote:
> On 09. 07. 26, 8:25, Praveen Talari wrote:
>> The uart_ops.pm callback has been declared void since its introduction,
>> which means any error from a driver's power management implementation is
>> silently discarded by uart_change_pm(). Beyond losing the error
>> information, uart_change_pm() unconditionally updates state->pm_state
>> even when the underlying hardware transition failed. This causes the
>> serial core to track a power state that does not reflect reality:
>> subsequent calls to uart_change_pm() see the stale cached state as
>> matching the requested state and skip the callback entirely, leaving the
>> hardware permanently stuck with no further recovery attempt.
>>
>> On modern platforms where the .pm callback performs real work —
>> enabling clock trees, interacting with runtime PM, asserting voltage
>> regulators — this is a correctness gap. Failures are invisible to the
>> PM framework, the port proceeds to call ops->startup() on potentially
>> unpowered hardware, and suspend/resume errors are hidden from the core
>> that needs to handle them.
>>
>> This series fixes the problem in four steps:
>>
>>    Patch 1 changes the uart_ops.pm callback signature from void to int,
>>    updates uart_change_pm() to propagate errors and only commit
>>    state->pm_state on success, and handles the return value at every
>>    call site in serial_core.c with appropriate policy per context
>>    (propagate, log, or skip-on-failure).
>
> So does this break build without the below applied? IOW: breaks 
> bisectability?

You are right, patch 1 alone breaks the build since the driver 
implementations are still void until patches 2–4. The series as 
structured is not bisect-safe.

Do you have any suggestions on how to fix this issue?

>
>>    Patch 2 updates the 8250 driver family: serial8250_do_pm() and
>>    serial8250_pm() are updated to return int (with the exported symbol
>>    declaration updated in serial_8250.h), and the 8250 sub-driver
>>    pm callbacks are updated to return 0.
>>
>>    Patch 3 updates the remaining non-8250 serial drivers. All .pm
>>    implementations are updated to return 0. The sh-sci forward
>>    declaration shared with rsci is also updated.
>>
>>    Patch 4 updates arch-level implementations: SA1100 (assabet, h3xxx),
>>    OMAP1/ams-delta (modem_pm, now propagates regulator errors), and
>>    MIPS/Alchemy (alchemy_8250_pm).
>>
>> All existing .pm implementations return 0, so there is no functional
>> change for any current driver. The series purely adds the infrastructure
>> for drivers to report errors going forward, with the serial core ready
>> to handle them correctly.
>
> OK, now I miss the rationale behind the patchset. Neither there is a 
> possible code path to actually test this?
The rationale is that qcom_geni_serial_pm() calls 
pm_runtime_resume_and_get() which can fail, but its return value is 
currently discarded because the callback is void. Patch 6 in this series 
is the concrete user: it makes qcom_geni_serial_pm() propagate the 
pm_runtime_resume_and_get() error so that a failure to resume the UART 
power domain is visible at uart_port_startup() time rather than silently 
proceeding to call ops->startup() on an unpowered port.

A code path to test: on qcom platforms with CONFIG_SERIAL_QCOM_GENI 
enabled, if pm_runtime_resume_and_get() fails during 
uart_configure_port() or uart_port_startup(), the error now propagates 
to the caller instead of being dropped. The test case from development 
was injecting a failure in qcom_geni_serial_pm() and observing that 
uart_add_one_port() returns an error rather than proceeding silently.

Thanks,

Praveen Talari

>
> thanks,

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

* Re: [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback
  2026-07-09  8:52   ` Praveen Talari
@ 2026-07-09 10:16     ` Andy Shevchenko
  2026-07-09 12:45       ` Praveen Talari
  0 siblings, 1 reply; 13+ messages in thread
From: Andy Shevchenko @ 2026-07-09 10:16 UTC (permalink / raw)
  To: Praveen Talari
  Cc: Jiri Slaby, Greg Kroah-Hartman, Ilpo Järvinen,
	Matthias Brugger, AngeloGioacchino Del Regno, Richard Genoud,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio, linux-kernel,
	linux-serial, linux-arm-kernel, linux-mediatek, linux-arm-msm,
	linux-samsung-soc, linux-stm32, linux-omap, linux-mips,
	Mukesh Kumar Savaliya, aniket.randive, chandana.chiluveru

On Thu, Jul 09, 2026 at 02:22:17PM +0530, Praveen Talari wrote:
> On 09-07-2026 12:23, Jiri Slaby wrote:
> > On 09. 07. 26, 8:25, Praveen Talari wrote:

...

> > OK, now I miss the rationale behind the patchset. Neither there is a
> > possible code path to actually test this?
> The rationale is that qcom_geni_serial_pm() calls
> pm_runtime_resume_and_get() which can fail, but its return value is
> currently discarded because the callback is void. 

So, you take the solution from a wrong end. Just get rid of .pm() in your driver.
With that, problem solved. Really, this series is a road to even more broken
PM solutions.

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback
  2026-07-09 10:16     ` Andy Shevchenko
@ 2026-07-09 12:45       ` Praveen Talari
  0 siblings, 0 replies; 13+ messages in thread
From: Praveen Talari @ 2026-07-09 12:45 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Jiri Slaby, Greg Kroah-Hartman, Ilpo Järvinen,
	Matthias Brugger, AngeloGioacchino Del Regno, Richard Genoud,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Tony Lindgren, Russell King,
	Thomas Bogendoerfer, bjorn.andersson, Konrad Dybcio, linux-kernel,
	linux-serial, linux-arm-kernel, linux-mediatek, linux-arm-msm,
	linux-samsung-soc, linux-stm32, linux-omap, linux-mips,
	Mukesh Kumar Savaliya, aniket.randive, chandana.chiluveru

Hi Andy

On 09-07-2026 15:46, Andy Shevchenko wrote:
> On Thu, Jul 09, 2026 at 02:22:17PM +0530, Praveen Talari wrote:
>> On 09-07-2026 12:23, Jiri Slaby wrote:
>>> On 09. 07. 26, 8:25, Praveen Talari wrote:
> ...
>
>>> OK, now I miss the rationale behind the patchset. Neither there is a
>>> possible code path to actually test this?
>> The rationale is that qcom_geni_serial_pm() calls
>> pm_runtime_resume_and_get() which can fail, but its return value is
>> currently discarded because the callback is void.
> So, you take the solution from a wrong end. Just get rid of .pm() in your driver.

Ok I agree. Now i can control resources via PM runtime APIs from 
startup(get_sync()) and shutdown(put_sync()) APIs right?

Thanks,

Praveen Talari

> With that, problem solved. Really, this series is a road to even more broken
> PM solutions.
>

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

* Re: [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback
  2026-07-09  7:31 ` Andy Shevchenko
@ 2026-07-10  4:37   ` Tony Lindgren
  0 siblings, 0 replies; 13+ messages in thread
From: Tony Lindgren @ 2026-07-10  4:37 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Praveen Talari, Greg Kroah-Hartman, Jiri Slaby,
	Ilpo Järvinen, Matthias Brugger, AngeloGioacchino Del Regno,
	Richard Genoud, Nicolas Ferre, Alexandre Belloni, Claudiu Beznea,
	Krzysztof Kozlowski, Peter Griffin, Alim Akhtar, Orson Zhai,
	Baolin Wang, Chunyan Zhang, Patrice Chotard, Maxime Coquelin,
	Alexandre Torgue, Peter Korsgaard, Michal Simek, Aaro Koskinen,
	Janusz Krzysztofik, Russell King, Thomas Bogendoerfer,
	bjorn.andersson, Konrad Dybcio, linux-kernel, linux-serial,
	linux-arm-kernel, linux-mediatek, linux-arm-msm,
	linux-samsung-soc, linux-stm32, linux-omap, linux-mips,
	Mukesh Kumar Savaliya, aniket.randive, chandana.chiluveru

* Andy Shevchenko <andriy.shevchenko@linux.intel.com> [260709 10:38]:
> Just no, please just properly implement runtime PM. The .pm() must die completely.
> I used to have the PoC for that long time ago [1], but due to lack of time and
> other priorities it went abandoned. Also Atlassian made a brain damages move to
> limit repository to 1Gb, so I haven't able to update it for a few years.
> 
> Note, that Tony (you have him in the Cc list) did a lot for some corner cases
> with that and we still have them IIRC. Tony, do you know what is the state of
> affairs with runtime PM for UART?

The RX side of things should work for hardware that can wake up to an incoming
event. Linux for sure needs more work to properly handle the various corner case
TX wake-up of a PM runtime idled UART. I'm mostly offline until Monday though,
will check my notes for more details next week.

Regards,

Tony

> [1]: https://bitbucket.org/andy-shev/linux/branch/topic/uart/rpm-plus

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

end of thread, other threads:[~2026-07-10  4:45 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09  6:25 [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Praveen Talari
2026-07-09  6:25 ` [PATCH 1/6] tty: serial: change uart_ops.pm callback to return int Praveen Talari
2026-07-09  6:25 ` [PATCH 2/6] serial: 8250: update .pm callbacks " Praveen Talari
2026-07-09  6:25 ` [PATCH 3/6] tty: serial: " Praveen Talari
2026-07-09  6:25 ` [PATCH 4/6] arch: update uart pm " Praveen Talari
2026-07-09  6:25 ` [PATCH 5/6] tty: serial: propagate uart_configure_port failure to uart_add_one_port Praveen Talari
2026-07-09  6:25 ` [PATCH 6/6] serial: qcom-geni: check return value of pm_runtime_resume_and_get() Praveen Talari
2026-07-09  6:53 ` [PATCH 0/6] tty: serial: propagate errors from uart_ops.pm callback Jiri Slaby
2026-07-09  8:52   ` Praveen Talari
2026-07-09 10:16     ` Andy Shevchenko
2026-07-09 12:45       ` Praveen Talari
2026-07-09  7:31 ` Andy Shevchenko
2026-07-10  4:37   ` Tony Lindgren

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox