mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus
@ 2026-09-15 14:24 Manivannan Sadhasivam via B4 Relay
  2026-09-15 14:24 ` [PATCH 1/5] Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules Manivannan Sadhasivam via B4 Relay
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-09-15 14:24 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel
  Cc: linux-kernel, linux-bluetooth, linux-arm-msm, linux-pci,
	linux-pm, Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Manivannan Sadhasivam

Hi,

This series reworks the PCIe M.2 Bluetooth instantiation using the auxiliary
bus.

The Bluetooth interface of the QCA2066, WCN6855, WCN7850 and NXP 88W8987
M.2 modules is accessed over UART, but the modules show up at runtime over
PCIe and are not described in firmware like devicetree. To make the serdev
bus match the Bluetooth driver, the power sequencing driver was creating a
devicetree node at runtime using an of_changeset. But, creating a node just for
driver matching is not recommended as per Krzysztof [1].

So this series reworks the instantiation to use the auxiliary bus instead. The
power sequencing driver still allocates the UART serdev, but now hands it over
through an auxiliary device along with the power sequencing target. So this
series adds auxiliary driver support to hci_qca and btnxpuart drivers binding to
this device and brings up the controller over the existing UART transport.
Finally, it drops the serdev based M.2 code from both BT drivers.

Dependency
==========

This series depends on Monza series from Loic [2] and reuses its
pwrseq_is_controllable() API.

[1] https://lore.kernel.org/linux-pci/768142f0-2638-41f3-a34f-dc80bb99eb7f@kernel.org
[2] https://lore.kernel.org/all/20260904-monza-wireless-v6-0-d8c5042b3efd@oss.qualcomm.com

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
Manivannan Sadhasivam (5):
      Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules
      Bluetooth: btnxpuart: Add auxiliary driver for PCIe M.2 modules
      power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface
      Bluetooth: hci_qca: Drop serdev based M.2 power sequencing code
      Bluetooth: btnxpuart: Drop serdev based M.2 power sequencing code

 MAINTAINERS                               |   1 +
 drivers/bluetooth/Kconfig                 |   2 +
 drivers/bluetooth/btnxpuart.c             | 193 +++++++++++++++++++-----------
 drivers/bluetooth/hci_qca.c               | 138 +++++++++++++++------
 drivers/power/sequencing/Kconfig          |   2 +-
 drivers/power/sequencing/pwrseq-pcie-m2.c | 137 +++++++++++----------
 include/linux/pwrseq/pcie-m2-bt.h         |  38 ++++++
 7 files changed, 343 insertions(+), 168 deletions(-)
---
base-commit: e5e04726cdd043e309677071ab1b65a4b18f422b
change-id: 20260806-pci-m2-bt-rework-78dc2c926f00
prerequisite-message-id: 20260904-monza-wireless-v6-0-d8c5042b3efd@oss.qualcomm.com
prerequisite-patch-id: c4e777757ca9358cc4dd35e23738cd7dc1ead1a0
prerequisite-patch-id: 09600595c2e80b12eda3aae39af192847d0f03d0
prerequisite-patch-id: afe88432c4d459e9094c0e6dd31ebeab6c9f2f08
prerequisite-patch-id: 60fd7b13447ccad7546fa8e5b5609d8f133f788f
prerequisite-patch-id: 61dcbe55fdb6f33a3229c197f16a678785aa16d8
prerequisite-patch-id: 14099e5c573d7c41b899c3f9bb9126aa23f1553e
prerequisite-patch-id: 647ee15b77671054b4bb012afa9ba8694b6c86c5
prerequisite-patch-id: c3a774cae81a6fad287731a38f1b76c7e8babc4c
prerequisite-patch-id: 18b0fa62060a1a2c96e76051944d692699035572
prerequisite-patch-id: df02ab167976ac1f0d5a38c78e559a7372b16c1f
prerequisite-patch-id: 3f2dddd108677252a539cdb1eb661912836289ca

Best regards,
--  
மணிவண்ணன் சதாசிவம்



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

* [PATCH 1/5] Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules
  2026-09-15 14:24 [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Manivannan Sadhasivam via B4 Relay
@ 2026-09-15 14:24 ` Manivannan Sadhasivam via B4 Relay
  2026-09-16  9:39   ` Bartosz Golaszewski
  2026-09-15 14:24 ` [PATCH 2/5] Bluetooth: btnxpuart: " Manivannan Sadhasivam via B4 Relay
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-09-15 14:24 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel
  Cc: linux-kernel, linux-bluetooth, linux-arm-msm, linux-pci,
	linux-pm, Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Manivannan Sadhasivam

From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>

The QCA2066, WCN6855 and WCN7850 combo modules expose Bluetooth over
UART. Since these modules are attached over PCIe, these are not described
in firmware like devicetree. So these modules are discovered at runtime
over PCIe by the power sequencing driver, which allocates the UART serdev
and creates an auxiliary device carrying that transport and the power
sequencing target to power up the Bluetooth function.

Add an auxiliary driver that binds to this device. It reuses the serdev
provided by the producer, brings up the controller through the existing
UART transport with hci_uart_register_device() and drives power through
the sequencer obtained with devm_pwrseq_get().

Add a shared header describing the auxiliary device so that the power
sequencing driver and hci_qca agree on its layout.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
 MAINTAINERS                       |   1 +
 drivers/bluetooth/Kconfig         |   1 +
 drivers/bluetooth/hci_qca.c       | 101 ++++++++++++++++++++++++++++++++++++++
 include/linux/pwrseq/pcie-m2-bt.h |  38 ++++++++++++++
 4 files changed, 141 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..ed0c622fa83f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21750,6 +21750,7 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/connector/pcie-m2-e-connector.yaml
 F:	Documentation/devicetree/bindings/connector/pcie-m2-m-connector.yaml
 F:	drivers/power/sequencing/pwrseq-pcie-m2.c
+F:	include/linux/pwrseq/pcie-m2-bt.h
 
 POWER STATE COORDINATION INTERFACE (PSCI)
 M:	Mark Rutland <mark.rutland@arm.com>
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e8c24d757e9..3acaec6f90ac 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -244,6 +244,7 @@ config BT_HCIUART_QCA
 	depends on BT_HCIUART_SERDEV
 	select BT_HCIUART_H4
 	select BT_QCA
+	select AUXILIARY_BUS
 	help
 	  The Qualcomm Atheros protocol supports HCI In-Band Sleep feature
 	  over serial port interface(H4) between controller and host.
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index cfefa8b72e25..15cc6cca9057 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -15,6 +15,7 @@
  *  by Maxim Krasnyansky and Marcel Holtmann.
  */
 
+#include <linux/auxiliary_bus.h>
 #include <linux/kernel.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
@@ -29,6 +30,7 @@
 #include <linux/acpi.h>
 #include <linux/platform_device.h>
 #include <linux/pwrseq/consumer.h>
+#include <linux/pwrseq/pcie-m2-bt.h>
 #include <linux/regulator/consumer.h>
 #include <linux/serdev.h>
 #include <linux/string_choices.h>
@@ -2850,15 +2852,114 @@ static struct serdev_device_driver qca_serdev_driver = {
 	},
 };
 
