* [PATCH] Bluetooth: hci_qca: Reprobe QCA6390 after hibernation
@ 2026-10-17 17:43 Nikolay Bryskin
2026-10-17 19:30 ` bluez.test.bot
0 siblings, 1 reply; 2+ messages in thread
From: Nikolay Bryskin @ 2026-10-17 17:43 UTC (permalink / raw)
To: linux-bluetooth
Cc: Nikolay Bryskin, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Rocky Liao, linux-arm-msm, linux-kernel
Without a way to power-cycle the SoC (no BT_EN GPIO and no power
sequencer, e.g. the ACPI-enumerated QCA6390 on the Dell XPS 13 9310),
HCI_QUIRK_NON_PERSISTENT_SETUP is not set and hdev->setup() runs only
once, at probe, on the assumption that the SoC keeps its firmware.
Hibernation breaks that assumption: the platform powers the SoC off,
and after the image is restored it is back in its ROM state. The
driver's resume callback, shared with suspend-to-idle via
SIMPLE_DEV_PM_OPS(), only clears QCA_SUSPENDING, so the first command
times out and the controller stays unusable. Error recovery and
power-cycling the adapter from userspace don't help either, because
none of them run setup again:
Bluetooth: hci0: Opcode 0x0c01 failed: -110
Bluetooth: hci0: command 0x0c01 tx timeout
Bluetooth: hci0: crash the soc to collect controller dump
Bluetooth: hci0: Opcode 0x0c05 failed: -110
Bluetooth: hci0: Frame reassembly failed (-84)
...
Bluetooth: hci0: Opcode 0x0c03 failed: -110
Give the restore phase its own callback which, in this configuration,
rebinds the driver so that probe downloads the firmware again. The
rebind is done from a work item on system_freezable_wq, as it can't
run from within a PM callback and it frees the qca_serdev the callback
works on. All other sleep transitions keep their existing callbacks.
Rebinding works here because the SoC is in its ROM state after the
restore. Rebinding while it still runs the downloaded firmware fails
the same way a warm reboot used to before commit 7e7bbddd029b
("Bluetooth: hci_qca: Fix qca6390 enable failure after warm reboot"),
which this patch doesn't change.
Tested on a Dell XPS 13 9310 (DLB16390): Bluetooth comes back about a
second after resuming from hibernation, with no command timeouts.
Suspend-to-idle is unaffected.
Fixes: e5d6468fe9d8 ("Bluetooth: hci_qca: Add support for Qualcomm Bluetooth SoC QCA6390")
Assisted-by: LLM
Signed-off-by: Nikolay Bryskin <nbryskin@gmail.com>
---
drivers/bluetooth/hci_qca.c | 58 ++++++++++++++++++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 7089e9b639b2..7bca14d364b9 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -2775,7 +2775,63 @@ static int __maybe_unused qca_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume);
+struct qca_reprobe {
+ struct work_struct work;
+ struct device *dev;
+};
+
+static void qca_reprobe_work(struct work_struct *work)
+{
+ struct qca_reprobe *reprobe = container_of(work, struct qca_reprobe,
+ work);
+ int ret;
+
+ ret = device_reprobe(reprobe->dev);
+ if (ret && ret != -EPROBE_DEFER)
+ dev_err(reprobe->dev, "Failed to reprobe device (error %d)\n",
+ ret);
+
+ put_device(reprobe->dev);
+ kfree(reprobe);
+}
+
+static int __maybe_unused qca_restore(struct device *dev)
+{
+ struct serdev_device *serdev = to_serdev_device(dev);
+ struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
+ struct hci_uart *hu = &qcadev->serdev_hu;
+ struct qca_reprobe *reprobe;
+
+ qca_resume(dev);
+
+ /* Without power control hdev->setup() runs only once, at probe, as
+ * the SoC is expected to keep its firmware. Hibernation powers the
+ * SoC off, so rebind the driver to download the firmware again. The
+ * rebind can't run from a PM callback, and qcadev is gone once it
+ * does, so it gets a work item of its own.
+ */
+ if (hci_test_quirk(hu->hdev, HCI_QUIRK_NON_PERSISTENT_SETUP))
+ return 0;
+
+ reprobe = kzalloc_obj(*reprobe);
+ if (!reprobe)
+ return -ENOMEM;
+
+ INIT_WORK(&reprobe->work, qca_reprobe_work);
+ reprobe->dev = get_device(dev);
+ queue_work(system_freezable_wq, &reprobe->work);
+
+ return 0;
+}
+
+static const struct dev_pm_ops qca_pm_ops = {
+ .suspend = pm_sleep_ptr(qca_suspend),
+ .resume = pm_sleep_ptr(qca_resume),
+ .freeze = pm_sleep_ptr(qca_suspend),
+ .thaw = pm_sleep_ptr(qca_resume),
+ .poweroff = pm_sleep_ptr(qca_suspend),
+ .restore = pm_sleep_ptr(qca_restore),
+};
#ifdef CONFIG_OF
static const struct of_device_id qca_bluetooth_of_match[] = {
--
2.56.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-10-17 19:30 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17 17:43 [PATCH] Bluetooth: hci_qca: Reprobe QCA6390 after hibernation Nikolay Bryskin
2026-10-17 19:30 ` bluez.test.bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox