[PATCH v2] serial: qcom-geni: add force suspend/resume to system sleep callbacks
From: Praveen Talari
Date: Thu Jul 02 2026 - 07:57:10 EST
During system sleep the hardware resources (clocks, interconnect) are
not gated because the runtime-suspend callback is never invoked from
the system sleep path. This prevents the platform from reaching its
lowest idle state.
The system sleep callbacks qcom_geni_serial_suspend() and
qcom_geni_serial_resume() rely solely on uart_suspend_port() /
uart_resume_port() to manage power. uart_suspend_port() drives the
UART PM state machine to UART_PM_STATE_OFF, which in turn calls
pm_runtime_put_sync() and eventually the runtime-suspend callback.
However, if the runtime-PM usage count is still elevated at the time
of system sleep (e.g. the port is held active by an open file
descriptor), the runtime-suspend callback is never invoked and the
hardware resources (clocks, interconnect) remain enabled across
suspend, preventing the platform from reaching its lowest idle state.
Fix this by calling pm_runtime_force_suspend() at the end of
qcom_geni_serial_suspend() so that the runtime-suspend callback is
always executed regardless of the usage count, and by calling
pm_runtime_force_resume() at the start of qcom_geni_serial_resume()
to restore those resources before uart_resume_port() re-opens the
port.
Signed-off-by: Praveen Talari <praveen.talari@xxxxxxxxxxxxxxxx>
---
Changes in v2:
- EDITME: describe what is new in this series revision.
- EDITME: use bulletpoints and terse descriptions.
- Link to v1: https://patch.msgid.link/20260701-add_force_suspend_resume_to_system_sleep_callbacks-v1-1-38c9a721a462@xxxxxxxxxxxxxxxx
---
v1->v2
- Used console_suspend_enabled instead of uport->suspend.
---
drivers/tty/serial/qcom_geni_serial.c | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index e6b0a55f0cfb..55c9d716bd89 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -1954,6 +1954,7 @@ static int qcom_geni_serial_suspend(struct device *dev)
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
struct qcom_geni_private_data *private_data = uport->private_data;
+ int ret;
/*
* This is done so we can hit the lowest possible state in suspend
@@ -1963,7 +1964,19 @@ static int qcom_geni_serial_suspend(struct device *dev)
geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ACTIVE_ONLY);
geni_icc_set_bw(&port->se);
}
- return uart_suspend_port(private_data->drv, uport);
+
+ ret = uart_suspend_port(private_data->drv, uport);
+ if (ret)
+ return ret;
+
+ /*
+ * When no_console_suspend is set the console must remain active
+ * across system sleep, so skip the force suspend path.
+ */
+ if (!console_suspend_enabled && uart_console(uport))
+ return 0;
+
+ return pm_runtime_force_suspend(dev);
}
static int qcom_geni_serial_resume(struct device *dev)
@@ -1973,6 +1986,10 @@ static int qcom_geni_serial_resume(struct device *dev)
struct uart_port *uport = &port->uport;
struct qcom_geni_private_data *private_data = uport->private_data;
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
ret = uart_resume_port(private_data->drv, uport);
if (uart_console(uport)) {
geni_icc_set_tag(&port->se, QCOM_ICC_TAG_ALWAYS);
---
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
change-id: 20260617-add_force_suspend_resume_to_system_sleep_callbacks-ff2a94756306
Best regards,
--
Praveen Talari <praveen.talari@xxxxxxxxxxxxxxxx>