+static int qca_bt_aux_probe(struct auxiliary_device *adev,
+			    const struct auxiliary_device_id *id)
+{
+	struct pcie_m2_bt_auxdev *bt_auxdev = to_pcie_m2_bt_auxdev(adev);
+	struct serdev_device *serdev = bt_auxdev->serdev;
+	const struct qca_device_data *data;
+	struct qca_serdev *qcadev;
+	struct hci_dev *hdev;
+	bool controllable;
+	int err;
+
+	data = (const struct qca_device_data *)id->driver_data;
+	if (!data)
+		return -ENODEV;
+
+	qcadev = devm_kzalloc(&adev->dev, sizeof(*qcadev), GFP_KERNEL);
+	if (!qcadev)
+		return -ENOMEM;
+
+	qcadev->serdev_hu.serdev = serdev;
+	qcadev->btsoc_type = data->soc_type;
+	serdev_device_set_drvdata(serdev, qcadev);
+
+	qcadev->pwrseq = devm_pwrseq_get(&adev->dev, bt_auxdev->pwrseq_target);
+	if (IS_ERR(qcadev->pwrseq))
+		return dev_err_probe(&adev->dev, PTR_ERR(qcadev->pwrseq),
+				     "failed to acquire power sequencer\n");
+
+	/*
+	 * When the host cannot gate the BT power individually, treat it as
+	 * always-on.
+	 */
+	controllable = pwrseq_is_controllable(qcadev->pwrseq);
+	if (!controllable) {
+		pwrseq_enable(qcadev->pwrseq);
+		qcadev->pwrseq = NULL;
+	}
+
+	err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
+	if (err)
+		return dev_err_probe(&adev->dev, err,
+				     "failed to register hci_uart device\n");
+
+	hdev = qcadev->serdev_hu.hdev;
+
+	if (controllable) {
+		hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
+		hdev->shutdown = qca_hci_shutdown;
+	}
+
+	if (data->capabilities & QCA_CAP_WIDEBAND_SPEECH)
+		hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
+
+	if (!(data->capabilities & QCA_CAP_VALID_LE_STATES))
+		hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES);
+
+	if (data->capabilities & QCA_CAP_HFP_HW_OFFLOAD)
+		qcadev->support_hfp_hw_offload = true;
+
+	auxiliary_set_drvdata(adev, qcadev);
+
+	return 0;
+}
+
+static void qca_bt_aux_remove(struct auxiliary_device *adev)
+{
+	struct qca_serdev *qcadev = auxiliary_get_drvdata(adev);
+
+	hci_uart_unregister_device(&qcadev->serdev_hu);
+}
+
+static const struct auxiliary_device_id qca_bt_aux_id_table[] = {
+	{
+		.name = "pwrseq_pcie_m2.qca2066-bt",
+		.driver_data = (kernel_ulong_t)&qca_soc_data_qca2066,
+	},
+	{
+		.name = "pwrseq_pcie_m2.wcn6855-bt",
+		.driver_data = (kernel_ulong_t)&qca_soc_data_wcn6855,
+	},
+	{
+		.name = "pwrseq_pcie_m2.wcn7850-bt",
+		.driver_data = (kernel_ulong_t)&qca_soc_data_wcn7850,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(auxiliary, qca_bt_aux_id_table);
+
+static struct auxiliary_driver qca_bt_aux_driver = {
+	.name = "qca_bt",
+	.probe = qca_bt_aux_probe,
+	.remove = qca_bt_aux_remove,
+	.id_table = qca_bt_aux_id_table,
+};
+
 int __init qca_init(void)
 {
 	serdev_device_driver_register(&qca_serdev_driver);
 
+	auxiliary_driver_register(&qca_bt_aux_driver);
+
 	return hci_uart_register_proto(&qca_proto);
 }
 
 int __exit qca_deinit(void)
 {
+	auxiliary_driver_unregister(&qca_bt_aux_driver);
+
 	serdev_device_driver_unregister(&qca_serdev_driver);
 
 	return hci_uart_unregister_proto(&qca_proto);
diff --git a/include/linux/pwrseq/pcie-m2-bt.h b/include/linux/pwrseq/pcie-m2-bt.h
new file mode 100644
index 000000000000..39e7475d7962
--- /dev/null
+++ b/include/linux/pwrseq/pcie-m2-bt.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
+ */
+
+#ifndef __POWER_SEQUENCING_PCIE_M2_BT_H__
+#define __POWER_SEQUENCING_PCIE_M2_BT_H__
+
+#include <linux/auxiliary_bus.h>
+#include <linux/container_of.h>
+
+struct serdev_device;
+
+/**
+ * struct pcie_m2_bt_auxdev - Auxiliary device for the Bluetooth function of a
+ *                            PCIe M.2 module.
+ * @adev: Auxiliary device.
+ * @serdev: Serdev device representing the UART transport. Allocated and owned
+ *          by the producer (power sequencing driver).
+ * @pwrseq_target: Power sequencing target the consumer requests to power up the
+ *                 Bluetooth function.
+ *
+ * On M.2 modules exposing Bluetooth over UART, the controller is not described
+ * in firmware. The power sequencing driver detects the module over PCIe and
+ * publishes this auxiliary device so that the Bluetooth driver can bind to the
+ * pre-allocated serdev transport and drive power sequencing.
+ */
+struct pcie_m2_bt_auxdev {
+	struct auxiliary_device adev;
+	struct serdev_device *serdev;
+	const char *pwrseq_target;
+};
+
+#define to_pcie_m2_bt_auxdev(_adev) \
+	container_of(_adev, struct pcie_m2_bt_auxdev, adev)
+
+#endif /* __POWER_SEQUENCING_PCIE_M2_BT_H__ */

-- 
2.43.0



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

* [PATCH 2/5] Bluetooth: btnxpuart: Add auxiliary driver for PCIe M.2 modules
  2026-09-15 14:24 [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Manivannan Sadhasivam via B4 Relay
  2026-09-15 14:24 ` [PATCH 1/5] Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules Manivannan Sadhasivam via B4 Relay
@ 2026-09-15 14:24 ` Manivannan Sadhasivam via B4 Relay
  2026-09-15 14:51   ` Andy Shevchenko
  2026-09-15 14:24 ` [PATCH 3/5] power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface Manivannan Sadhasivam via B4 Relay
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-09-15 14:24 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel
  Cc: linux-kernel, linux-bluetooth, linux-arm-msm, linux-pci,
	linux-pm, Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Manivannan Sadhasivam

From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>

The 88W8987 combo module exposes Bluetooth over UART. Since this module is
attached over PCIe, it is not described in firmware like devicetree. So it
is discovered at runtime over PCIe by the power sequencing driver, which
allocates the UART serdev and creates an auxiliary device carrying that
transport and the power sequencing target to power up the Bluetooth
function.

Add an auxiliary driver that binds to this device. It reuses the serdev
provided by the producer, brings up the controller through the existing
UART transport with nxp_register_dev() and drives power through the
sequencer obtained with pwrseq_get().

Factor the HCI device setup and teardown shared with the serdev probe into
helpers so both paths register the same controller.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
 drivers/bluetooth/Kconfig     |   1 +
 drivers/bluetooth/btnxpuart.c | 187 +++++++++++++++++++++++++++++++++---------
 2 files changed, 147 insertions(+), 41 deletions(-)

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 3acaec6f90ac..ce0cf50b663b 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -454,6 +454,7 @@ config BT_NXPUART
 	select BT_HCIUART_H4
 	select CRC32
 	select CRC8
+	select AUXILIARY_BUS
 	help
 	  NXP is serial driver required for NXP Bluetooth
 	  devices with UART interface.
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index 25e7b41b349f..2672e481ef15 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -7,10 +7,12 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 
+#include <linux/auxiliary_bus.h>
 #include <linux/serdev.h>
 #include <linux/of.h>
 #include <linux/of_graph.h>
 #include <linux/pwrseq/consumer.h>
+#include <linux/pwrseq/pcie-m2-bt.h>
 #include <linux/skbuff.h>
 #include <linux/unaligned.h>
 #include <linux/firmware.h>
@@ -1840,20 +1842,13 @@ static bool nxp_m2_connector_is_available(struct device *dev)
 	return remote && of_device_is_available(remote);
 }
 
-static int nxp_serdev_probe(struct serdev_device *serdev)
+static int nxp_register_dev(struct btnxpuart_dev *nxpdev)
 {
+	struct serdev_device *serdev = nxpdev->serdev;
 	struct hci_dev *hdev;
-	struct btnxpuart_dev *nxpdev;
 	bdaddr_t ba = {0};
 	int err;
 
-	nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL);
-	if (!nxpdev)
-		return -ENOMEM;
-
-	nxpdev->nxp_data = (struct btnxpuart_data *)device_get_match_data(&serdev->dev);
-
-	nxpdev->serdev = serdev;
 	serdev_device_set_drvdata(serdev, nxpdev);
 
 	serdev_device_set_client_ops(serdev, &btnxpuart_client_ops);
@@ -1864,12 +1859,12 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
 	init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q);
 	init_waitqueue_head(&nxpdev->check_boot_sign_wait_q);
 
-	device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate",
+	device_property_read_u32(&serdev->dev, "fw-init-baudrate",
 				 &nxpdev->fw_init_baudrate);
 	if (!nxpdev->fw_init_baudrate)
 		nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE;
 
-	device_property_read_u32(&nxpdev->serdev->dev, "max-speed",
+	device_property_read_u32(&serdev->dev, "max-speed",
 				 &nxpdev->secondary_baudrate);
 	if (!nxpdev->secondary_baudrate ||
 	    (nxpdev->secondary_baudrate != HCI_NXP_SEC_BAUDRATE_3M &&
@@ -1884,36 +1879,11 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
 
 	crc8_populate_msb(crc8_table, POLYNOMIAL8);
 
-	nxpdev->pdn = devm_reset_control_get_optional_shared(&serdev->dev, NULL);
-	if (IS_ERR(nxpdev->pdn))
-		return PTR_ERR(nxpdev->pdn);
-
-	err = devm_regulator_get_enable(&serdev->dev, "vcc");
-	if (err) {
-		dev_err(&serdev->dev, "Failed to enable vcc regulator\n");
-		return err;
-	}
-
-	if (nxp_m2_connector_is_available(&serdev->ctrl->dev)) {
-		struct pwrseq_desc *pwrseq;
-
-		pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
-		if (IS_ERR(pwrseq))
-			return dev_err_probe(&serdev->dev, PTR_ERR(pwrseq),
-					     "failed to get pwrseq\n");
-
-		nxpdev->pwrseq = pwrseq;
-		err = pwrseq_enable(pwrseq);
-		if (err)
-			goto err_pwrseq_put;
-	}
-
 	/* Initialize and register HCI device */
 	hdev = hci_alloc_dev();
 	if (!hdev) {
 		dev_err(&serdev->dev, "Can't allocate HCI device\n");
-		err = -ENOMEM;
-		goto err_pwrseq_put;
+		return -ENOMEM;
 	}
 
 	reset_control_deassert(nxpdev->pdn);
@@ -1938,7 +1908,7 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
 	hdev->set_bdaddr = nxp_set_bdaddr;
 	SET_HCIDEV_DEV(hdev, &serdev->dev);
 
-	device_property_read_u8_array(&nxpdev->serdev->dev,
+	device_property_read_u8_array(&serdev->dev,
 				      "local-bd-address",
 				      (u8 *)&ba, sizeof(ba));
 	if (bacmp(&ba, BDADDR_ANY))
@@ -1965,15 +1935,61 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
 probe_fail:
 	reset_control_assert(nxpdev->pdn);
 	hci_free_dev(hdev);
+
+	return err;
+}
+
+static int nxp_serdev_probe(struct serdev_device *serdev)
+{
+	struct btnxpuart_dev *nxpdev;
+	int err;
+
+	nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL);
+	if (!nxpdev)
+		return -ENOMEM;
+
+	nxpdev->nxp_data = (struct btnxpuart_data *)device_get_match_data(&serdev->dev);
+
+	nxpdev->serdev = serdev;
+
+	nxpdev->pdn = devm_reset_control_get_optional_shared(&serdev->dev, NULL);
+	if (IS_ERR(nxpdev->pdn))
+		return PTR_ERR(nxpdev->pdn);
+
+	err = devm_regulator_get_enable(&serdev->dev, "vcc");
+	if (err) {
+		dev_err(&serdev->dev, "Failed to enable vcc regulator\n");
+		return err;
+	}
+
+	if (nxp_m2_connector_is_available(&serdev->ctrl->dev)) {
+		struct pwrseq_desc *pwrseq;
+
+		pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
+		if (IS_ERR(pwrseq))
+			return dev_err_probe(&serdev->dev, PTR_ERR(pwrseq),
+					     "failed to get pwrseq\n");
+
+		nxpdev->pwrseq = pwrseq;
+		err = pwrseq_enable(pwrseq);
+		if (err)
+			goto err_pwrseq_put;
+	}
+
+	err = nxp_register_dev(nxpdev);
+	if (err)
+		goto err_pwrseq_put;
+
+	return 0;
+
 err_pwrseq_put:
 	if (nxpdev->pwrseq)
 		pwrseq_put(nxpdev->pwrseq);
 	return err;
 }
 
-static void nxp_serdev_remove(struct serdev_device *serdev)
+static void __nxp_remove(struct btnxpuart_dev *nxpdev)
 {
-	struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev);
 	struct hci_dev *hdev = nxpdev->hdev;
 
 	if (is_fw_downloading(nxpdev)) {
@@ -2000,6 +2016,58 @@ static void nxp_serdev_remove(struct serdev_device *serdev)
 	hci_free_dev(hdev);
 }
 
+static void nxp_serdev_remove(struct serdev_device *serdev)
+{
+	struct btnxpuart_dev *nxpdev = serdev_device_get_drvdata(serdev);
+
+	__nxp_remove(nxpdev);
+}
+
+static int nxp_bt_aux_probe(struct auxiliary_device *adev,
+			    const struct auxiliary_device_id *id)
+{
+	struct pcie_m2_bt_auxdev *bt_auxdev = to_pcie_m2_bt_auxdev(adev);
+	struct btnxpuart_dev *nxpdev;
+	struct pwrseq_desc *pwrseq;
+	int err;
+
+	nxpdev = devm_kzalloc(&adev->dev, sizeof(*nxpdev), GFP_KERNEL);
+	if (!nxpdev)
+		return -ENOMEM;
+
+	nxpdev->nxp_data = (struct btnxpuart_data *)id->driver_data;
+	nxpdev->serdev = bt_auxdev->serdev;
+
+	pwrseq = pwrseq_get(&adev->dev, bt_auxdev->pwrseq_target);
+	if (IS_ERR(pwrseq))
+		return dev_err_probe(&adev->dev, PTR_ERR(pwrseq),
+				     "failed to get pwrseq\n");
+
+	nxpdev->pwrseq = pwrseq;
+	err = pwrseq_enable(pwrseq);
+	if (err)
+		goto err_pwrseq_put;
+
+	err = nxp_register_dev(nxpdev);
+	if (err)
+		goto err_pwrseq_put;
+
+	auxiliary_set_drvdata(adev, nxpdev);
+
+	return 0;
+
+err_pwrseq_put:
+	pwrseq_put(nxpdev->pwrseq);
+	return err;
+}
+
+static void nxp_bt_aux_remove(struct auxiliary_device *adev)
+{
+	struct btnxpuart_dev *nxpdev = auxiliary_get_drvdata(adev);
+
+	__nxp_remove(nxpdev);
+}
+
 static int __maybe_unused nxp_serdev_suspend(struct device *dev)
 {
 	struct btnxpuart_dev *nxpdev = dev_get_drvdata(dev);
@@ -2075,7 +2143,44 @@ static struct serdev_device_driver nxp_serdev_driver = {
 	},
 };
 
-module_serdev_device_driver(nxp_serdev_driver);
+static const struct auxiliary_device_id nxp_bt_aux_id_table[] = {
+	{
+		.name = "pwrseq_pcie_m2.88w8987-bt",
+		.driver_data = (kernel_ulong_t)&w8987_data,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(auxiliary, nxp_bt_aux_id_table);
+
+static struct auxiliary_driver nxp_bt_aux_driver = {
+	.name = "nxp_bt",
+	.probe = nxp_bt_aux_probe,
+	.remove = nxp_bt_aux_remove,
+	.id_table = nxp_bt_aux_id_table,
+};
+
+static int __init nxp_bt_init(void)
+{
+	int err;
+
+	err = serdev_device_driver_register(&nxp_serdev_driver);
+	if (err)
+		return err;
+
+	err = auxiliary_driver_register(&nxp_bt_aux_driver);
+	if (err)
+		serdev_device_driver_unregister(&nxp_serdev_driver);
+
+	return err;
+}
+module_init(nxp_bt_init);
+
+static void __exit nxp_bt_exit(void)
+{
+	auxiliary_driver_unregister(&nxp_bt_aux_driver);
+	serdev_device_driver_unregister(&nxp_serdev_driver);
+}
+module_exit(nxp_bt_exit);
 
 MODULE_AUTHOR("Neeraj Sanjay Kale <neeraj.sanjaykale@nxp.com>");
 MODULE_DESCRIPTION("NXP Bluetooth Serial driver");

-- 
2.43.0



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

* [PATCH 3/5] power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface
  2026-09-15 14:24 [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Manivannan Sadhasivam via B4 Relay
  2026-09-15 14:24 ` [PATCH 1/5] Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules Manivannan Sadhasivam via B4 Relay
  2026-09-15 14:24 ` [PATCH 2/5] Bluetooth: btnxpuart: " Manivannan Sadhasivam via B4 Relay
@ 2026-09-15 14:24 ` Manivannan Sadhasivam via B4 Relay
  2026-09-16  9:19   ` Bartosz Golaszewski
  2026-09-15 14:24 ` [PATCH 4/5] Bluetooth: hci_qca: Drop serdev based M.2 power sequencing code Manivannan Sadhasivam via B4 Relay
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-09-15 14:24 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel
  Cc: linux-kernel, linux-bluetooth, linux-arm-msm, linux-pci,
	linux-pm, Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Manivannan Sadhasivam

From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>

Currently, the driver instantiates the BT interface of the WCN6855 and
WCN7850 combo modules by creating a devicetree node at runtime using an
of_changeset, with the compatible passed through the PCI ID table. But this
method of creating a devicetree node just for matching with the driver is
not recommended.

So create an auxiliary device for the BT interface instead. The serdev
transport is still allocated here and handed over to the Bluetooth driver
through the auxiliary device, along with the power sequencing target used
to power up the controller. Also extend the provider match so that the
auxiliary device created for this connector is matched as its power
sequencing consumer.

Since the runtime node creation is now gone, select AUXILIARY_BUS in
place of OF_DYNAMIC.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
 drivers/power/sequencing/Kconfig          |   2 +-
 drivers/power/sequencing/pwrseq-pcie-m2.c | 137 ++++++++++++++++--------------
 2 files changed, 75 insertions(+), 64 deletions(-)

diff --git a/drivers/power/sequencing/Kconfig b/drivers/power/sequencing/Kconfig
index 1c5f5820f5b7..2943af5db135 100644
--- a/drivers/power/sequencing/Kconfig
+++ b/drivers/power/sequencing/Kconfig
@@ -40,7 +40,7 @@ config POWER_SEQUENCING_PCIE_M2
 	depends on OF
 	depends on PCI
 	depends on SERIAL_DEV_BUS
-	select OF_DYNAMIC
+	select AUXILIARY_BUS
 	help
 	  Say Y here to enable the power sequencing driver for PCIe M.2
 	  connectors. This driver handles the power sequencing for the M.2
diff --git a/drivers/power/sequencing/pwrseq-pcie-m2.c b/drivers/power/sequencing/pwrseq-pcie-m2.c
index bee1aa0e077d..9e485641695c 100644
--- a/drivers/power/sequencing/pwrseq-pcie-m2.c
+++ b/drivers/power/sequencing/pwrseq-pcie-m2.c
@@ -4,16 +4,18 @@
  * Author: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
  */
 
+#include <linux/auxiliary_bus.h>
 #include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
+#include <linux/idr.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_graph.h>
-#include <linux/of_platform.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/pwrseq/pcie-m2-bt.h>
 #include <linux/pwrseq/provider.h>
 #include <linux/regulator/consumer.h>
 #include <linux/serdev.h>
@@ -21,7 +23,7 @@
 
 struct pwrseq_pci_dev {
 	struct serdev_device *serdev;
-	struct of_changeset *ocs;
+	struct pcie_m2_bt_auxdev *bt;
 	struct pci_dev *pdev;
 	struct list_head list;
 };
@@ -196,6 +198,14 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
 	struct pwrseq_pcie_m2_ctx *ctx = pwrseq_device_get_drvdata(pwrseq);
 	struct device_node *endpoint __free(device_node) = NULL;
 
+	/*
+	 * The Bluetooth function is represented by an auxiliary device created
+	 * by this driver. It has no OF node, so match it by verifying that it
+	 * is a child of this connector.
+	 */
+	if (dev_is_auxiliary(dev) && dev->parent == ctx->dev)
+		return PWRSEQ_MATCH_OK;
+
 	/*
 	 * Traverse the 'remote-endpoint' nodes and check if the remote node's
 	 * parent matches the OF node of 'dev'.
@@ -210,78 +220,86 @@ static int pwrseq_pcie_m2_match(struct pwrseq_device *pwrseq,
 	return PWRSEQ_NO_MATCH;
 }
 
+/*
+ * The Bluetooth interface of a supported module is exposed as an auxiliary
+ * device. Its name (combined with this driver's module name) is matched by
+ * the Bluetooth driver. Store that name as the PCI ID driver data.
+ */
 static const struct pci_device_id pwrseq_m2_pci_ids[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_MARVELL_EXT, 0x2b43),
-	  .driver_data = (kernel_ulong_t)"nxp,88w8987-bt" },
+	  .driver_data = (kernel_ulong_t)"88w8987-bt" },
 	{ PCI_DEVICE(PCI_VENDOR_ID_PHILIPS, 0x3003),
-	  .driver_data = (kernel_ulong_t)"nxp,88w8987-bt" },
+	  .driver_data = (kernel_ulong_t)"88w8987-bt" },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x1103, PCI_VENDOR_ID_QCOM, 0x0108),
-	  .driver_data = (kernel_ulong_t)"qcom,qca2066-bt" },
+	  .driver_data = (kernel_ulong_t)"qca2066-bt" },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x1103, PCI_VENDOR_ID_FOXCONN, 0xe105),
-	  .driver_data = (kernel_ulong_t)"qcom,wcn6855-bt" },
+	  .driver_data = (kernel_ulong_t)"wcn6855-bt" },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x1103, PCI_VENDOR_ID_QCOM, 0x337e),
-	  .driver_data = (kernel_ulong_t)"qcom,wcn6855-bt" },
+	  .driver_data = (kernel_ulong_t)"wcn6855-bt" },
 	{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x1107, PCI_VENDOR_ID_QCOM, 0x337c),
-	  .driver_data = (kernel_ulong_t)"qcom,wcn7850-bt" },
+	  .driver_data = (kernel_ulong_t)"wcn7850-bt" },
 	{ } /* Sentinel */
 };
 
-static int pwrseq_pcie_m2_create_bt_node(struct pwrseq_pcie_m2_ctx *ctx,
+static DEFINE_IDA(pwrseq_pcie_m2_bt_ida);
+
+static void pwrseq_pcie_m2_bt_release(struct device *dev)
+{
+	struct pcie_m2_bt_auxdev *bt =
+		to_pcie_m2_bt_auxdev(to_auxiliary_dev(dev));
+
+	ida_free(&pwrseq_pcie_m2_bt_ida, bt->adev.id);
+	kfree(bt);
+}
+
+static int pwrseq_pcie_m2_create_bt_aux(struct pwrseq_pcie_m2_ctx *ctx,
 					struct pwrseq_pci_dev *pci_dev,
-					struct device_node *parent,
 					struct pci_dev *pdev)
 {
 	const struct pci_device_id *id;
 	struct device *dev = ctx->dev;
-	const char *compatible;
-	struct device_node *np;
+	struct pcie_m2_bt_auxdev *bt;
 	int ret;
 
 	id = pci_match_id(pwrseq_m2_pci_ids, pdev);
 	if (WARN_ON_ONCE(!id)) /* Shouldn't happen */
 		return -ENODEV;
 
-	compatible = (const char *)id->driver_data;
-
-	pci_dev->ocs = kzalloc_obj(*pci_dev->ocs);
-	if (!pci_dev->ocs)
+	bt = kzalloc_obj(*bt);
+	if (!bt)
 		return -ENOMEM;
 
-	of_changeset_init(pci_dev->ocs);
+	ret = ida_alloc(&pwrseq_pcie_m2_bt_ida, GFP_KERNEL);
+	if (ret < 0)
+		goto err_free_bt;
 
-	np = of_changeset_create_node(pci_dev->ocs, parent, "bluetooth");
-	if (!np) {
-		dev_err(dev, "Failed to create bluetooth node\n");
-		ret = -ENODEV;
-		goto err_destroy_changeset;
-	}
+	bt->serdev = pci_dev->serdev;
+	bt->pwrseq_target = "uart";
+	bt->adev.id = ret;
+	bt->adev.name = (const char *)id->driver_data;
+	bt->adev.dev.parent = dev;
+	bt->adev.dev.release = pwrseq_pcie_m2_bt_release;
 
-	ret = of_changeset_add_prop_string(pci_dev->ocs, np, "compatible", compatible);
-	if (ret) {
-		dev_err(dev, "Failed to add bluetooth compatible: %d\n", ret);
-		goto err_destroy_changeset;
-	}
+	ret = auxiliary_device_init(&bt->adev);
+	if (ret)
+		goto err_free_ida;
 
-	ret = of_changeset_apply(pci_dev->ocs);
+	ret = auxiliary_device_add(&bt->adev);
 	if (ret) {
-		dev_err(dev, "Failed to apply changeset: %d\n", ret);
-		goto err_destroy_changeset;
+		dev_err(dev, "Failed to add bluetooth auxiliary device: %d\n",
+			ret);
+		auxiliary_device_uninit(&bt->adev);
+		return ret;
 	}
 
-	ret = device_add_of_node(&pci_dev->serdev->dev, np);
-	if (ret) {
-		dev_err(dev, "Failed to add OF node: %d\n", ret);
-		goto err_revert_changeset;
-	}
+	pci_dev->bt = bt;
 
 	return 0;
 
-err_revert_changeset:
-	of_changeset_revert(pci_dev->ocs);
-err_destroy_changeset:
-	of_changeset_destroy(pci_dev->ocs);
-	kfree(pci_dev->ocs);
-	pci_dev->ocs = NULL;
+err_free_ida:
+	ida_free(&pwrseq_pcie_m2_bt_ida, bt->adev.id);
+err_free_bt:
+	kfree(bt);
 
 	return ret;
 }
@@ -329,17 +347,17 @@ static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx,
 		goto err_free_pci_dev;
 	}
 
-	ret = pwrseq_pcie_m2_create_bt_node(ctx, pci_dev, serdev_parent, pdev);
-	if (ret)
-		goto err_free_serdev;
-
 	ret = serdev_device_add(pci_dev->serdev);
 	if (ret) {
 		dev_err(dev, "Failed to add serdev for PCI device (%s): %d\n",
 			pci_name(pdev), ret);
-		goto err_free_dt_node;
+		goto err_free_serdev;
 	}
 
+	ret = pwrseq_pcie_m2_create_bt_aux(ctx, pci_dev, pdev);
+	if (ret)
+		goto err_remove_serdev;
+
 	serdev_controller_put(serdev_ctrl);
 
 	pci_dev->pdev = pci_dev_get(pdev);
@@ -350,15 +368,11 @@ static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx,
 
 	return 0;
 
-err_free_dt_node:
-	device_remove_of_node(&pci_dev->serdev->dev);
-	of_changeset_revert(pci_dev->ocs);
-	of_changeset_destroy(pci_dev->ocs);
-	kfree(pci_dev->ocs);
-	pci_dev->ocs = NULL;
+err_remove_serdev:
+	serdev_device_remove(pci_dev->serdev);
+	goto err_free_pci_dev;
 err_free_serdev:
 	serdev_device_put(pci_dev->serdev);
-	pci_dev->serdev = NULL;
 err_free_pci_dev:
 	kfree(pci_dev);
 err_put_ctrl:
@@ -370,16 +384,13 @@ static int pwrseq_pcie_m2_create_serdev_one(struct pwrseq_pcie_m2_ctx *ctx,
 static void __pwrseq_pcie_m2_remove_serdev(struct pwrseq_pcie_m2_ctx *ctx,
 					   struct pwrseq_pci_dev *pci_dev)
 {
-	if (pci_dev->serdev) {
-		device_remove_of_node(&pci_dev->serdev->dev);
-		serdev_device_remove(pci_dev->serdev);
+	if (pci_dev->bt) {
+		auxiliary_device_delete(&pci_dev->bt->adev);
+		auxiliary_device_uninit(&pci_dev->bt->adev);
 	}
 
-	if (pci_dev->ocs) {
-		of_changeset_revert(pci_dev->ocs);
-		of_changeset_destroy(pci_dev->ocs);
-		kfree(pci_dev->ocs);
-	}
+	if (pci_dev->serdev)
+		serdev_device_remove(pci_dev->serdev);
 
 	pci_dev_put(pci_dev->pdev);
 	list_del(&pci_dev->list);
@@ -526,6 +537,7 @@ static int pwrseq_pcie_m2_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	platform_set_drvdata(pdev, ctx);
+	ctx->dev = dev;
 	ctx->of_node = dev_of_node(dev);
 	ctx->pdata = device_get_match_data(dev);
 	if (!ctx->pdata)
@@ -573,7 +585,6 @@ static int pwrseq_pcie_m2_probe(struct platform_device *pdev)
 
 	mutex_init(&ctx->list_lock);
 	INIT_LIST_HEAD(&ctx->pci_devices);
-	ctx->dev = dev;
 
 	/* Create serdev for available PCI devices (if required) */
 	ret = pwrseq_pcie_m2_create_serdev(ctx);

-- 
2.43.0



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

* [PATCH 4/5] Bluetooth: hci_qca: Drop serdev based M.2 power sequencing code
  2026-09-15 14:24 [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Manivannan Sadhasivam via B4 Relay
                   ` (2 preceding siblings ...)
  2026-09-15 14:24 ` [PATCH 3/5] power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface Manivannan Sadhasivam via B4 Relay
@ 2026-09-15 14:24 ` Manivannan Sadhasivam via B4 Relay
  2026-09-15 14:24 ` [PATCH 5/5] Bluetooth: btnxpuart: " Manivannan Sadhasivam via B4 Relay
  2026-09-18  9:48 ` [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Sherry Sun
  5 siblings, 0 replies; 10+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-09-15 14:24 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel
  Cc: linux-kernel, linux-bluetooth, linux-arm-msm, linux-pci,
	linux-pm, Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Manivannan Sadhasivam

From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>

Bluetooth interface in M.2 modules is now enumerated using an auxiliary
device instead of serdev. The power sequencing driver now allocates an
auxiliary device for the Bluetooth interface whenever a compatible
M.2 module shows up over PCIe. And the auxiliary device gets bind to this
hci_qca driver using the existing auxiliary driver matching logic.

Earlier the same modules were brought up by fabricating a bluetooth
node under the UART controller, so that the serdev bus matched this
driver, and qca_serdev_probe grabbed the "uart" sequencer when the M.2
connector was reachable over the OF graph. That node is no longer
created, so the serdev driver never binds for an M.2 module.

Hence, remove the serdev based M.2 power sequencing code.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
 drivers/bluetooth/hci_qca.c | 37 -------------------------------------
 1 file changed, 37 deletions(-)

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 15cc6cca9057..3a348f4153e7 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -26,7 +26,6 @@
 #include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_graph.h>
 #include <linux/acpi.h>
 #include <linux/platform_device.h>
 #include <linux/pwrseq/consumer.h>
@@ -2384,34 +2383,6 @@ static int qca_init_regulators(struct qca_serdev *qcadev,
 	return 0;
 }
 
-static void qca_serdev_put_pwrseq(void *data)
-{
-	pwrseq_put(data);
-}
-
-static int qca_serdev_get_m2_pwrseq(struct qca_serdev *qcadev)
-{
-	struct serdev_device *serdev = qcadev->serdev_hu.serdev;
-	struct pwrseq_desc *pwrseq;
-
-	if (!of_graph_is_present(dev_of_node(&serdev->ctrl->dev)))
-		return 0;
-
-	/* The pwrseq is looked up on the serdev controller (which holds the
-	 * OF graph to the M.2 connector), but its lifetime must follow this
-	 * serdev consumer device, not the controller. So acquire it with the
-	 * non-devres pwrseq_get() and release it via a devres action bound to
-	 * &serdev->dev instead of using devm_pwrseq_get(&serdev->ctrl->dev).
-	 */
-	pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
-	if (IS_ERR(pwrseq))
-		return PTR_ERR(pwrseq);
-
-	qcadev->pwrseq = pwrseq;
-
-	return devm_add_action_or_reset(&serdev->dev, qca_serdev_put_pwrseq, pwrseq);
-}
-
 static int qca_serdev_probe(struct serdev_device *serdev)
 {
 	struct qca_serdev *qcadev;
@@ -2442,10 +2413,6 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 	else
 		qcadev->btsoc_type = QCA_ROME;
 
-	err = qca_serdev_get_m2_pwrseq(qcadev);
-	if (err)
-		return err;
-
 	switch (qcadev->btsoc_type) {
 	case QCA_WCN3950:
 	case QCA_WCN3988:
@@ -2455,10 +2422,6 @@ static int qca_serdev_probe(struct serdev_device *serdev)
 	case QCA_WCN6750:
 	case QCA_WCN6855:
 	case QCA_WCN7850:
-		/* M.2 connector modules are powered by the pwrseq acquired above. */
-		if (qcadev->pwrseq)
-			break;
-
 		if (!device_property_present(&serdev->dev, "enable-gpios")) {
 			/*
 			 * Backward compatibility with old DT sources. If the

-- 
2.43.0



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

* [PATCH 5/5] Bluetooth: btnxpuart: Drop serdev based M.2 power sequencing code
  2026-09-15 14:24 [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Manivannan Sadhasivam via B4 Relay
                   ` (3 preceding siblings ...)
  2026-09-15 14:24 ` [PATCH 4/5] Bluetooth: hci_qca: Drop serdev based M.2 power sequencing code Manivannan Sadhasivam via B4 Relay
@ 2026-09-15 14:24 ` Manivannan Sadhasivam via B4 Relay
  2026-09-18  9:48 ` [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Sherry Sun
  5 siblings, 0 replies; 10+ messages in thread
From: Manivannan Sadhasivam via B4 Relay @ 2026-09-15 14:24 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel
  Cc: linux-kernel, linux-bluetooth, linux-arm-msm, linux-pci,
	linux-pm, Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Manivannan Sadhasivam

From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>

Bluetooth interface in M.2 modules is now enumerated using an auxiliary
device instead of serdev. The power sequencing driver now allocates an
auxiliary device for the Bluetooth interface whenever a compatible
M.2 module shows up over PCIe. And the auxiliary device gets bind to this
btnxpuart driver using the existing auxiliary driver matching logic.

Earlier the same modules were brought up by fabricating a bluetooth
node under the UART controller, so that the serdev bus matched this
driver, and nxp_serdev_probe grabbed the "uart" sequencer when the M.2
connector was reachable over the OF graph. That node is no longer
created, so the serdev driver never binds for an M.2 module.

Hence, remove the connector helper and the serdev power sequencing code.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
---
 drivers/bluetooth/btnxpuart.c | 48 +------------------------------------------
 1 file changed, 1 insertion(+), 47 deletions(-)

diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index 2672e481ef15..eaa82cc6444f 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -10,7 +10,6 @@
 #include <linux/auxiliary_bus.h>
 #include <linux/serdev.h>
 #include <linux/of.h>
-#include <linux/of_graph.h>
 #include <linux/pwrseq/consumer.h>
 #include <linux/pwrseq/pcie-m2-bt.h>
 #include <linux/skbuff.h>
@@ -1820,28 +1819,6 @@ static void nxp_coredump_notify(struct hci_dev *hdev, int state)
 	kobject_uevent_env(&serdev->dev.kobj, KOBJ_CHANGE, envp);
 }
 
-/*
- * Check if the remote M.2 connector device linked via OF graph is present
- * and available. This is used to determine whether the pwrseq path should
- * be taken. When the remote connector node is disabled (e.g., by a DT
- * overlay switching from PCIe WiFi to SDIO WiFi), the pwrseq path is
- * skipped, allowing the BT driver to use a direct bluetooth child node
- * instead.
- */
-static bool nxp_m2_connector_is_available(struct device *dev)
-{
-	struct device_node *ep __free(device_node) =
-		of_graph_get_next_endpoint(dev_of_node(dev), NULL);
-
-	if (!ep)
-		return false;
-
-	struct device_node *remote __free(device_node) =
-		of_graph_get_remote_port_parent(ep);
-
-	return remote && of_device_is_available(remote);
-}
-
 static int nxp_register_dev(struct btnxpuart_dev *nxpdev)
 {
 	struct serdev_device *serdev = nxpdev->serdev;
@@ -1962,30 +1939,7 @@ static int nxp_serdev_probe(struct serdev_device *serdev)
 		return err;
 	}
 
-	if (nxp_m2_connector_is_available(&serdev->ctrl->dev)) {
-		struct pwrseq_desc *pwrseq;
-
-		pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
-		if (IS_ERR(pwrseq))
-			return dev_err_probe(&serdev->dev, PTR_ERR(pwrseq),
-					     "failed to get pwrseq\n");
-
-		nxpdev->pwrseq = pwrseq;
-		err = pwrseq_enable(pwrseq);
-		if (err)
-			goto err_pwrseq_put;
-	}
-
-	err = nxp_register_dev(nxpdev);
-	if (err)
-		goto err_pwrseq_put;
-
-	return 0;
-
-err_pwrseq_put:
-	if (nxpdev->pwrseq)
-		pwrseq_put(nxpdev->pwrseq);
-	return err;
+	return nxp_register_dev(nxpdev);
 }
 
 static void __nxp_remove(struct btnxpuart_dev *nxpdev)

-- 
2.43.0



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

* Re: [PATCH 2/5] Bluetooth: btnxpuart: Add auxiliary driver for PCIe M.2 modules
  2026-09-15 14:24 ` [PATCH 2/5] Bluetooth: btnxpuart: " Manivannan Sadhasivam via B4 Relay
@ 2026-09-15 14:51   ` Andy Shevchenko
  0 siblings, 0 replies; 10+ messages in thread
From: Andy Shevchenko @ 2026-09-15 14:51 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel, linux-kernel, linux-bluetooth, linux-arm-msm,
	linux-pci, linux-pm, Rahul Samana, Wei Deng, Loic Poulain

On Tue, Sep 15, 2026 at 04:24:03PM +0200, Manivannan Sadhasivam via B4 Relay wrote:

> The 88W8987 combo module exposes Bluetooth over UART. Since this module is
> attached over PCIe, it is not described in firmware like devicetree. So it
> is discovered at runtime over PCIe by the power sequencing driver, which
> allocates the UART serdev and creates an auxiliary device carrying that
> transport and the power sequencing target to power up the Bluetooth
> function.
> 
> Add an auxiliary driver that binds to this device. It reuses the serdev
> provided by the producer, brings up the controller through the existing
> UART transport with nxp_register_dev() and drives power through the
> sequencer obtained with pwrseq_get().
> 
> Factor the HCI device setup and teardown shared with the serdev probe into
> helpers so both paths register the same controller.

...

> -static int nxp_serdev_probe(struct serdev_device *serdev)
> +static int nxp_register_dev(struct btnxpuart_dev *nxpdev)
>  {
> +	struct serdev_device *serdev = nxpdev->serdev;

Why not also

	struct device *dev = &serdev->dev;

and use it to make lines even shorter?

...

> -	device_property_read_u32(&nxpdev->serdev->dev, "max-speed",
> +	device_property_read_u32(&serdev->dev, "max-speed",
>  				 &nxpdev->secondary_baudrate);

	device_property_read_u32(dev, "max-speed", &nxpdev->secondary_baudrate);

Now exactly a single line (out of 80 characters).

...

> -	device_property_read_u8_array(&nxpdev->serdev->dev,
> +	device_property_read_u8_array(&serdev->dev,
>  				      "local-bd-address",
>  				      (u8 *)&ba, sizeof(ba));

	device_property_read_u8_array(dev, "local-bd-address",
				      (u8 *)&ba, sizeof(ba));

Now two lines instead of three. But ideally this casting should gone and
instead something like ether_addr_copy() to be used. (The latter is out
of scope here, of course.)

...

> +static int nxp_serdev_probe(struct serdev_device *serdev)
> +{
> +	struct btnxpuart_dev *nxpdev;
> +	int err;
> +
> +	nxpdev = devm_kzalloc(&serdev->dev, sizeof(*nxpdev), GFP_KERNEL);
> +	if (!nxpdev)
> +		return -ENOMEM;

> +	nxpdev->nxp_data = (struct btnxpuart_data *)device_get_match_data(&serdev->dev);

This is bad. The const qualifier is for a reason. Make sure it's kept.
On top the same suggestion as per above, use local 'dev' pointer.

Also Sashiko found a nice issue with the driver data, id est driver_override
mechanism that in some cases may lead to NULL dereferencing. Please, double
check if it's not the case, otherwise check for NULL and return -ENODATA.

> +	nxpdev->serdev = serdev;
> +
> +	nxpdev->pdn = devm_reset_control_get_optional_shared(&serdev->dev, NULL);
> +	if (IS_ERR(nxpdev->pdn))
> +		return PTR_ERR(nxpdev->pdn);
> +
> +	err = devm_regulator_get_enable(&serdev->dev, "vcc");
> +	if (err) {
> +		dev_err(&serdev->dev, "Failed to enable vcc regulator\n");
> +		return err;
> +	}
> +
> +	if (nxp_m2_connector_is_available(&serdev->ctrl->dev)) {
> +		struct pwrseq_desc *pwrseq;
> +
> +		pwrseq = pwrseq_get(&serdev->ctrl->dev, "uart");
> +		if (IS_ERR(pwrseq))
> +			return dev_err_probe(&serdev->dev, PTR_ERR(pwrseq),
> +					     "failed to get pwrseq\n");
> +
> +		nxpdev->pwrseq = pwrseq;
> +		err = pwrseq_enable(pwrseq);
> +		if (err)
> +			goto err_pwrseq_put;
> +	}
> +
> +	err = nxp_register_dev(nxpdev);
> +	if (err)
> +		goto err_pwrseq_put;
> +
> +	return 0;
> +
>  err_pwrseq_put:
>  	if (nxpdev->pwrseq)
>  		pwrseq_put(nxpdev->pwrseq);
>  	return err;
>  }

-- 
With Best Regards,
Andy Shevchenko



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

* Re: [PATCH 3/5] power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface
  2026-09-15 14:24 ` [PATCH 3/5] power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface Manivannan Sadhasivam via B4 Relay
@ 2026-09-16  9:19   ` Bartosz Golaszewski
  0 siblings, 0 replies; 10+ messages in thread
From: Bartosz Golaszewski @ 2026-09-16  9:19 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: Manivannan Sadhasivam via B4 Relay, linux-kernel,
	linux-bluetooth, linux-arm-msm, linux-pci, linux-pm,
	Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel

On Tue, 15 Sep 2026 16:24:04 +0200, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> Currently, the driver instantiates the BT interface of the WCN6855 and
> WCN7850 combo modules by creating a devicetree node at runtime using an
> of_changeset, with the compatible passed through the PCI ID table. But this
> method of creating a devicetree node just for matching with the driver is
> not recommended.
>
> So create an auxiliary device for the BT interface instead. The serdev
> transport is still allocated here and handed over to the Bluetooth driver
> through the auxiliary device, along with the power sequencing target used
> to power up the controller. Also extend the provider match so that the
> auxiliary device created for this connector is matched as its power
> sequencing consumer.
>
> Since the runtime node creation is now gone, select AUXILIARY_BUS in
> place of OF_DYNAMIC.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---

That's some nice stuff, thanks for doing it!

It looks like there's no build-time dependency on this, only run-time? Can I
take it independently?

Bart

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

* Re: [PATCH 1/5] Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules
  2026-09-15 14:24 ` [PATCH 1/5] Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules Manivannan Sadhasivam via B4 Relay
@ 2026-09-16  9:39   ` Bartosz Golaszewski
  0 siblings, 0 replies; 10+ messages in thread
From: Bartosz Golaszewski @ 2026-09-16  9:39 UTC (permalink / raw)
  To: manivannan.sadhasivam
  Cc: linux-kernel, linux-bluetooth, linux-arm-msm, linux-pci,
	linux-pm, Rahul Samana, Wei Deng, Loic Poulain, Andy Shevchenko,
	Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel, Manivannan Sadhasivam via B4 Relay

On Tue, 15 Sep 2026 16:24:02 +0200, Manivannan Sadhasivam via B4 Relay
<devnull+manivannan.sadhasivam.oss.qualcomm.com@kernel.org> said:
> From: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>
> The QCA2066, WCN6855 and WCN7850 combo modules expose Bluetooth over
> UART. Since these modules are attached over PCIe, these are not described
> in firmware like devicetree. So these modules are discovered at runtime
> over PCIe by the power sequencing driver, which allocates the UART serdev
> and creates an auxiliary device carrying that transport and the power
> sequencing target to power up the Bluetooth function.
>
> Add an auxiliary driver that binds to this device. It reuses the serdev
> provided by the producer, brings up the controller through the existing
> UART transport with hci_uart_register_device() and drives power through
> the sequencer obtained with devm_pwrseq_get().
>
> Add a shared header describing the auxiliary device so that the power
> sequencing driver and hci_qca agree on its layout.
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
> ---

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

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

* Re: [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus
  2026-09-15 14:24 [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Manivannan Sadhasivam via B4 Relay
                   ` (4 preceding siblings ...)
  2026-09-15 14:24 ` [PATCH 5/5] Bluetooth: btnxpuart: " Manivannan Sadhasivam via B4 Relay
@ 2026-09-18  9:48 ` Sherry Sun
  5 siblings, 0 replies; 10+ messages in thread
From: Sherry Sun @ 2026-09-18  9:48 UTC (permalink / raw)
  To: Manivannan Sadhasivam
  Cc: Marcel Holtmann, Luiz Augusto von Dentz, Bartosz Golaszewski,
	Manivannan Sadhasivam, Amitkumar Karwar, Neeraj Kale,
	Philipp Zabel, linux-kernel, linux-bluetooth, linux-arm-msm,
	linux-pci, linux-pm, Rahul Samana, Wei Deng, Loic Poulain,
	Andy Shevchenko

On Tue, Sep 15, 2026 at 04:24:01PM +0200, Manivannan Sadhasivam wrote:
>Hi,
>
>This series reworks the PCIe M.2 Bluetooth instantiation using the auxiliary
>bus.
>
>The Bluetooth interface of the QCA2066, WCN6855, WCN7850 and NXP 88W8987
>M.2 modules is accessed over UART, but the modules show up at runtime over
>PCIe and are not described in firmware like devicetree. To make the serdev
>bus match the Bluetooth driver, the power sequencing driver was creating a
>devicetree node at runtime using an of_changeset. But, creating a node just for
>driver matching is not recommended as per Krzysztof [1].
>
>So this series reworks the instantiation to use the auxiliary bus instead. The
>power sequencing driver still allocates the UART serdev, but now hands it over
>through an auxiliary device along with the power sequencing target. So this
>series adds auxiliary driver support to hci_qca and btnxpuart drivers binding to
>this device and brings up the controller over the existing UART transport.
>Finally, it drops the serdev based M.2 code from both BT drivers.
>
>Dependency
>==========
>
>This series depends on Monza series from Loic [2] and reuses its
>pwrseq_is_controllable() API.
>
>[1] https://lore.kernel.org/linux-pci/768142f0-2638-41f3-a34f-dc80bb99eb7f@kernel.org
>[2] https://lore.kernel.org/all/20260904-monza-wireless-v6-0-d8c5042b3efd@oss.qualcomm.com
>

For the NXP 88W8987 M.2 module related changes:
Tested-by: Sherry Sun <sherry.sun@nxp.com>

Best Regards
Sherry Sun

>Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
>---
>Manivannan Sadhasivam (5):
>      Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules
>      Bluetooth: btnxpuart: Add auxiliary driver for PCIe M.2 modules
>      power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface
>      Bluetooth: hci_qca: Drop serdev based M.2 power sequencing code
>      Bluetooth: btnxpuart: Drop serdev based M.2 power sequencing code
>
> MAINTAINERS                               |   1 +
> drivers/bluetooth/Kconfig                 |   2 +
> drivers/bluetooth/btnxpuart.c             | 193 +++++++++++++++++++-----------
> drivers/bluetooth/hci_qca.c               | 138 +++++++++++++++------
> drivers/power/sequencing/Kconfig          |   2 +-
> drivers/power/sequencing/pwrseq-pcie-m2.c | 137 +++++++++++----------
> include/linux/pwrseq/pcie-m2-bt.h         |  38 ++++++
> 7 files changed, 343 insertions(+), 168 deletions(-)
>---
>base-commit: e5e04726cdd043e309677071ab1b65a4b18f422b
>change-id: 20260806-pci-m2-bt-rework-78dc2c926f00
>prerequisite-message-id: 20260904-monza-wireless-v6-0-d8c5042b3efd@oss.qualcomm.com
>prerequisite-patch-id: c4e777757ca9358cc4dd35e23738cd7dc1ead1a0
>prerequisite-patch-id: 09600595c2e80b12eda3aae39af192847d0f03d0
>prerequisite-patch-id: afe88432c4d459e9094c0e6dd31ebeab6c9f2f08
>prerequisite-patch-id: 60fd7b13447ccad7546fa8e5b5609d8f133f788f
>prerequisite-patch-id: 61dcbe55fdb6f33a3229c197f16a678785aa16d8
>prerequisite-patch-id: 14099e5c573d7c41b899c3f9bb9126aa23f1553e
>prerequisite-patch-id: 647ee15b77671054b4bb012afa9ba8694b6c86c5
>prerequisite-patch-id: c3a774cae81a6fad287731a38f1b76c7e8babc4c
>prerequisite-patch-id: 18b0fa62060a1a2c96e76051944d692699035572
>prerequisite-patch-id: df02ab167976ac1f0d5a38c78e559a7372b16c1f
>prerequisite-patch-id: 3f2dddd108677252a539cdb1eb661912836289ca
>
>Best regards,
>--
>மணிவண்ணன் சதாசிவம்
>

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

end of thread, other threads:[~2026-09-18  9:45 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-15 14:24 [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Manivannan Sadhasivam via B4 Relay
2026-09-15 14:24 ` [PATCH 1/5] Bluetooth: hci_qca: Add auxiliary driver for PCIe M.2 modules Manivannan Sadhasivam via B4 Relay
2026-09-16  9:39   ` Bartosz Golaszewski
2026-09-15 14:24 ` [PATCH 2/5] Bluetooth: btnxpuart: " Manivannan Sadhasivam via B4 Relay
2026-09-15 14:51   ` Andy Shevchenko
2026-09-15 14:24 ` [PATCH 3/5] power: sequencing: pcie-m2: Create auxiliary device for the M.2 BT interface Manivannan Sadhasivam via B4 Relay
2026-09-16  9:19   ` Bartosz Golaszewski
2026-09-15 14:24 ` [PATCH 4/5] Bluetooth: hci_qca: Drop serdev based M.2 power sequencing code Manivannan Sadhasivam via B4 Relay
2026-09-15 14:24 ` [PATCH 5/5] Bluetooth: btnxpuart: " Manivannan Sadhasivam via B4 Relay
2026-09-18  9:48 ` [PATCH 0/5] Rework M.2 Bluetooth instantiation using the auxiliary bus Sherry Sun

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

all inboxes | Powered by JetHome®