* [PATCH 0/4] Bluetooth: qca: Fix WCN3950 power-off
@ 2026-10-28 22:34 Michael McAllister
2026-10-28 22:34 ` [PATCH 4/4] Bluetooth: qca: Power off WCN3950 Michael McAllister
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Michael McAllister @ 2026-10-28 22:34 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
linux-bluetooth
Cc: linux-arm-msm, linux-kernel, Balakrishna Godavarthi,
Dmitry Baryshkov, Matthias Kaehlcke, Michael McAllister
Hi,
Turning Bluetooth off on my Moto G10 leaves all four WCN3950 regulator
votes enabled. This series fixes the shared UART cleanup and shutdown
ordering first, then adds WCN3950 to the existing power-off paths.
Thanks,
Michael
Michael McAllister (4):
Bluetooth: qca: Restore flow control after a failed power pulse
Bluetooth: hci_serdev: Track whether the serial port is open
Bluetooth: qca: Power off after unregistering the HCI device
Bluetooth: qca: Power off WCN3950
drivers/bluetooth/hci_mrvl.c | 4 ++--
drivers/bluetooth/hci_qca.c | 38 ++++++++++------------------------
drivers/bluetooth/hci_serdev.c | 37 ++++++++++++++++++++++++++-------
drivers/bluetooth/hci_uart.h | 3 +++
4 files changed, 46 insertions(+), 36 deletions(-)
base-commit: 1081a6ef8f1e0661fd52eb3f878691b0b96cdff9
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/4] Bluetooth: hci_serdev: Track whether the serial port is open
2026-10-28 22:34 [PATCH 0/4] Bluetooth: qca: Fix WCN3950 power-off Michael McAllister
2026-10-28 22:34 ` [PATCH 4/4] Bluetooth: qca: Power off WCN3950 Michael McAllister
@ 2026-10-28 22:34 ` Michael McAllister
2026-10-29 2:09 ` Bluetooth: qca: Fix WCN3950 power-off bluez.test.bot
2026-10-28 22:34 ` [PATCH 1/4] Bluetooth: qca: Restore flow control after a failed power pulse Michael McAllister
2026-10-28 22:34 ` [PATCH 3/4] Bluetooth: qca: Power off after unregistering the HCI device Michael McAllister
3 siblings, 1 reply; 6+ messages in thread
From: Michael McAllister @ 2026-10-28 22:34 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
linux-bluetooth
Cc: linux-arm-msm, linux-kernel, Balakrishna Godavarthi,
Dmitry Baryshkov, Matthias Kaehlcke, Michael McAllister
QCA setup temporarily closes the serial port while enabling the supplies.
If enabling the supplies or reopening the port fails, PROTO_READY stays
set. Error cleanup can then flush or close an already-closed port.
Track the open state separately from protocol readiness. Use the same
open and close helpers in the transport and the QCA and Marvell drivers,
so setup retries do not close the port twice. Skip the serial flush when
the port is closed, while still flushing the protocol queues.
Close the port during unregister according to its actual open state.
Signed-off-by: Michael McAllister <me@michaelmcallister.org>
---
drivers/bluetooth/hci_mrvl.c | 4 ++--
drivers/bluetooth/hci_qca.c | 12 +++++------
drivers/bluetooth/hci_serdev.c | 37 +++++++++++++++++++++++++++-------
drivers/bluetooth/hci_uart.h | 3 +++
4 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/drivers/bluetooth/hci_mrvl.c b/drivers/bluetooth/hci_mrvl.c
index 5798a8db0..38d9add89 100644
--- a/drivers/bluetooth/hci_mrvl.c
+++ b/drivers/bluetooth/hci_mrvl.c
@@ -76,7 +76,7 @@ static int mrvl_open(struct hci_uart *hu)
hu->priv = mrvl;
if (hu->serdev) {
- ret = serdev_device_open(hu->serdev);
+ ret = hci_uart_serdev_open(hu);
if (ret)
goto err;
}
@@ -95,7 +95,7 @@ static int mrvl_close(struct hci_uart *hu)
BT_DBG("hu %p", hu);
if (hu->serdev)
- serdev_device_close(hu->serdev);
+ hci_uart_serdev_close(hu);
skb_queue_purge(&mrvl->txq);
skb_queue_purge(&mrvl->rawq);
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 8705c4543..3d9e6cd15 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1755,8 +1755,8 @@ static int qca_port_reopen(struct hci_uart *hu)
* Without this, we will have RTS and CTS synchronization
* issues.
*/
- serdev_device_close(hu->serdev);
- ret = serdev_device_open(hu->serdev);
+ hci_uart_serdev_close(hu);
+ ret = hci_uart_serdev_open(hu);
if (ret) {
bt_dev_err(hu->hdev, "failed to open port");
return ret;
@@ -1780,12 +1780,12 @@ static int qca_regulator_init(struct hci_uart *hu)
qcadev = serdev_device_get_drvdata(hu->serdev);
if (!qcadev->bt_power->vregs_on) {
- serdev_device_close(hu->serdev);
+ hci_uart_serdev_close(hu);
ret = qca_regulator_enable(qcadev);
if (ret)
return ret;
- ret = serdev_device_open(hu->serdev);
+ ret = hci_uart_serdev_open(hu);
if (ret) {
bt_dev_err(hu->hdev, "failed to open port");
return ret;
@@ -2061,8 +2061,8 @@ static int qca_setup(struct hci_uart *hu)
if (retries < MAX_INIT_RETRIES) {
bt_dev_warn(hdev, "Retry BT power ON:%d", retries);
if (hu->serdev) {
- serdev_device_close(hu->serdev);
- ret = serdev_device_open(hu->serdev);
+ hci_uart_serdev_close(hu);
+ ret = hci_uart_serdev_open(hu);
if (ret) {
bt_dev_err(hdev, "failed to open port");
return ret;
diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c
index 48ba017e2..05aa490a9 100644
--- a/drivers/bluetooth/hci_serdev.c
+++ b/drivers/bluetooth/hci_serdev.c
@@ -21,6 +21,29 @@
#include "hci_uart.h"
+int hci_uart_serdev_open(struct hci_uart *hu)
+{
+ int err;
+
+ if (hu->serdev_open)
+ return 0;
+
+ err = serdev_device_open(hu->serdev);
+ if (!err)
+ hu->serdev_open = true;
+
+ return err;
+}
+
+void hci_uart_serdev_close(struct hci_uart *hu)
+{
+ if (!hu->serdev_open)
+ return;
+
+ hu->serdev_open = false;
+ serdev_device_close(hu->serdev);
+}
+
static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
{
struct hci_dev *hdev = hu->hdev;
@@ -102,7 +125,8 @@ static int hci_uart_flush(struct hci_dev *hdev)
}
/* Flush any pending characters in the driver and discipline. */
- serdev_device_write_flush(hu->serdev);
+ if (hu->serdev_open)
+ serdev_device_write_flush(hu->serdev);
if (test_bit(HCI_UART_PROTO_READY, &hu->flags))
hu->proto->flush(hu);
@@ -123,7 +147,7 @@ static int hci_uart_open(struct hci_dev *hdev)
* BT OFF. Upon next BT ON UART port should be opened.
*/
if (!test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
- err = serdev_device_open(hu->serdev);
+ err = hci_uart_serdev_open(hu);
if (err)
return err;
set_bit(HCI_UART_PROTO_READY, &hu->flags);
@@ -154,7 +178,7 @@ static int hci_uart_close(struct hci_dev *hdev)
*/
if (hci_test_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP)) {
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
- serdev_device_close(hu->serdev);
+ hci_uart_serdev_close(hu);
}
return 0;
@@ -314,7 +338,7 @@ int hci_uart_register_device_priv(struct hci_uart *hu,
if (percpu_init_rwsem(&hu->proto_lock))
return -ENOMEM;
- err = serdev_device_open(hu->serdev);
+ err = hci_uart_serdev_open(hu);
if (err)
goto err_rwsem;
@@ -385,7 +409,7 @@ int hci_uart_register_device_priv(struct hci_uart *hu,
clear_bit(HCI_UART_PROTO_READY, &hu->flags);
p->close(hu);
err_open:
- serdev_device_close(hu->serdev);
+ hci_uart_serdev_close(hu);
err_rwsem:
percpu_free_rwsem(&hu->proto_lock);
return err;
@@ -438,8 +462,7 @@ void hci_uart_unregister_device(struct hci_uart *hu)
*/
hu->proto->close(hu);
- if (proto_ready)
- serdev_device_close(hu->serdev);
+ hci_uart_serdev_close(hu);
hci_free_dev(hdev);
diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
index 7fbe8dffa..2f8e346f3 100644
--- a/drivers/bluetooth/hci_uart.h
+++ b/drivers/bluetooth/hci_uart.h
@@ -66,6 +66,7 @@ struct hci_uart_proto {
struct hci_uart {
struct tty_struct *tty;
struct serdev_device *serdev;
+ bool serdev_open;
struct hci_dev *hdev;
unsigned long flags;
unsigned long hdev_flags;
@@ -112,6 +113,8 @@ static inline int hci_uart_register_device(struct hci_uart *hu,
}
void hci_uart_unregister_device(struct hci_uart *hu);
+int hci_uart_serdev_open(struct hci_uart *hu);
+void hci_uart_serdev_close(struct hci_uart *hu);
int hci_uart_tx_wakeup(struct hci_uart *hu);
int hci_uart_wait_until_sent(struct hci_uart *hu);
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 3/4] Bluetooth: qca: Power off after unregistering the HCI device
2026-10-28 22:34 [PATCH 0/4] Bluetooth: qca: Fix WCN3950 power-off Michael McAllister
` (2 preceding siblings ...)
2026-10-28 22:34 ` [PATCH 1/4] Bluetooth: qca: Restore flow control after a failed power pulse Michael McAllister
@ 2026-10-28 22:34 ` Michael McAllister
3 siblings, 0 replies; 6+ messages in thread
From: Michael McAllister @ 2026-10-28 22:34 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
linux-bluetooth
Cc: linux-arm-msm, linux-kernel, Balakrishna Godavarthi,
Dmitry Baryshkov, Matthias Kaehlcke, Michael McAllister
qca_serdev_remove() powers off the controller while HCI setup and
transmission can still be running. Leave power-off to qca_close(), which
runs after HCI unregistration and write-work cancellation, while the
serial transport is still available.
Use the serial port's open state for the power-pulse check. PROTO_READY
is already clear during protocol cleanup, even when the port is open.
The open-state check also skips serial I/O after a failed setup reopen.
Mark a newly allocated serdev controller as off until power-on is tried,
so registration failures do not send a power pulse before HCI allocation.
Signed-off-by: Michael McAllister <me@michaelmcallister.org>
---
drivers/bluetooth/hci_qca.c | 23 ++---------------------
1 file changed, 2 insertions(+), 21 deletions(-)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 3d9e6cd15..3d688ab75 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -619,6 +619,7 @@ static int qca_open(struct hci_uart *hu)
hu->priv = qca;
if (hu->serdev) {
+ set_bit(QCA_BT_OFF, &qca->flags);
qcadev = serdev_device_get_drvdata(hu->serdev);
switch (qcadev->btsoc_type) {
@@ -2252,11 +2253,7 @@ static void qca_power_off(struct hci_uart *hu)
case QCA_WCN3990:
case QCA_WCN3991:
case QCA_WCN3998:
- /* Both of these write to the serial port which may have
- * already been closed by hci_uart_close(), which closes
- * the port if HCI_QUIRK_NON_PERSISTENT_SETUP is set.
- */
- if (test_bit(HCI_UART_PROTO_READY, &hu->flags)) {
+ if (hu->serdev_open) {
host_set_baudrate(hu, 2400);
qca_send_power_pulse(hu, false);
}
@@ -2580,22 +2577,6 @@ static int qca_serdev_probe(struct serdev_device *serdev)
static void qca_serdev_remove(struct serdev_device *serdev)
{
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
- struct qca_power *power = qcadev->bt_power;
-
- switch (qcadev->btsoc_type) {
- case QCA_WCN3988:
- case QCA_WCN3990:
- case QCA_WCN3991:
- case QCA_WCN3998:
- case QCA_WCN6750:
- case QCA_WCN6855:
- case QCA_WCN7850:
- if (power->vregs_on)
- qca_power_off(&qcadev->serdev_hu);
- break;
- default:
- break;
- }
hci_uart_unregister_device(&qcadev->serdev_hu);
}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 1/4] Bluetooth: qca: Restore flow control after a failed power pulse
2026-10-28 22:34 [PATCH 0/4] Bluetooth: qca: Fix WCN3950 power-off Michael McAllister
2026-10-28 22:34 ` [PATCH 4/4] Bluetooth: qca: Power off WCN3950 Michael McAllister
2026-10-28 22:34 ` [PATCH 2/4] Bluetooth: hci_serdev: Track whether the serial port is open Michael McAllister
@ 2026-10-28 22:34 ` Michael McAllister
2026-10-28 22:34 ` [PATCH 3/4] Bluetooth: qca: Power off after unregistering the HCI device Michael McAllister
3 siblings, 0 replies; 6+ messages in thread
From: Michael McAllister @ 2026-10-28 22:34 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
linux-bluetooth
Cc: linux-arm-msm, linux-kernel, Balakrishna Godavarthi,
Dmitry Baryshkov, Matthias Kaehlcke, Michael McAllister
qca_send_power_pulse() disables hardware flow control before writing to
the UART. If the write fails, it returns without restoring flow control.
Restore flow control on the error path as on success.
Fixes: f9558270b85c ("Bluetooth: hci_qca: use wait_until_sent() for power pulses")
Signed-off-by: Michael McAllister <me@michaelmcallister.org>
---
drivers/bluetooth/hci_qca.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 7089e9b63..8705c4543 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1424,6 +1424,7 @@ static int qca_send_power_pulse(struct hci_uart *hu, bool on)
ret = serdev_device_write_buf(hu->serdev, &cmd, sizeof(cmd));
if (ret < 0) {
bt_dev_err(hu->hdev, "failed to send power pulse %02x", cmd);
+ hci_uart_set_flow_control(hu, false);
return ret;
}
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH 4/4] Bluetooth: qca: Power off WCN3950
2026-10-28 22:34 [PATCH 0/4] Bluetooth: qca: Fix WCN3950 power-off Michael McAllister
@ 2026-10-28 22:34 ` Michael McAllister
2026-10-28 22:34 ` [PATCH 2/4] Bluetooth: hci_serdev: Track whether the serial port is open Michael McAllister
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Michael McAllister @ 2026-10-28 22:34 UTC (permalink / raw)
To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
linux-bluetooth
Cc: linux-arm-msm, linux-kernel, Balakrishna Godavarthi,
Dmitry Baryshkov, Matthias Kaehlcke, Michael McAllister
Turning Bluetooth off on the Moto G10 leaves the WCN3950 regulator
votes enabled. qca_power_off() handles the other WCN39xx controllers
but omits WCN3950 from both switches.
Add it to the existing power-off pulse and regulator-disable paths.
Fixes: d5712c511cb3 ("Bluetooth: qca: add WCN3950 support")
Signed-off-by: Michael McAllister <me@michaelmcallister.org>
---
drivers/bluetooth/hci_qca.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 3d688ab75..540014182 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -2249,6 +2249,7 @@ static void qca_power_off(struct hci_uart *hu)
power = qcadev->bt_power;
switch (soc_type) {
+ case QCA_WCN3950:
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
@@ -2269,6 +2270,7 @@ static void qca_power_off(struct hci_uart *hu)
}
switch (soc_type) {
+ case QCA_WCN3950:
case QCA_WCN3988:
case QCA_WCN3990:
case QCA_WCN3991:
^ permalink raw reply related [flat|nested] 6+ messages in thread
* RE: Bluetooth: qca: Fix WCN3950 power-off
2026-10-28 22:34 ` [PATCH 2/4] Bluetooth: hci_serdev: Track whether the serial port is open Michael McAllister
@ 2026-10-29 2:09 ` bluez.test.bot
0 siblings, 0 replies; 6+ messages in thread
From: bluez.test.bot @ 2026-10-29 2:09 UTC (permalink / raw)
To: linux-bluetooth, me
[-- Attachment #1: Type: text/plain, Size: 1302 bytes --]
This is automated email and please do not reply to this email!
Dear submitter,
Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/series/1195776/
---Test result---
Test Summary:
CheckPatch PASS 3.03 seconds
VerifyFixes PASS 0.07 seconds
VerifySignedoff PASS 0.07 seconds
GitLint PASS 0.93 seconds
SubjectPrefix PASS 0.27 seconds
BuildKernel PASS 32.82 seconds
CheckAllWarning PASS 36.55 seconds
CheckSparse WARNING 40.78 seconds
BuildKernel32 PASS 35.33 seconds
CheckKernelLLVM PASS 36.31 seconds
TestRunnerSetup PASS 768.31 seconds
IncrementalBuild PASS 37.75 seconds
Details
##############################
Test: CheckSparse - WARNING
Desc: Run sparse tool with linux kernel
Output:
drivers/bluetooth/hci_mrvl.c:170:23: warning: restricted __le16 degrades to integerdrivers/bluetooth/hci_mrvl.c:203:23: warning: restricted __le16 degrades to integer
https://github.com/bluez/bluetooth-next/pull/1018
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-29 2:09 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 22:34 [PATCH 0/4] Bluetooth: qca: Fix WCN3950 power-off Michael McAllister
2026-10-28 22:34 ` [PATCH 4/4] Bluetooth: qca: Power off WCN3950 Michael McAllister
2026-10-28 22:34 ` [PATCH 2/4] Bluetooth: hci_serdev: Track whether the serial port is open Michael McAllister
2026-10-29 2:09 ` Bluetooth: qca: Fix WCN3950 power-off bluez.test.bot
2026-10-28 22:34 ` [PATCH 1/4] Bluetooth: qca: Restore flow control after a failed power pulse Michael McAllister
2026-10-28 22:34 ` [PATCH 3/4] Bluetooth: qca: Power off after unregistering the HCI device Michael McAllister
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.