All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] serial: qcom-geni: Set the serial engine up again after hibernation
@ 2026-10-27 20:31 Birk Skyum
  2026-10-27 20:50 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Birk Skyum @ 2026-10-27 20:31 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Jiri Slaby
  Cc: Praveen Talari, linux-arm-msm, linux-serial, linux-pm,
	linux-kernel

A hibernation image is restored after a power cycle, which resets every
serial engine. The kernel that loads the image only sets up the engines
it uses itself, and on laptops the Bluetooth UART is opened from the
root file system, so that kernel never touches it.

The restored kernel still has port->setup set from before, so the
startup in uart_resume_port() skips qcom_geni_serial_port_setup() and
the port runs on an engine whose FIFOs, packing and transfer mode were
never configured. On a Lenovo Yoga Slim 7x (X1E80100) the WCN7850's
Bluetooth controller then never comes back: every setup attempt of
hci_qca's error recovery ends in "command 0xfc00 tx timeout" and
"Reading QCA version information failed (-110)".

Clear port->setup in a restore callback of its own, so the next startup
sets the engine up again. The other system sleep callbacks are
unchanged.

Signed-off-by: Birk Skyum <birk.skyum@pm.me>
---
Tested on a Lenovo Yoga Slim 7x (X1E80100) with 7.3 and the other
hibernation fixes for this laptop, which are in review:

  pinctrl: qcom: Restore the pin configuration after hibernation (v4)
  https://lore.kernel.org/all/20261021210425.57066-1-birk.skyum@pm.me/
  PCI: qcom: Power the controller down when hibernating (v2), on top of
  the v6 series "PCI: qcom: Implement shutdown() to avoid SMMU/NoC
  errors on reboot"
  https://lore.kernel.org/all/179181074907.22199.10556821368232442334@pm.me/
  soc: qcom: smp2p: Start over after a restore from hibernation
  https://lore.kernel.org/all/179158127177.70185.5691100900886332545@pm.me/
  Bluetooth: hci_qca: Reset the in-band sleep state on power off (v2)
  https://lore.kernel.org/all/20261014180201.57109-1-birk.skyum@pm.me/

The test kernel had a switch for this change, so both ways ran on the
same build, each a real hibernation with a power-off from a fresh boot:

  without   restored after 112 s, Bluetooth never comes back
  with      restored after 89 s, hci_qca's recovery sets the controller
            up about 18 s after the restore and a scan finds devices

pm_test=core and test_resume pass with it as well; there the port is
restored without a power cycle and is simply set up a second time.

7.3 does not have the console pm_runtime_resume() that tty-next adds to
the resume callback. The port here is not a console and this laptop has
no serial console, so console ports were not exercised. For those, the
kernel that loads the image sets the engine up itself when it
registers the console, so clearing port->setup only repeats that at the
next startup.

Built with W=1 and sparse on tty-next with hibernation on, with
hibernation off, and without system sleep.

 drivers/tty/serial/qcom_geni_serial.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index bfdc155c1..4e5b0e247 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -2131,6 +2131,20 @@ static int qcom_geni_serial_resume(struct device *dev)
 	return ret;
 }
 
+/*
+ * A hibernation image is restored after a power cycle, which reset the serial
+ * engine. The kernel that loaded the image only set up the engines it used
+ * itself, so set this one up again when the port starts.
+ */
+static int qcom_geni_serial_restore(struct device *dev)
+{
+	struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
+
+	port->setup = false;
+
+	return qcom_geni_serial_resume(dev);
+}
+
 #if IS_ENABLED(CONFIG_SERIAL_QCOM_GENI_CONSOLE)
 static const struct qcom_geni_device_data qcom_geni_console_data = {
 	.console = true,
@@ -2168,7 +2182,12 @@ static const struct qcom_geni_device_data sa8255p_qcom_geni_uart_data = {
 static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
 	SET_RUNTIME_PM_OPS(qcom_geni_serial_runtime_suspend,
 			   qcom_geni_serial_runtime_resume, NULL)
-	SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_suspend, qcom_geni_serial_resume)
+	.suspend = pm_sleep_ptr(qcom_geni_serial_suspend),
+	.resume = pm_sleep_ptr(qcom_geni_serial_resume),
+	.freeze = pm_sleep_ptr(qcom_geni_serial_suspend),
+	.thaw = pm_sleep_ptr(qcom_geni_serial_resume),
+	.poweroff = pm_sleep_ptr(qcom_geni_serial_suspend),
+	.restore = pm_sleep_ptr(qcom_geni_serial_restore),
 };
 
 static void qcom_geni_serial_sys_shutdown(struct platform_device *pdev)
-- 
2.56.0



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

end of thread, other threads:[~2026-10-27 20:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 20:31 [PATCH] serial: qcom-geni: Set the serial engine up again after hibernation Birk Skyum
2026-10-27 20:50 ` sashiko-bot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.