Devicetree
 help / color / mirror / Atom feed
* [PATCH 0/6] Add support for IPQ5018 Bluetooth
@ 2026-06-25 14:10 George Moussalem via B4 Relay
  2026-06-25 14:10 ` [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL George Moussalem via B4 Relay
                   ` (5 more replies)
  0 siblings, 6 replies; 22+ messages in thread
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

Hello,

This patch series introduces Bluetooth support for IPQ5018.

Bluetooth firmware on the IPQ5018 platform runs on the M0 co-processor.
The firmware is loaded by the host into a dedicated reserved memory
carveout and authenticated by TrustZone. A Secure Channel Manager (SCM)
call safely brings the peripheral core out of reset.

A shared memory ring buffer topology handles runtime data frame
transport between the host APSS and the M0 core. An outgoing APCS IPC
bit and an incoming GIC interrupt handle host/guest signaling.

This series has been tested and verified on various IPQ5018 router
boards utilizing firmware extracted from GPL distributions, using both
mdt and mbn file formats.

[   14.781511] Bluetooth: hci0: QCA Product ID   :0x00000016
[   14.781583] Bluetooth: hci0: QCA SOC Version  :0x20180100
[   14.785926] Bluetooth: hci0: QCA ROM Version  :0x00000100
[   14.791546] Bluetooth: hci0: QCA Patch Version:0x00003ded
[   14.796698] Bluetooth: hci0: QCA controller version 0x01000100
[   14.802217] Bluetooth: hci0: QCA Downloading qca/mpnv10.bin
[   16.393850] Bluetooth: hci0: QCA Build Info: BTFW.MAPLE.1.0.0-00102-MPL_ROM_PATCHZ-1

Best regards,
George Moussalem

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
George Moussalem (6):
      dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
      remoteproc: qcom: Add M0 BTSS secure PIL driver
      Bluetooth: btqca: Add IPQ5018 support
      dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
      Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
      arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support

 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    |  63 ++
 .../bindings/remoteproc/qcom,m0-btss-pil.yaml      |  72 ++
 arch/arm64/boot/dts/qcom/ipq5018.dtsi              |  34 +-
 drivers/bluetooth/Kconfig                          |  11 +
 drivers/bluetooth/Makefile                         |   1 +
 drivers/bluetooth/btqca.c                          |  16 +
 drivers/bluetooth/btqca.h                          |   3 +
 drivers/bluetooth/btqcomipc.c                      | 939 +++++++++++++++++++++
 drivers/remoteproc/Kconfig                         |  12 +
 drivers/remoteproc/Makefile                        |   1 +
 drivers/remoteproc/qcom_m0_btss_pil.c              | 261 ++++++
 11 files changed, 1412 insertions(+), 1 deletion(-)
---
base-commit: 4d1ab324fcb7d20df5a071edb0304461846fdc12
change-id: 20260625-ipq5018-bluetooth-06ff66c9d753
prerequisite-message-id: <20260612-block-as-nvmem-v5-0-95e0b30fff90@oss.qualcomm.com>
prerequisite-patch-id: 6ce8686c1683f468d86b4502f5ec9d19c392a382
prerequisite-patch-id: e362f7fcbacff716b7ef720e6780786a7d88c013
prerequisite-patch-id: 9168930e40551e842c8171d5433a6f39ad4b78a4
prerequisite-patch-id: 64fecfbd1e085d7d2ab0ae23295ca34ec8e14c5e
prerequisite-patch-id: 566804aaa690ee9aa285d0fd75fd16d94fbadebf
prerequisite-patch-id: dc18bec338f54b3051f4523f9d1d3c0566a20ccd
prerequisite-patch-id: b6b3eb46429936ab49423d295433daf47981db0f
prerequisite-patch-id: 75caa99e3bbcdf41b6462b9f5f703bea1d4a65fa
prerequisite-patch-id: 35e9968f482f78ca233eb0306d9c5fdbff093175

Best regards,
-- 
George Moussalem <george.moussalem@outlook.com>



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

* [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
  2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
@ 2026-06-25 14:10 ` George Moussalem via B4 Relay
  2026-06-26 10:47   ` Krzysztof Kozlowski
  2026-06-25 14:10 ` [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver George Moussalem via B4 Relay
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 22+ messages in thread
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

From: George Moussalem <george.moussalem@outlook.com>

Document the M0 Bluetooth Subsystem remote processor core found in the
Qualcomm IPQ5018 SoC. Firmware loaded is authenticated via TrustZone.
The firmware running on the M0 core provides bluetooth functionality.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 .../bindings/remoteproc/qcom,m0-btss-pil.yaml      | 72 ++++++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
new file mode 100644
index 000000000000..397bb6815d71
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/remoteproc/qcom,m0-btss-pil.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm M0 BTSS Peripheral Image Loader
+
+maintainers:
+  - George Moussalem <george.moussalem@outlook.com>
+
+description:
+  Qualcomm M0 BTSS Peripheral Secure Image Loader loads firmware and powers up
+  the M0 BTSS remote processor core on the Qualcomm IPQ5018 SoC.
+
+properties:
+  compatible:
+    enum:
+      - qcom,ipq5018-btss-pil
+
+  firmware-name:
+    maxItems: 1
+    description: Firmware name for the M0 Bluetooth Subsystem core
+
+  clocks:
+    items:
+      - description: M0 BTSS low power oscillator clock
+
+  clock-names:
+    items:
+      - const: btss_lpo_clk
+
+  memory-region:
+    items:
+      - description: M0 BTSS reserved memory carveout
+
+  resets:
+    items:
+      - description: M0 BTSS reset
+
+  reset-names:
+    items:
+      - const: btss_reset
+
+required:
+  - compatible
+  - firmware-name
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - memory-region
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-ipq5018.h>
+    #include <dt-bindings/reset/qcom,gcc-ipq5018.h>
+
+    remoteproc {
+      compatible = "qcom,ipq5018-btss-pil";
+
+      firmware-name = "qca/bt_fw_patch.mbn";
+
+      clocks = <&gcc GCC_BTSS_LPO_CLK>;
+      clock-names = "btss_lpo_clk";
+      resets = <&gcc GCC_BTSS_BCR>;
+      reset-names = "btss_reset";
+
+      memory-region = <&btss_region>;
+    };

-- 
2.53.0



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

* [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
  2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
  2026-06-25 14:10 ` [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL George Moussalem via B4 Relay
@ 2026-06-25 14:10 ` George Moussalem via B4 Relay
  2026-06-25 14:18   ` Philipp Zabel
                     ` (2 more replies)
  2026-06-25 14:10 ` [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
                   ` (3 subsequent siblings)
  5 siblings, 3 replies; 22+ messages in thread
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

From: George Moussalem <george.moussalem@outlook.com>

Add support to bring up the M0 core of the bluetooth subsystem found in
the IPQ5018 SoC.

The signed firmware loaded is authenticated by TrustZone. If successful,
the M0 core boots the firmware and the peripheral is taken out of reset
using a Secure Channel Manager call to TrustZone.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 drivers/remoteproc/Kconfig            |  12 ++
 drivers/remoteproc/Makefile           |   1 +
 drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
 3 files changed, 274 insertions(+)

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index c521c744e7db..6b52f78f1427 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -163,6 +163,18 @@ config PRU_REMOTEPROC
 	  processors on various TI SoCs. It's safe to say N here if you're
 	  not interested in the PRU or if you are unsure.
 
+config QCOM_M0_BTSS_PIL
+	tristate "Qualcomm M0 BTSS Peripheral Image Loader"
+	depends on OF && ARCH_QCOM
+	select QCOM_MDT_LOADER
+	select QCOM_RPROC_COMMON
+	select QCOM_SCM
+	help
+	  Say y here to support the Secure Peripheral Imager Loader for the
+	  Qualcomm Bluetooth Subsystem running on the M0 remote processor found
+	  in the IPQ5018 SoC. The M0 core is started and stopped using a
+	  Secure Channel Manager call to TrustZone.
+
 config QCOM_PIL_INFO
 	tristate
 
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 1c7598b8475d..df80faf8d0df 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_DA8XX_REMOTEPROC)		+= da8xx_remoteproc.o
 obj-$(CONFIG_KEYSTONE_REMOTEPROC)	+= keystone_remoteproc.o
 obj-$(CONFIG_MESON_MX_AO_ARC_REMOTEPROC)+= meson_mx_ao_arc.o
 obj-$(CONFIG_PRU_REMOTEPROC)		+= pru_rproc.o
+obj-$(CONFIG_QCOM_M0_BTSS_PIL)		+= qcom_m0_btss_pil.o
 obj-$(CONFIG_QCOM_PIL_INFO)		+= qcom_pil_info.o
 obj-$(CONFIG_QCOM_RPROC_COMMON)		+= qcom_common.o
 obj-$(CONFIG_QCOM_Q6V5_COMMON)		+= qcom_q6v5.o
diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
new file mode 100644
index 000000000000..7168e270e4d4
--- /dev/null
+++ b/drivers/remoteproc/qcom_m0_btss_pil.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+#include "qcom_common.h"
+
+#define BTSS_PAS_ID	0xc
+
+struct m0_btss {
+	struct device *dev;
+	phys_addr_t mem_phys;
+	phys_addr_t mem_reloc;
+	void __iomem *mem_region;
+	size_t mem_size;
+	struct reset_control *btss_reset;
+};
+
+static int m0_btss_start(struct rproc *rproc)
+{
+	int ret;
+
+	if (!qcom_scm_pas_supported(BTSS_PAS_ID)) {
+		dev_err(rproc->dev.parent,
+			"PAS is not available for peripheral: 0x%x\n",
+			BTSS_PAS_ID);
+		return -ENODEV;
+	}
+
+	ret = qcom_scm_pas_auth_and_reset(BTSS_PAS_ID);
+	if (ret) {
+		dev_err(rproc->dev.parent, "Failed to start rproc: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int m0_btss_stop(struct rproc *rproc)
+{
+	int ret;
+
+	if (rproc->state == RPROC_RUNNING || rproc->state == RPROC_CRASHED) {
+		ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
+		if (ret) {
+			dev_err(rproc->dev.parent, "Failed to stop rproc: %d\n",
+				ret);
+			return ret;
+		}
+
+		dev_info(rproc->dev.parent, "Successfully stopped rproc\n");
+	}
+
+	return 0;
+}
+
+static int m0_btss_load(struct rproc *rproc, const struct firmware *fw)
+{
+	struct m0_btss *desc = rproc->priv;
+	const struct elf32_phdr *phdrs;
+	const struct firmware *seg_fw;
+	const struct elf32_phdr *phdr;
+	const struct elf32_hdr *ehdr;
+	void __iomem *metadata;
+	size_t metadata_size;
+	int i, ret;
+
+	ehdr = (const struct elf32_hdr *)fw->data;
+	phdrs = (const struct elf32_phdr *)(ehdr + 1);
+
+	ret = request_firmware(&fw, rproc->firmware, rproc->dev.parent);
+	if (ret) {
+		dev_err(rproc->dev.parent, "Failed to request firmware: %d\n",
+			ret);
+		return ret;
+	}
+
+	metadata = qcom_mdt_read_metadata(fw, &metadata_size, rproc->firmware,
+					  rproc->dev.parent);
+	if (IS_ERR(metadata)) {
+		ret = PTR_ERR(metadata);
+		dev_err(rproc->dev.parent,
+			"Failed to read firmware metadata: %d\n", ret);
+		goto release_fw;
+	}
+
+	ret = qcom_scm_pas_init_image(BTSS_PAS_ID, metadata,
+				      metadata_size, NULL);
+	if (ret) {
+		dev_err(rproc->dev.parent, "PAS init image failed: %d\n", ret);
+		goto free_metadata;
+	}
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		char *seg_name __free(kfree) = kstrdup(rproc->firmware,
+						       GFP_KERNEL);
+		if (!seg_name)
+			return -ENOMEM;
+
+		phdr = &phdrs[i];
+
+		/* Only process valid loadable data segments */
+		if (phdr->p_type != PT_LOAD || !phdr->p_memsz)
+			continue;
+
+		if (phdr->p_vaddr + phdr->p_filesz > desc->mem_size) {
+			dev_err(rproc->dev.parent,
+				"Segment data exceeds the reserved memory area!\n");
+			goto free_metadata;
+		}
+
+		/* Check if firmware is split across multiple segment files */
+		if (phdr->p_offset > fw->size ||
+		    phdr->p_offset + phdr->p_filesz > fw->size) {
+			sprintf(seg_name + strlen(seg_name) - 3, "b%02d", i);
+			ret = request_firmware(&seg_fw, seg_name,
+					       rproc->dev.parent);
+			if (ret) {
+				dev_err(rproc->dev.parent,
+					"Could not find split segment binary: %s\n",
+					seg_name);
+				goto free_metadata;
+			}
+
+			/*
+			 * Use the virtual instead of the physical address as
+			 * the offset
+			 */
+			memcpy_toio(desc->mem_region + phdr->p_vaddr,
+				    seg_fw->data, phdr->p_filesz);
+
+			release_firmware(seg_fw);
+		} else {
+			memcpy_toio(desc->mem_region + phdr->p_vaddr,
+				    fw->data + phdr->p_offset, phdr->p_filesz);
+		}
+	}
+
+	return 0;
+
+free_metadata:
+	kfree(metadata);
+release_fw:
+	release_firmware(fw);
+	return ret;
+}
+
+static const struct rproc_ops m0_btss_ops = {
+	.start = m0_btss_start,
+	.stop = m0_btss_stop,
+	.load = m0_btss_load,
+	.get_boot_addr = rproc_elf_get_boot_addr,
+};
+
+static int m0_btss_alloc_memory_region(struct m0_btss *desc)
+{
+	struct device *dev = desc->dev;
+	struct resource res;
+	int ret;
+
+	ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+	if (ret) {
+		dev_err(dev, "unable to acquire memory-region resource\n");
+		return ret;
+	}
+
+	desc->mem_phys = res.start;
+	desc->mem_reloc = res.start;
+	desc->mem_size = resource_size(&res);
+	desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
+	if (!desc->mem_region) {
+		dev_err(dev, "unable to map memory region: %pR\n", &res);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int m0_btss_pil_probe(struct platform_device *pdev)
+{
+	// struct reset_control *btss_reset;
+	struct device *dev = &pdev->dev;
+	const char *fw_name = NULL;
+	struct m0_btss *desc;
+	struct clk *lpo_clk;
+	struct rproc *rproc;
+	int ret;
+
+	ret = of_property_read_string(dev->of_node, "firmware-name",
+				      &fw_name);
+	if (ret < 0)
+		return ret;
+
+	rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
+				 fw_name, sizeof(*desc));
+	if (!rproc) {
+		dev_err(dev, "failed to allocate rproc\n");
+		return -ENOMEM;
+	}
+
+	desc = rproc->priv;
+	desc->dev = dev;
+
+	ret = m0_btss_alloc_memory_region(desc);
+	if (ret)
+		return ret;
+
+	lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
+	if (IS_ERR(lpo_clk))
+		return dev_err_probe(dev, PTR_ERR(lpo_clk),
+				     "Failed to get lpo clock\n");
+
+	desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
+	if (IS_ERR_OR_NULL(desc->btss_reset))
+		return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
+				     "unable to acquire btss_reset\n");
+
+	ret = reset_control_deassert(desc->btss_reset);
+	if (ret)
+		return dev_err_probe(rproc->dev.parent, ret,
+				     "Failed to deassert reset\n");
+
+	rproc->auto_boot = false;
+	ret = devm_rproc_add(dev, rproc);
+	if (ret)
+		return ret;
+
+	platform_set_drvdata(pdev, rproc);
+
+	return 0;
+}
+
+static const struct of_device_id m0_btss_of_match[] = {
+	{ .compatible = "qcom,ipq5018-btss-pil" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, m0_btss_of_match);
+
+static struct platform_driver m0_btss_pil_driver = {
+	.probe = m0_btss_pil_probe,
+	.driver = {
+		.name = "qcom-m0-btss-pil",
+		.of_match_table = m0_btss_of_match,
+	},
+};
+
+module_platform_driver(m0_btss_pil_driver);
+
+MODULE_DESCRIPTION("Qualcomm M0 Bluetooth Subsystem Peripheral Image Loader");
+MODULE_LICENSE("GPL");

-- 
2.53.0



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

* [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support
  2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
  2026-06-25 14:10 ` [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL George Moussalem via B4 Relay
  2026-06-25 14:10 ` [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver George Moussalem via B4 Relay
@ 2026-06-25 14:10 ` George Moussalem via B4 Relay
  2026-06-26 14:10   ` sashiko-bot
  2026-06-25 14:10 ` [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 22+ messages in thread
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

From: George Moussalem <george.moussalem@outlook.com>

Add the IPQ5018 SoC type and support for loading its firmware.

The firmware tested has been taken from GPL sources of various router
boards. Firmware files needed are:
- qca/bt_fw_patch.mbn
- qca/mpnv10.bin

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 drivers/bluetooth/btqca.c | 16 ++++++++++++++++
 drivers/bluetooth/btqca.h |  3 +++
 2 files changed, 19 insertions(+)

diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 04ebe290bc78..e136e91976cf 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -380,6 +380,9 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
 		break;
 
 	case TLV_TYPE_NVM:
+		if (soc_type == QCA_IPQ5018)
+			break;
+
 		if (fw_size < sizeof(struct tlv_type_hdr))
 			return -EINVAL;
 
@@ -794,6 +797,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	else
 		rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f);
 
+	if (soc_type == QCA_IPQ5018)
+		goto download_nvm;
+
 	if (soc_type == QCA_WCN6750)
 		qca_send_patch_config_cmd(hdev);
 
@@ -881,6 +887,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	if (soc_type == QCA_QCA2066 || soc_type == QCA_WCN7850)
 		qca_read_fw_board_id(hdev, &boardid);
 
+download_nvm:
 	/* Download NVM configuration */
 	config.type = TLV_TYPE_NVM;
 	if (firmware_name) {
@@ -939,6 +946,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 			qca_get_nvm_name_by_board(config.fwname, sizeof(config.fwname),
 				 "hmtnv", soc_type, ver, rom_ver, boardid);
 			break;
+		case QCA_IPQ5018:
+			snprintf(config.fwname, sizeof(config.fwname),
+				 "qca/mpnv%02x.bin", rom_ver);
+			break;
 		default:
 			snprintf(config.fwname, sizeof(config.fwname),
 				 "qca/nvm_%08x.bin", soc_ver);
@@ -958,6 +969,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 		return err;
 	}
 
+	if (soc_type == QCA_IPQ5018)
+		msleep(NVM_READY_DELAY_MS);
+
 	switch (soc_type) {
 	case QCA_QCA2066:
 	case QCA_QCA6390:
@@ -965,6 +979,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	case QCA_WCN6750:
 	case QCA_WCN6855:
 	case QCA_WCN7850:
+	case QCA_IPQ5018:
 		err = qca_disable_soc_logging(hdev);
 		if (err < 0)
 			return err;
@@ -1001,6 +1016,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	case QCA_WCN6750:
 	case QCA_WCN6855:
 	case QCA_WCN7850:
+	case QCA_IPQ5018:
 		/* get fw build info */
 		err = qca_read_fw_build_info(hdev);
 		if (err < 0)
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 8f3c1b1c77b3..343cd62d1137 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -54,6 +54,8 @@
 #define QCA_HSP_GF_SOC_ID		0x1200
 #define QCA_HSP_GF_SOC_MASK		0x0000ff00
 
+#define NVM_READY_DELAY_MS		1500
+
 enum qca_baudrate {
 	QCA_BAUDRATE_115200	= 0,
 	QCA_BAUDRATE_57600,
@@ -158,6 +160,7 @@ enum qca_btsoc_type {
 	QCA_WCN6750,
 	QCA_WCN6855,
 	QCA_WCN7850,
+	QCA_IPQ5018,
 };
 
 #if IS_ENABLED(CONFIG_BT_QCA)

-- 
2.53.0



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

* [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (2 preceding siblings ...)
  2026-06-25 14:10 ` [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
@ 2026-06-25 14:10 ` George Moussalem via B4 Relay
  2026-06-26 10:53   ` Krzysztof Kozlowski
  2026-06-26 14:10   ` sashiko-bot
  2026-06-25 14:10 ` [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
  2026-06-25 14:10 ` [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
  5 siblings, 2 replies; 22+ messages in thread
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

From: George Moussalem <george.moussalem@outlook.com>

Document the Qualcomm IPQ5018 Bluetooth controller.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    | 63 ++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
new file mode 100644
index 000000000000..afd33f851858
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/bluetooth/qcom,ipq5018-bt.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm IPQ5018 Bluetooth
+
+maintainers:
+  - George Moussalem <george.moussalem@outlook.com>
+
+properties:
+  compatible:
+    enum:
+      - qcom,ipq5018-bt
+
+  interrupts:
+    items:
+      - description:
+          Interrupt line from the M0 Bluetooth Subsystem to the host processor
+          to notify it of events such as re
+
+  qcom,ipc:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: phandle to a syscon node representing the APCS registers
+          - description: u32 representing offset to the register within the syscon
+          - description: u32 representing the ipc bit within the register
+    description: |
+      These entries specify the outgoing IPC bit used for signaling the remote
+      M0 BTSS core of a host event or for sending an ACK if the remote processor
+      expects it.
+
+  qcom,rproc:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the remote processor node representing the M0 BTSS core.
+
+required:
+  - compatible
+  - interrupts
+  - qcom,ipc
+  - qcom,rproc
+
+allOf:
+  - $ref: bluetooth-controller.yaml#
+  - $ref: qcom,bluetooth-common.yaml
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    bluetooth: bluetooth {
+      compatible = "qcom,ipq5018-bt";
+
+      qcom,ipc = <&apcs_glb 8 23>;
+      interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+      qcom,rproc = <&m0_btss>;
+    };

-- 
2.53.0



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

* [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
  2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (3 preceding siblings ...)
  2026-06-25 14:10 ` [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
@ 2026-06-25 14:10 ` George Moussalem via B4 Relay
  2026-06-26 14:10   ` sashiko-bot
  2026-06-25 14:10 ` [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
  5 siblings, 1 reply; 22+ messages in thread
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

From: George Moussalem <george.moussalem@outlook.com>

Add driver support for the Qualcomm IPQ5018 bluetooth chip.
The firmware runs on the M0 co-processor.

The host and the M0 core use a shared memory carveout for transport
using ring buffers. This driver implements the transport layer between
the HCI core and the Bluetooth subsystem running on the M0 core.

Notifications of host and M0 core events are triggered by an IPC
register BIT and an interrupt line respectfully.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 drivers/bluetooth/Kconfig     |  11 +
 drivers/bluetooth/Makefile    |   1 +
 drivers/bluetooth/btqcomipc.c | 939 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 951 insertions(+)

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e8c24d757e9..6b8bed6a6ffd 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -413,6 +413,17 @@ config BT_MTKUART
 	  Say Y here to compile support for MediaTek Bluetooth UART devices
 	  into the kernel or say M to compile it as module (btmtkuart).
 
+config BT_QCOMIPC
+	tristate "Qualcomm IPQ5018 IPC based HCI support"
+	select BT_QCA
+	help
+	  Qualcomm IPQ5018 IPC based HCI driver.
+	  This driver is used to bridge HCI data onto shared memory between
+	  the host and the M0 BTSS core.
+
+	  Say Y here to compile support for HCI over Qualcomm IPC into the
+	  kernel or say M to compile as a module.
+
 config BT_QCOMSMD
 	tristate "Qualcomm SMD based HCI support"
 	depends on RPMSG || (COMPILE_TEST && RPMSG=n)
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index e6b1c1180d1d..05f19047bed0 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_BT_MRVL)		+= btmrvl.o
 obj-$(CONFIG_BT_MRVL_SDIO)	+= btmrvl_sdio.o
 obj-$(CONFIG_BT_MTKSDIO)	+= btmtksdio.o
 obj-$(CONFIG_BT_MTKUART)	+= btmtkuart.o
+obj-$(CONFIG_BT_QCOMIPC)	+= btqcomipc.o
 obj-$(CONFIG_BT_QCOMSMD)	+= btqcomsmd.o
 obj-$(CONFIG_BT_BCM)		+= btbcm.o
 obj-$(CONFIG_BT_RTL)		+= btrtl.o
diff --git a/drivers/bluetooth/btqcomipc.c b/drivers/bluetooth/btqcomipc.c
new file mode 100644
index 000000000000..662a75b6c4a9
--- /dev/null
+++ b/drivers/bluetooth/btqcomipc.c
@@ -0,0 +1,939 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/remoteproc.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "btqca.h"
+
+/** Message header format.
+ *
+ *        ----------------------------------------------------------------
+ * BitPos |    15    | 14 | 13 | 12 | 11 | 10 |  9  |  8  |    7 - 0     |
+ *         ---------------------------------------------------------------
+ * Field  | long_msg |ACK |        RFU        |  pkt_type |    cmd       |
+ *        ----------------------------------------------------------------
+ *
+ * - long_msg   : If set, indicates that the payload is larger than the
+ *                IPC_MSG_PLD_SZ. The payload instead contains a pointer to the
+ *                long message buffer in the shared BTSS memory space.
+ *
+ * - ACK       : Set if sending ACK if required by sending acknowledegement
+ *                to sender i.e. send an ack IPC interrupt if set.
+ *
+ * - RFU        : Reserved for future use.
+ *
+ * - pkt_type   : IPC Packet Type
+ *
+ * - cmd        : Contains unique command ID
+ */
+
+#define IPC_MSG_HDR_SZ		4
+#define IPC_MSG_PLD_SZ		40
+#define IPC_TOTAL_MSG_SZ	(IPC_MSG_HDR_SZ + IPC_MSG_PLD_SZ)
+
+/* Message Header */
+#define IPC_HDR_LONG_MSG	BIT(15)
+#define IPC_HDR_REQ_ACK		BIT(14)
+#define IPC_HDR_PKT_TYPE_MASK	GENMASK(9, 8)
+#define  IPC_HDR_PKT_TYPE_CUST	0
+#define  IPC_HDR_PKT_TYPE_HCI	1
+#define  IPC_HDR_PKT_TYPE_AUDIO	2
+#define  IPC_HDR_PKT_TYPE_RFU	3
+#define IPC_HDR_CMD_MASK	GENMASK(7, 0)
+
+#define IPC_CMD_STOP		1
+#define IPC_CMD_SWITCH_TO_UART	2
+#define IPC_CMD_PREPARE_DUMP	3
+#define IPC_CMD_COLLECT_DUMP	4
+#define IPC_CMD_START		5
+
+#define IPC_TX_QSIZE		32
+
+#define	TO_APPS_ADDR(a)		(desc->mem_region + (int)(uintptr_t)a)
+#define	TO_BT_ADDR(a)		(a - desc->mem_region)
+#define IPC_LBUF_SZ(w, x, y, z)	(((TO_BT_ADDR((void *)w) + w->x) - w->y) / w->z)
+
+#define	GET_NO_OF_BLOCKS(a, b) ((a + b - 1) / b)
+
+#define GET_RX_INDEX_FROM_BUF(x, y)	((x - desc->rx_ctxt->lring_buf) / y)
+
+#define GET_TX_INDEX_FROM_BUF(x, y)	((x - desc->tx_ctxt->lring_buf) / y)
+
+#define IS_RX_MEM_NON_CONTIGIOUS(buf, len, sz)		\
+	((buf + len) > (desc->rx_ctxt->lring_buf +	\
+	(sz * desc->rx_ctxt->lmsg_buf_cnt)))
+
+#define POWER_CONTROL_DELAY_MS	50
+
+#define BTSS_PAS_ID	0xc
+
+struct long_msg_info {
+	__le16 smsg_free_cnt;
+	__le16 lmsg_free_cnt;
+	u8 ridx;
+	u8 widx;
+} __packed;
+
+struct ipc_aux_ptr {
+	__le32 len;
+	__le32 buf;
+} __packed;
+
+struct ring_buffer {
+	__le16 msg_hdr;
+	__le16 len;
+	union {
+		u8 smsg_data[IPC_MSG_PLD_SZ];
+		__le32 lmsg_data;
+	} payload;
+} __packed;
+
+struct ring_buffer_info {
+	__le32 rbuf;
+	u8 ring_buf_cnt;
+	u8 ridx;
+	u8 widx;
+	u8 tidx;
+	__le32 next;
+} __packed;
+
+struct context_info {
+	__le16 total_size;
+	u8 lmsg_buf_cnt;
+	u8 smsg_buf_cnt;
+	struct ring_buffer_info sring_buf_info;
+	__le32 sring_buf;
+	__le32 lring_buf;
+	__le32 reserved;
+} __packed;
+
+struct qcom_btss {
+	struct device *dev;
+	struct rproc *rproc;
+	struct hci_dev *hdev;
+
+	struct regmap *regmap;
+	u32 offset;
+	u32 bit;
+	int irq;
+
+	void *mem_region;
+	phys_addr_t mem_phys;
+	phys_addr_t mem_reloc;
+	size_t mem_size;
+
+	struct sk_buff_head tx_q;
+	struct workqueue_struct *wq;
+	struct work_struct work;
+	wait_queue_head_t wait_q;
+	spinlock_t lock;
+
+	struct context_info *tx_ctxt;
+	struct context_info *rx_ctxt;
+	struct long_msg_info lmsg_ctxt;
+
+	bool running;
+};
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb);
+
+static void *btss_alloc_lmsg(struct qcom_btss *desc, u32 len,
+			     struct ipc_aux_ptr *aux_ptr, bool *is_lbuf_full)
+{
+	struct device *dev = desc->dev;
+	u8 idx, blks, blks_consumed;
+	void *ret_ptr;
+	u32 lsz;
+
+	if (desc->tx_ctxt->lring_buf == 0) {
+		dev_err(dev, "no long message buffer initialized\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf, lmsg_buf_cnt);
+	blks = GET_NO_OF_BLOCKS(len, lsz);
+
+	if (!desc->lmsg_ctxt.lmsg_free_cnt ||
+			(blks > desc->lmsg_ctxt.lmsg_free_cnt))
+		return ERR_PTR(-EAGAIN);
+
+	idx = desc->lmsg_ctxt.widx;
+
+	if ((desc->lmsg_ctxt.widx + blks) > desc->tx_ctxt->lmsg_buf_cnt) {
+		blks_consumed = desc->tx_ctxt->lmsg_buf_cnt - idx;
+		aux_ptr->len = len - (blks_consumed * lsz);
+		aux_ptr->buf = desc->tx_ctxt->lring_buf;
+	}
+
+	desc->lmsg_ctxt.widx = (desc->lmsg_ctxt.widx + blks) %
+		desc->tx_ctxt->lmsg_buf_cnt;
+
+	desc->lmsg_ctxt.lmsg_free_cnt -= blks;
+
+	if (desc->lmsg_ctxt.lmsg_free_cnt <=
+			((desc->tx_ctxt->lmsg_buf_cnt * 20) / 100))
+		*is_lbuf_full = true;
+
+	ret_ptr = TO_APPS_ADDR(desc->tx_ctxt->lring_buf) + (idx * lsz);
+
+	return ret_ptr;
+}
+
+static struct ring_buffer_info *btss_get_tx_rbuf(struct qcom_btss *desc,
+						 bool *is_sbuf_full)
+{
+	u8 idx;
+	struct ring_buffer_info *rinfo;
+
+	for (rinfo = &(desc->tx_ctxt->sring_buf_info);	rinfo != NULL;
+		rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+		idx = (rinfo->widx + 1) % (desc->tx_ctxt->smsg_buf_cnt);
+
+		if (idx != rinfo->tidx) {
+			desc->lmsg_ctxt.smsg_free_cnt--;
+
+			if (desc->lmsg_ctxt.smsg_free_cnt <=
+				((desc->tx_ctxt->smsg_buf_cnt * 20) / 100))
+				*is_sbuf_full = true;
+
+			return rinfo;
+		}
+	}
+
+	return ERR_PTR(-EAGAIN);
+}
+
+static int btss_send(struct qcom_btss *desc, u16 msg_hdr,
+		     struct sk_buff *skb)
+{
+	struct hci_dev *hdev = desc->hdev;
+	struct ring_buffer_info *rinfo;
+	struct ipc_aux_ptr aux_ptr;
+	struct ring_buffer *rbuf;
+	bool is_lbuf_full = false;
+	bool is_sbuf_full = false;
+	u16 hdr = msg_hdr;
+	void *ptr_buf;
+	u32 len;
+
+	/* Account for HCI packet type as it's not included in the skb payload */
+	len = skb->len + 1;
+	memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+	if (len > IPC_MSG_PLD_SZ) {
+		hdr |= IPC_HDR_LONG_MSG;
+
+		ptr_buf = btss_alloc_lmsg(desc, len,
+					  &aux_ptr, &is_lbuf_full);
+		if (IS_ERR(ptr_buf)) {
+			bt_dev_err(hdev, "long msg buf full");
+			hdev->stat.err_tx++;
+			return PTR_ERR(ptr_buf);
+		}
+	}
+
+	rinfo = btss_get_tx_rbuf(desc, &is_sbuf_full);
+	if (IS_ERR(rinfo)) {
+		bt_dev_err(hdev, "short msg buf full");
+		hdev->stat.err_tx++;
+		return PTR_ERR(rinfo);
+	}
+
+	rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[rinfo->widx];
+
+	if (len > IPC_MSG_PLD_SZ)
+		rbuf->payload.lmsg_data = cpu_to_le32(TO_BT_ADDR(ptr_buf));
+	else
+		ptr_buf = rbuf->payload.smsg_data;
+
+	/* if it's a short message, the aux len and buf are NULL */
+	memcpy_toio(ptr_buf, &hci_skb_pkt_type(skb), 1);
+	memcpy_toio((u8 *)ptr_buf + 1, skb->data, skb->len - aux_ptr.len);
+	if (aux_ptr.buf) {
+		memcpy_toio(TO_APPS_ADDR(aux_ptr.buf),
+			    (skb->data + (skb->len - aux_ptr.len)), aux_ptr.len);
+	}
+
+	if (is_sbuf_full || is_lbuf_full)
+		hdr |= IPC_HDR_REQ_ACK;
+
+	rbuf->msg_hdr = cpu_to_le16(hdr);
+	rbuf->len = cpu_to_le16(len);
+
+	rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+
+	regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+	return 0;
+}
+
+static void btss_process_tx_queue(struct qcom_btss *desc)
+{
+	struct sk_buff *skb;
+	u16 hdr;
+	int ret;
+
+	while ((skb = skb_dequeue(&desc->tx_q))) {
+		hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+
+		ret = btss_send(desc, hdr, skb);
+		if (ret) {
+			bt_dev_err(desc->hdev, "Failed to send message");
+			skb_queue_head(&desc->tx_q, skb);
+			break;
+		}
+
+		btqcomipc_update_stats(desc->hdev, skb);
+		kfree_skb(skb);
+	}
+}
+
+static void btss_free_lmsg(struct qcom_btss *desc, u32 lmsg, u16 len)
+{
+	u8 idx;
+	u8 blks;
+	u32 lsz = IPC_LBUF_SZ(desc->tx_ctxt, total_size, lring_buf,
+				   lmsg_buf_cnt);
+
+	idx = GET_TX_INDEX_FROM_BUF(lmsg, lsz);
+
+	if (idx != desc->lmsg_ctxt.ridx)
+		return;
+
+	blks = GET_NO_OF_BLOCKS(len, lsz);
+
+	desc->lmsg_ctxt.ridx  = (desc->lmsg_ctxt.ridx  + blks) %
+		desc->tx_ctxt->lmsg_buf_cnt;
+
+	desc->lmsg_ctxt.lmsg_free_cnt += blks;
+}
+
+static int btss_send_ctrl(struct qcom_btss *desc, u16 msg_hdr)
+{
+	struct ring_buffer_info *rinfo;
+	struct ring_buffer *rbuf;
+
+	rinfo = btss_get_tx_rbuf(desc, NULL);
+	if (IS_ERR(rinfo))
+		return PTR_ERR(rinfo);
+
+	rbuf = &((struct ring_buffer *)TO_APPS_ADDR(rinfo->rbuf))[rinfo->widx];
+	rbuf->msg_hdr = cpu_to_le16(msg_hdr);
+	rbuf->len = 0;
+
+	rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+
+	regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+	return 0;
+}
+
+static void btss_recv_cust_frame(struct qcom_btss *desc, u8 cmd)
+{
+	u16 msg_hdr = 0;
+	int ret;
+
+	msg_hdr |= cmd;
+
+	switch (cmd) {
+	case IPC_CMD_STOP:
+		bt_dev_info(desc->hdev, "BTSS stopped, gracefully stopping APSS IPC");
+		break;
+	case IPC_CMD_START:
+		desc->tx_ctxt = (struct context_info *)((void *)desc->rx_ctxt +
+				le16_to_cpu(desc->rx_ctxt->total_size));
+		desc->lmsg_ctxt.widx = 0;
+		desc->lmsg_ctxt.ridx = 0;
+		desc->lmsg_ctxt.smsg_free_cnt = desc->tx_ctxt->smsg_buf_cnt;
+		desc->lmsg_ctxt.lmsg_free_cnt = desc->tx_ctxt->lmsg_buf_cnt;
+		WRITE_ONCE(desc->running, true);
+		wake_up(&desc->wait_q);
+
+		bt_dev_info(desc->hdev, "BTSS started, initializing APSS BT IPC");
+		return;
+	default:
+		bt_dev_err(desc->hdev, "Unsupported CMD ID: %u", cmd);
+		return;
+	}
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		bt_dev_err(desc->hdev, "BTSS not initialized, no message sent");
+		return;
+	}
+
+	WRITE_ONCE(desc->running, false);
+
+	ret = btss_send_ctrl(desc, msg_hdr);
+	if (ret)
+		bt_dev_err(desc->hdev, "Failed to send control message");
+}
+
+static inline int btss_recv_hci_frame(struct qcom_btss *desc, const u8 *data, size_t len)
+{
+	unsigned char pkt_type;
+	struct sk_buff *skb;
+	size_t pkt_len;
+
+	if (len < 1)
+		return -EPROTO;
+
+	pkt_type = data[0];
+
+	switch (pkt_type) {
+	case HCI_EVENT_PKT:
+	{
+		if (len < HCI_EVENT_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_event_hdr *hdr = (struct hci_event_hdr *)(data + 1);
+
+		pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
+		break;
+	}
+	case HCI_COMMAND_PKT: {
+		if (len < HCI_COMMAND_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_command_hdr *hdr = (struct hci_command_hdr *)(data + 1);
+
+		pkt_len = HCI_COMMAND_HDR_SIZE + le16_to_cpu(hdr->plen);
+		break;
+	}
+	case HCI_ACLDATA_PKT:
+	{
+		if (len < HCI_ACL_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_acl_hdr *hdr = (struct hci_acl_hdr *)(data + 1);
+
+		pkt_len = HCI_ACL_HDR_SIZE + le16_to_cpu(hdr->dlen);
+		break;
+	}
+	case HCI_SCODATA_PKT:
+	{
+		if (len < HCI_SCO_HDR_SIZE)
+			return -EILSEQ;
+		struct hci_sco_hdr *hdr = (struct hci_sco_hdr *)(data + 1);
+
+		pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
+		break;
+	}
+	default:
+		return -EPROTO;
+	}
+
+	if (pkt_len > len)
+		return -EINVAL;
+
+	skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
+	if (!skb) {
+		desc->hdev->stat.err_rx++;
+		return -ENOMEM;
+	}
+
+	skb->dev = (void *)desc->hdev;
+	hci_skb_pkt_type(skb) = pkt_type;
+	skb_put_data(skb, data + 1, pkt_len);
+
+	hci_recv_frame(desc->hdev, skb);
+	desc->hdev->stat.byte_rx += pkt_len;
+
+	return 0;
+}
+
+static inline int btss_process_rx(struct qcom_btss *desc,
+				  struct ring_buffer_info *rinfo,
+				  bool *ack, u8 *rx_count)
+{
+	u8 ridx, lbuf_idx, blks_consumed, pkt_type, cmd;
+	struct ipc_aux_ptr aux_ptr;
+	struct ring_buffer *rbuf;
+	uint8_t *rxbuf = NULL;
+	unsigned char *buf;
+	u32 lsz;
+	int ret;
+
+	ridx = rinfo->ridx;
+
+	while (ridx != rinfo->widx) {
+		memset(&aux_ptr, 0, sizeof(struct ipc_aux_ptr));
+
+		rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[ridx];
+
+		if (rbuf->msg_hdr & IPC_HDR_LONG_MSG) {
+			rxbuf = TO_APPS_ADDR(rbuf->payload.lmsg_data);
+			lsz = IPC_LBUF_SZ(desc->rx_ctxt, total_size, lring_buf,
+				   lmsg_buf_cnt);
+
+			if (IS_RX_MEM_NON_CONTIGIOUS(rbuf->payload.lmsg_data,
+						     rbuf->len, lsz)) {
+				lbuf_idx = GET_RX_INDEX_FROM_BUF(
+						rbuf->payload.lmsg_data, lsz);
+
+				blks_consumed = desc->rx_ctxt->lmsg_buf_cnt -
+					lbuf_idx;
+				aux_ptr.len = rbuf->len - (blks_consumed * lsz);
+				aux_ptr.buf = desc->rx_ctxt->lring_buf;
+			}
+		} else {
+			rxbuf = rbuf->payload.smsg_data;
+		}
+
+		*ack = (rbuf->msg_hdr & IPC_HDR_REQ_ACK);
+
+		pkt_type = FIELD_GET(IPC_HDR_PKT_TYPE_MASK, rbuf->msg_hdr);
+
+		switch (pkt_type) {
+		case IPC_HDR_PKT_TYPE_HCI:
+			buf = kmalloc(rbuf->len, GFP_ATOMIC);
+			if (!buf) {
+				rinfo->ridx = ridx;
+				return -ENOMEM;
+			}
+
+			memcpy_fromio(buf, rxbuf, rbuf->len - aux_ptr.len);
+
+			if (aux_ptr.buf)
+				memcpy_fromio(buf + (rbuf->len - aux_ptr.len),
+					      TO_APPS_ADDR(aux_ptr.buf), aux_ptr.len);
+
+			ret = btss_recv_hci_frame(desc, buf, rbuf->len);
+			if (ret)
+				bt_dev_err(desc->hdev, "Failed to process HCI frame: %d", ret);
+			kfree(buf);
+			break;
+		case IPC_HDR_PKT_TYPE_CUST:
+			cmd = FIELD_GET(IPC_HDR_CMD_MASK, rbuf->msg_hdr);
+			btss_recv_cust_frame(desc, cmd);
+			break;
+		default:
+			break;
+		}
+
+		ridx = (ridx + 1) % rinfo->ring_buf_cnt;
+
+		if (rx_count)
+			(*rx_count)++;
+
+		rinfo->ridx = ridx;
+	}
+
+	return 0;
+}
+
+static void btss_process_ack(struct qcom_btss *desc)
+{
+	struct ring_buffer_info *rinfo;
+	struct ring_buffer *rbuf;
+	u8 tidx;
+
+	for (rinfo = &desc->tx_ctxt->sring_buf_info; rinfo != NULL;
+		rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+		tidx = rinfo->tidx;
+		rbuf = (struct ring_buffer *)TO_APPS_ADDR(rinfo->rbuf);
+
+		while (tidx != rinfo->ridx) {
+			if (rbuf[tidx].msg_hdr & IPC_HDR_LONG_MSG) {
+				btss_free_lmsg(desc,
+					       rbuf[tidx].payload.lmsg_data,
+					       rbuf[tidx].len);
+			}
+
+			tidx = (tidx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+			desc->lmsg_ctxt.smsg_free_cnt++;
+		}
+
+		rinfo->tidx = tidx;
+
+		btss_process_tx_queue(desc);
+	}
+}
+
+static void btss_worker(struct work_struct *work)
+{
+	struct qcom_btss *desc = container_of(work, struct qcom_btss, work);
+	struct ring_buffer_info *rinfo;
+	bool ack = false;
+	u32 offset;
+	int ret;
+
+	if (desc->rproc->state != RPROC_RUNNING)
+		return;
+
+	spin_lock(&desc->lock);
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		// FW sets offset of RX context info at start of memory region upon boot
+		offset = readl(desc->mem_region);
+		dev_dbg(desc->dev, "offset after M0 boot: 0x%08x\n", offset);
+		desc->rx_ctxt = (struct context_info *)(desc->mem_region + offset);
+	} else {
+		btss_process_ack(desc);
+	}
+
+	for (rinfo = &(desc->rx_ctxt->sring_buf_info);
+	     rinfo != NULL;
+	     rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {
+		ret = btss_process_rx(desc, rinfo, &ack,
+				      &desc->rx_ctxt->smsg_buf_cnt);
+		if (ret) {
+			bt_dev_err(desc->hdev, "Failed to process peer msgs: %d", ret);
+			goto spin_unlock;
+		}
+	}
+
+	if (ack)
+		regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
+
+spin_unlock:
+	spin_unlock(&desc->lock);
+}
+
+static irqreturn_t btss_irq_handler(int irq, void *data)
+{
+	struct qcom_btss *desc = data;
+
+	queue_work(desc->wq, &desc->work);
+
+	return IRQ_HANDLED;
+}
+
+static int btss_init(struct qcom_btss *desc)
+{
+	struct device *dev = desc->dev;
+	int ret;
+
+	init_waitqueue_head(&desc->wait_q);
+	spin_lock_init(&desc->lock);
+	skb_queue_head_init(&desc->tx_q);
+
+	desc->wq = create_singlethread_workqueue("btss_wq");
+	if (!desc->wq) {
+		dev_err(dev, "Failed to initialize workqueue\n");
+		return -EAGAIN;
+	}
+
+	INIT_WORK(&desc->work, btss_worker);
+
+	ret = devm_request_threaded_irq(dev, desc->irq, NULL, btss_irq_handler,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					"btss_irq", desc);
+
+	if (ret)
+		dev_err(dev, "error registering irq[%d] ret = %d\n",
+			desc->irq, ret);
+
+	return ret;
+}
+
+static void btqcomipc_update_stats(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	u8 pkt_type = hci_skb_pkt_type(skb);
+
+	hdev->stat.byte_tx += skb->len;
+	switch (pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	default:
+		break;
+	}
+}
+
+static int btqcomipc_open(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	int ret;
+
+	ret = btss_init(desc);
+	if (ret) {
+		bt_dev_err(hdev, "Failed initializing BTSS: %d", ret);
+		return ret;
+	}
+
+	/* wait 2 seconds for filesystem to become available */
+	msleep(2000);
+
+	/* Boot M0 firmware */
+	ret = rproc_boot(desc->rproc);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to boot M0 processor: %d", ret);
+		return ret;
+	}
+
+	msleep(POWER_CONTROL_DELAY_MS);
+	ret = wait_event_timeout(desc->wait_q, READ_ONCE(desc->running),
+				 msecs_to_jiffies(1000));
+
+	if (!ret) {
+		bt_dev_err(hdev, "Timeout waiting for BTSS start");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int btqcomipc_close(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	rproc_shutdown(desc->rproc);
+	msleep(POWER_CONTROL_DELAY_MS);
+
+	return 0;
+}
+
+static int btqcomipc_shutdown(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	WRITE_ONCE(desc->running, false);
+	if (desc->wq != NULL) {
+		disable_irq(desc->irq);
+		flush_workqueue(desc->wq);
+		devm_free_irq(desc->dev, desc->irq, desc);
+		skb_queue_purge(&desc->tx_q);
+		destroy_workqueue(desc->wq);
+		desc->wq = NULL;
+	}
+
+	return 0;
+}
+
+static int btqcomipc_setup(struct hci_dev *hdev)
+{
+	struct qca_btsoc_version ver;
+	int ret;
+
+	/*
+	 * Enable controller to do both LE scan and BR/EDR inquiry
+	 * simultaneously.
+	 */
+	hci_set_quirk(hdev, HCI_QUIRK_SIMULTANEOUS_DISCOVERY);
+
+	/*
+	 * Enable NON_PERSISTENT_SETUP QUIRK to ensure to execute
+	 * setup for every hci up.
+	 */
+	hci_set_quirk(hdev, HCI_QUIRK_NON_PERSISTENT_SETUP);
+	ret = qca_read_soc_version(hdev, &ver, QCA_IPQ5018);
+	if (ret)
+		return -EINVAL;
+
+	ret = qca_uart_setup(hdev, 0, QCA_IPQ5018, ver, NULL);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to setup UART: %d\n", ret);
+		return ret;
+	}
+
+	bt_dev_info(hdev, "QCA Build Info: %s", hdev->fw_info);
+
+	/* Obtain and set BD address from NVMEM cell */
+	hci_set_quirk(hdev, HCI_QUIRK_USE_BDADDR_NVMEM);
+	hci_set_quirk(hdev, HCI_QUIRK_BDADDR_NVMEM_BE);
+
+	return 0;
+}
+
+static int btqcomipc_send(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	u16 hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+	int ret;
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		bt_dev_err(hdev, "BTSS not initialized, failed to send message");
+		ret = -ENODEV;
+		goto free_skb;
+	}
+
+	ret = btss_send(desc, hdr, skb);
+	if (ret) {
+		if (ret == -EAGAIN) {
+			if (skb_queue_len(&desc->tx_q) >= IPC_TX_QSIZE) {
+				bt_dev_err(hdev, "TX queue full, dropping message");
+				hdev->stat.err_tx++;
+				ret = -ENOBUFS;
+			} else {
+				skb_queue_tail(&desc->tx_q, skb);
+				return 0;
+			}
+		} else {
+			bt_dev_err(hdev, "Failed to send message: %d", ret);
+			hdev->stat.err_tx++;
+		}
+	}
+
+	btqcomipc_update_stats(desc->hdev, skb);
+
+free_skb:
+	kfree_skb(skb);
+
+	return ret;
+}
+
+static int btqcomipc_flush(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+
+	skb_queue_purge(&desc->tx_q);
+	return 0;
+}
+
+static int btqcomipc_alloc_memory_region(struct qcom_btss *desc)
+{
+	struct device *dev = desc->dev;
+	struct resource res;
+	int ret;
+
+	/* lookup reserved-memory region of the remoteproc node */
+	ret = of_reserved_mem_region_to_resource(desc->rproc->dev.parent->of_node, 0, &res);
+	if (ret) {
+		dev_err(dev, "unable to acquire memory-region resource\n");
+		return ret;
+	}
+
+	desc->mem_phys = res.start;
+	desc->mem_reloc = res.start;
+	desc->mem_size = resource_size(&res);
+	desc->mem_region = devm_ioremap(dev, desc->mem_phys, desc->mem_size);
+	if (!desc->mem_region) {
+		dev_err(dev, "unable to map memory region: %pR\n", &res);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void btqcomipc_rproc_put(void *data)
+{
+	struct rproc *rproc = data;
+
+	rproc_put(rproc);
+}
+
+static int btqcomipc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct qcom_btss *desc;
+	phandle rproc_phandle;
+	struct hci_dev *hdev;
+	unsigned int args[2];
+	int ret;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->dev = dev;
+
+	if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle))
+		return dev_err_probe(dev, -ENOENT, "Failed to get remoteproc handle\n");
+
+	desc->rproc = rproc_get_by_phandle(rproc_phandle);
+	if (!desc->rproc)
+		return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get remoteproc\n");
+
+	devm_add_action_or_reset(dev, btqcomipc_rproc_put, desc->rproc);
+
+	ret = btqcomipc_alloc_memory_region(desc);
+	if (ret)
+		return ret;
+
+	desc->regmap = syscon_regmap_lookup_by_phandle_args(dev->of_node, "qcom,ipc", 2, args);
+	if (IS_ERR(desc->regmap))
+		return PTR_ERR(desc->regmap);
+
+	desc->offset = args[0];
+	desc->bit = args[1];
+
+	desc->irq = platform_get_irq(pdev, 0);
+	if (desc->irq < 0)
+		return dev_err_probe(dev, desc->irq, "Failed to acquire IRQ\n");
+
+	hdev = hci_alloc_dev();
+	if (!hdev)
+		return -ENOMEM;
+
+	hci_set_drvdata(hdev, desc);
+	desc->hdev = hdev;
+	SET_HCIDEV_DEV(hdev, &pdev->dev);
+	hdev->bus = HCI_IPC;
+
+	hdev->open = btqcomipc_open;
+	hdev->close = btqcomipc_close;
+	hdev->shutdown = btqcomipc_shutdown;
+	hdev->setup = btqcomipc_setup;
+	hdev->send = btqcomipc_send;
+	hdev->flush = btqcomipc_flush;
+	hdev->set_bdaddr = qca_set_bdaddr;
+
+	ret = hci_register_dev(hdev);
+	if (ret < 0) {
+		hci_free_dev(hdev);
+		return dev_err_probe(dev, -EBUSY, "Failed to register hdev\n");
+	}
+
+	platform_set_drvdata(pdev, desc);
+
+	return 0;
+}
+
+static void btqcomipc_remove(struct platform_device *pdev)
+{
+	struct qcom_btss *desc = platform_get_drvdata(pdev);
+
+	if (!desc || !desc->hdev)
+		return;
+
+	hci_unregister_dev(desc->hdev);
+	hci_free_dev(desc->hdev);
+}
+
+static const struct of_device_id btqcomipc_of_match[] = {
+	{ .compatible = "qcom,ipq5018-bt" },
+	{ /* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, btqcomipc_of_match);
+
+static struct platform_driver btqcomipc_driver = {
+	.probe = btqcomipc_probe,
+	.remove = btqcomipc_remove,
+	.driver = {
+		.name = "btqcomipc",
+		.of_match_table = btqcomipc_of_match,
+	},
+};
+
+module_platform_driver(btqcomipc_driver);
+
+MODULE_DESCRIPTION("Qualcomm Bluetooth IPC Driver");
+MODULE_LICENSE("GPL");

-- 
2.53.0



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

* [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
  2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (4 preceding siblings ...)
  2026-06-25 14:10 ` [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
@ 2026-06-25 14:10 ` George Moussalem via B4 Relay
  2026-06-26 14:10   ` sashiko-bot
  5 siblings, 1 reply; 22+ messages in thread
From: George Moussalem via B4 Relay @ 2026-06-25 14:10 UTC (permalink / raw)
  To: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc,
	George Moussalem

From: George Moussalem <george.moussalem@outlook.com>

Add nodes for the M0 remoteproc, reserved memory carveout, and Bluetooth
to bring up the M0 core and enable the Bluetooth Subsystem.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 arch/arm64/boot/dts/qcom/ipq5018.dtsi | 34 +++++++++++++++++++++++++++++++++-
 1 file changed, 33 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
index 6f8004a22a1f..4fdf20c87b0a 100644
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -17,6 +17,17 @@ / {
 	#address-cells = <2>;
 	#size-cells = <2>;
 
+	bluetooth: bluetooth {
+		compatible = "qcom,ipq5018-bt";
+
+		qcom,ipc = <&apcs_glb 8 23>;
+		interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+		qcom,rproc = <&m0_btss>;
+
+		status = "disabled";
+	};
+
 	clocks {
 		gephy_rx_clk: gephy-rx-clk {
 			compatible = "fixed-clock";
@@ -131,11 +142,31 @@ psci {
 		method = "smc";
 	};
 
+	m0_btss: remoteproc {
+		compatible = "qcom,ipq5018-btss-pil";
+
+		firmware-name = "qca/bt_fw_patch.mbn";
+
+		clocks = <&gcc GCC_BTSS_LPO_CLK>;
+		clock-names = "btss_lpo_clk";
+		resets = <&gcc GCC_BTSS_BCR>;
+		reset-names = "btss_reset";
+
+		memory-region = <&btss_region>;
+
+		status = "disabled";
+	};
+
 	reserved-memory {
 		#address-cells = <2>;
 		#size-cells = <2>;
 		ranges;
 
+		btss_region: bluetooth@7000000 {
+			reg = <0x0 0x07000000 0x0 0x58000>;
+			no-map;
+		};
+
 		bootloader@4a800000 {
 			reg = <0x0 0x4a800000 0x0 0x200000>;
 			no-map;
@@ -647,7 +678,8 @@ watchdog: watchdog@b017000 {
 
 		apcs_glb: mailbox@b111000 {
 			compatible = "qcom,ipq5018-apcs-apps-global",
-				     "qcom,ipq6018-apcs-apps-global";
+				     "qcom,ipq6018-apcs-apps-global",
+				     "syscon";
 			reg = <0x0b111000 0x1000>;
 			#clock-cells = <1>;
 			clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>;

-- 
2.53.0



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

* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
  2026-06-25 14:10 ` [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver George Moussalem via B4 Relay
@ 2026-06-25 14:18   ` Philipp Zabel
  2026-06-25 14:24     ` George Moussalem
  2026-06-26 11:20   ` Konrad Dybcio
  2026-06-26 14:10   ` sashiko-bot
  2 siblings, 1 reply; 22+ messages in thread
From: Philipp Zabel @ 2026-06-25 14:18 UTC (permalink / raw)
  To: george.moussalem, Jens Axboe, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
	Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
	Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
	Konrad Dybcio, Mathieu Poirier
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc

On Do, 2026-06-25 at 18:10 +0400, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Add support to bring up the M0 core of the bluetooth subsystem found in
> the IPQ5018 SoC.
> 
> The signed firmware loaded is authenticated by TrustZone. If successful,
> the M0 core boots the firmware and the peripheral is taken out of reset
> using a Secure Channel Manager call to TrustZone.
> 
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  drivers/remoteproc/Kconfig            |  12 ++
>  drivers/remoteproc/Makefile           |   1 +
>  drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
>  3 files changed, 274 insertions(+)
> 
[...]
> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
> new file mode 100644
> index 000000000000..7168e270e4d4
> --- /dev/null
> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c
> @@ -0,0 +1,261 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2026 The Linux Foundation. All rights reserved.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/elf.h>
> +#include <linux/firmware/qcom/qcom_scm.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/of_reserved_mem.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +#include <linux/soc/qcom/mdt_loader.h>
> +
> +#include "qcom_common.h"
> +
> +#define BTSS_PAS_ID	0xc
> +
> +struct m0_btss {
> +	struct device *dev;
> +	phys_addr_t mem_phys;
> +	phys_addr_t mem_reloc;
> +	void __iomem *mem_region;
> +	size_t mem_size;
> +	struct reset_control *btss_reset;

Why is this stored here? It doesn't seem to be used.

[...]
> +static int m0_btss_pil_probe(struct platform_device *pdev)
> +{
> +	// struct reset_control *btss_reset;

It looks like this shouldn't be commented out.

> +	struct device *dev = &pdev->dev;
> +	const char *fw_name = NULL;
> +	struct m0_btss *desc;
> +	struct clk *lpo_clk;
> +	struct rproc *rproc;
> +	int ret;
> +
> +	ret = of_property_read_string(dev->of_node, "firmware-name",
> +				      &fw_name);
> +	if (ret < 0)
> +		return ret;
> +
> +	rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
> +				 fw_name, sizeof(*desc));
> +	if (!rproc) {
> +		dev_err(dev, "failed to allocate rproc\n");
> +		return -ENOMEM;
> +	}
> +
> +	desc = rproc->priv;
> +	desc->dev = dev;
> +
> +	ret = m0_btss_alloc_memory_region(desc);
> +	if (ret)
> +		return ret;
> +
> +	lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
> +	if (IS_ERR(lpo_clk))
> +		return dev_err_probe(dev, PTR_ERR(lpo_clk),
> +				     "Failed to get lpo clock\n");
> +
> +	desc->btss_reset = devm_reset_control_get(dev, "btss_reset");

Please use devm_reset_control_get_exclusive() directly.

> +	if (IS_ERR_OR_NULL(desc->btss_reset))
> +		return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
> +				     "unable to acquire btss_reset\n");
> +
> +	ret = reset_control_deassert(desc->btss_reset);
> +	if (ret)
> +		return dev_err_probe(rproc->dev.parent, ret,
> +				     "Failed to deassert reset\n");

Shouldn't this be asserted on remove? Otherwise after an unbind/bind
cycle probe will enable the clock with reset already deasserted.
That may or may not be problematic.

Consider using devm_reset_control_get_exclusive_deasserted().


regards
Philipp

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

* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
  2026-06-25 14:18   ` Philipp Zabel
@ 2026-06-25 14:24     ` George Moussalem
  0 siblings, 0 replies; 22+ messages in thread
From: George Moussalem @ 2026-06-25 14:24 UTC (permalink / raw)
  To: Philipp Zabel, Jens Axboe, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
	Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
	Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
	Konrad Dybcio, Mathieu Poirier
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc

Thanks, that was quick!

On 6/25/26 18:18, Philipp Zabel wrote:
> On Do, 2026-06-25 at 18:10 +0400, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add support to bring up the M0 core of the bluetooth subsystem found in
>> the IPQ5018 SoC.
>>
>> The signed firmware loaded is authenticated by TrustZone. If successful,
>> the M0 core boots the firmware and the peripheral is taken out of reset
>> using a Secure Channel Manager call to TrustZone.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>>  drivers/remoteproc/Kconfig            |  12 ++
>>  drivers/remoteproc/Makefile           |   1 +
>>  drivers/remoteproc/qcom_m0_btss_pil.c | 261 ++++++++++++++++++++++++++++++++++
>>  3 files changed, 274 insertions(+)
>>
> [...]
>> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
>> new file mode 100644
>> index 000000000000..7168e270e4d4
>> --- /dev/null
>> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c
>> @@ -0,0 +1,261 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Copyright (c) 2026 The Linux Foundation. All rights reserved.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/elf.h>
>> +#include <linux/firmware/qcom/qcom_scm.h>
>> +#include <linux/io.h>
>> +#include <linux/kernel.h>
>> +#include <linux/of_reserved_mem.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +#include <linux/soc/qcom/mdt_loader.h>
>> +
>> +#include "qcom_common.h"
>> +
>> +#define BTSS_PAS_ID	0xc
>> +
>> +struct m0_btss {
>> +	struct device *dev;
>> +	phys_addr_t mem_phys;
>> +	phys_addr_t mem_reloc;
>> +	void __iomem *mem_region;
>> +	size_t mem_size;
>> +	struct reset_control *btss_reset;
> 
> Why is this stored here? It doesn't seem to be used.

will remove it and use devm_reset_control_get_exclusive_deasserted as
suggested below.

> 
> [...]
>> +static int m0_btss_pil_probe(struct platform_device *pdev)
>> +{
>> +	// struct reset_control *btss_reset;
> 
> It looks like this shouldn't be commented out.
> 
>> +	struct device *dev = &pdev->dev;
>> +	const char *fw_name = NULL;
>> +	struct m0_btss *desc;
>> +	struct clk *lpo_clk;
>> +	struct rproc *rproc;
>> +	int ret;
>> +
>> +	ret = of_property_read_string(dev->of_node, "firmware-name",
>> +				      &fw_name);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	rproc = devm_rproc_alloc(dev, "m0btss", &m0_btss_ops,
>> +				 fw_name, sizeof(*desc));
>> +	if (!rproc) {
>> +		dev_err(dev, "failed to allocate rproc\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	desc = rproc->priv;
>> +	desc->dev = dev;
>> +
>> +	ret = m0_btss_alloc_memory_region(desc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	lpo_clk = devm_clk_get_enabled(dev, "btss_lpo_clk");
>> +	if (IS_ERR(lpo_clk))
>> +		return dev_err_probe(dev, PTR_ERR(lpo_clk),
>> +				     "Failed to get lpo clock\n");
>> +
>> +	desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
> 
> Please use devm_reset_control_get_exclusive() directly.
> 
>> +	if (IS_ERR_OR_NULL(desc->btss_reset))
>> +		return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
>> +				     "unable to acquire btss_reset\n");
>> +
>> +	ret = reset_control_deassert(desc->btss_reset);
>> +	if (ret)
>> +		return dev_err_probe(rproc->dev.parent, ret,
>> +				     "Failed to deassert reset\n");
> 
> Shouldn't this be asserted on remove? Otherwise after an unbind/bind
> cycle probe will enable the clock with reset already deasserted.
> That may or may not be problematic.
> 
> Consider using devm_reset_control_get_exclusive_deasserted().
> 
> 
> regards
> Philipp

Regards,
George


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

* Re: [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
  2026-06-25 14:10 ` [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL George Moussalem via B4 Relay
@ 2026-06-26 10:47   ` Krzysztof Kozlowski
  2026-06-26 10:51     ` George Moussalem
  0 siblings, 1 reply; 22+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-26 10:47 UTC (permalink / raw)
  To: George Moussalem
  Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
	linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
	linux-bluetooth, netdev, linux-remoteproc

On Thu, Jun 25, 2026 at 06:10:05PM +0400, George Moussalem wrote:
> Document the M0 Bluetooth Subsystem remote processor core found in the
> Qualcomm IPQ5018 SoC. Firmware loaded is authenticated via TrustZone.
> The firmware running on the M0 core provides bluetooth functionality.
> 
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  .../bindings/remoteproc/qcom,m0-btss-pil.yaml      | 72 ++++++++++++++++++++++
>  1 file changed, 72 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
> new file mode 100644
> index 000000000000..397bb6815d71
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml

Use compatible as filename.

> @@ -0,0 +1,72 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/remoteproc/qcom,m0-btss-pil.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm M0 BTSS Peripheral Image Loader
> +
> +maintainers:
> +  - George Moussalem <george.moussalem@outlook.com>
> +
> +description:
> +  Qualcomm M0 BTSS Peripheral Secure Image Loader loads firmware and powers up
> +  the M0 BTSS remote processor core on the Qualcomm IPQ5018 SoC.
> +
> +properties:
> +  compatible:
> +    enum:
> +      - qcom,ipq5018-btss-pil
> +
> +  firmware-name:
> +    maxItems: 1
> +    description: Firmware name for the M0 Bluetooth Subsystem core

You can drop description, pretty obvious.

> +
> +  clocks:
> +    items:
> +      - description: M0 BTSS low power oscillator clock
> +
> +  clock-names:
> +    items:
> +      - const: btss_lpo_clk

Just "lpo"

> +
> +  memory-region:
> +    items:
> +      - description: M0 BTSS reserved memory carveout
> +
> +  resets:
> +    items:
> +      - description: M0 BTSS reset
> +
> +  reset-names:
> +    items:
> +      - const: btss_reset

Drop names. Using block name as input name is not really useful.

No supplies? no address space? How do you actually trigger remoteproc
startup?

> +
> +required:
> +  - compatible
> +  - firmware-name
> +  - clocks
> +  - clock-names
> +  - resets
> +  - reset-names
> +  - memory-region
> +
> +additionalProperties: false

Best regards,
Krzysztof


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

* Re: [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
  2026-06-26 10:47   ` Krzysztof Kozlowski
@ 2026-06-26 10:51     ` George Moussalem
  2026-06-26 11:16       ` Krzysztof Kozlowski
  0 siblings, 1 reply; 22+ messages in thread
From: George Moussalem @ 2026-06-26 10:51 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
	linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
	linux-bluetooth, netdev, linux-remoteproc

Hi Krzysztof,

On 6/26/26 14:47, Krzysztof Kozlowski wrote:
> On Thu, Jun 25, 2026 at 06:10:05PM +0400, George Moussalem wrote:
>> Document the M0 Bluetooth Subsystem remote processor core found in the
>> Qualcomm IPQ5018 SoC. Firmware loaded is authenticated via TrustZone.
>> The firmware running on the M0 core provides bluetooth functionality.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>>  .../bindings/remoteproc/qcom,m0-btss-pil.yaml      | 72 ++++++++++++++++++++++
>>  1 file changed, 72 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
>> new file mode 100644
>> index 000000000000..397bb6815d71
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/remoteproc/qcom,m0-btss-pil.yaml
> 
> Use compatible as filename.

understood, will update in v2.

> 
>> @@ -0,0 +1,72 @@
>> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/remoteproc/qcom,m0-btss-pil.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Qualcomm M0 BTSS Peripheral Image Loader
>> +
>> +maintainers:
>> +  - George Moussalem <george.moussalem@outlook.com>
>> +
>> +description:
>> +  Qualcomm M0 BTSS Peripheral Secure Image Loader loads firmware and powers up
>> +  the M0 BTSS remote processor core on the Qualcomm IPQ5018 SoC.
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - qcom,ipq5018-btss-pil
>> +
>> +  firmware-name:
>> +    maxItems: 1
>> +    description: Firmware name for the M0 Bluetooth Subsystem core
> 
> You can drop description, pretty obvious.

will drop

> 
>> +
>> +  clocks:
>> +    items:
>> +      - description: M0 BTSS low power oscillator clock
>> +
>> +  clock-names:
>> +    items:
>> +      - const: btss_lpo_clk
> 
> Just "lpo"

will update

> 
>> +
>> +  memory-region:
>> +    items:
>> +      - description: M0 BTSS reserved memory carveout
>> +
>> +  resets:
>> +    items:
>> +      - description: M0 BTSS reset
>> +
>> +  reset-names:
>> +    items:
>> +      - const: btss_reset
> 
> Drop names. Using block name as input name is not really useful.

Will drop

> 
> No supplies? no address space? How do you actually trigger remoteproc
> startup?

No supplied and no address space. The core is booted by a
qcom_scm_auth_and_reset call to TrustZone which authenticated the
firmware, takes it out of reset and boots it.

> 
>> +
>> +required:
>> +  - compatible
>> +  - firmware-name
>> +  - clocks
>> +  - clock-names
>> +  - resets
>> +  - reset-names
>> +  - memory-region
>> +
>> +additionalProperties: false
> 
> Best regards,
> Krzysztof
> 


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

* Re: [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-06-25 14:10 ` [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
@ 2026-06-26 10:53   ` Krzysztof Kozlowski
  2026-06-26 11:20     ` George Moussalem
  2026-06-26 14:10   ` sashiko-bot
  1 sibling, 1 reply; 22+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-26 10:53 UTC (permalink / raw)
  To: George Moussalem
  Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
	linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
	linux-bluetooth, netdev, linux-remoteproc

On Thu, Jun 25, 2026 at 06:10:08PM +0400, George Moussalem wrote:
> Document the Qualcomm IPQ5018 Bluetooth controller.
> 
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    | 63 ++++++++++++++++++++++
>  1 file changed, 63 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
> new file mode 100644
> index 000000000000..afd33f851858
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
> @@ -0,0 +1,63 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/net/bluetooth/qcom,ipq5018-bt.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Qualcomm IPQ5018 Bluetooth
> +
> +maintainers:
> +  - George Moussalem <george.moussalem@outlook.com>
> +
> +properties:
> +  compatible:
> +    enum:
> +      - qcom,ipq5018-bt
> +
> +  interrupts:
> +    items:
> +      - description:
> +          Interrupt line from the M0 Bluetooth Subsystem to the host processor

What is M0?

Anyway, this part feels completely redundant. Can "interrupts" property
be anything else than an interrupt line from the device to the host
processor?


> +          to notify it of events such as re

This feels useful, but cut/incomplete.

> +
> +  qcom,ipc:
> +    $ref: /schemas/types.yaml#/definitions/phandle-array
> +    items:
> +      - items:
> +          - description: phandle to a syscon node representing the APCS registers
> +          - description: u32 representing offset to the register within the syscon
> +          - description: u32 representing the ipc bit within the register
> +    description: |
> +      These entries specify the outgoing IPC bit used for signaling the remote
> +      M0 BTSS core of a host event or for sending an ACK if the remote processor
> +      expects it.
> +
> +  qcom,rproc:
> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description:
> +      Phandle to the remote processor node representing the M0 BTSS core.
> +
> +required:
> +  - compatible
> +  - interrupts
> +  - qcom,ipc
> +  - qcom,rproc
> +
> +allOf:
> +  - $ref: bluetooth-controller.yaml#
> +  - $ref: qcom,bluetooth-common.yaml
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +    bluetooth: bluetooth {

Drop unused label

> +      compatible = "qcom,ipq5018-bt";
> +
> +      qcom,ipc = <&apcs_glb 8 23>;
> +      interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;

No firmware to load?

It feels like remoteproc node split is fake. The property qcom,rproc is
even more supporting that case. Shouldn't this be simply one device -
bluetooth? What sort of two devices do you have exactly? How can I
identify them in the hardware?

> +
> +      qcom,rproc = <&m0_btss>;

Best regards,
Krzysztof


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

* Re: [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL
  2026-06-26 10:51     ` George Moussalem
@ 2026-06-26 11:16       ` Krzysztof Kozlowski
  0 siblings, 0 replies; 22+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-26 11:16 UTC (permalink / raw)
  To: George Moussalem
  Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
	linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
	linux-bluetooth, netdev, linux-remoteproc

On 26/06/2026 12:51, George Moussalem wrote:
>>
>> No supplies? no address space? How do you actually trigger remoteproc
>> startup?
> 
> No supplied and no address space. The core is booted by a
> qcom_scm_auth_and_reset call to TrustZone which authenticated the
> firmware, takes it out of reset and boots it.

Then commit msg could be improved:

"Firmware loaded is authenticated via TrustZone." ->
"Firmware is loaded and authenticated via TrustZone."


Best regards,
Krzysztof

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

* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
  2026-06-25 14:10 ` [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver George Moussalem via B4 Relay
  2026-06-25 14:18   ` Philipp Zabel
@ 2026-06-26 11:20   ` Konrad Dybcio
  2026-06-26 11:32     ` George Moussalem
  2026-06-26 14:10   ` sashiko-bot
  2 siblings, 1 reply; 22+ messages in thread
From: Konrad Dybcio @ 2026-06-26 11:20 UTC (permalink / raw)
  To: george.moussalem, Jens Axboe, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
	Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
	Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
	Konrad Dybcio, Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc

On 6/25/26 4:10 PM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Add support to bring up the M0 core of the bluetooth subsystem found in
> the IPQ5018 SoC.
> 
> The signed firmware loaded is authenticated by TrustZone. If successful,
> the M0 core boots the firmware and the peripheral is taken out of reset
> using a Secure Channel Manager call to TrustZone.
> 
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---

Can this not fit inside the existing PAS driver?

Konrad

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

* Re: [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-06-26 10:53   ` Krzysztof Kozlowski
@ 2026-06-26 11:20     ` George Moussalem
  2026-06-26 11:30       ` Konrad Dybcio
  0 siblings, 1 reply; 22+ messages in thread
From: George Moussalem @ 2026-06-26 11:20 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
	linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
	linux-bluetooth, netdev, linux-remoteproc

On 6/26/26 14:53, Krzysztof Kozlowski wrote:
> On Thu, Jun 25, 2026 at 06:10:08PM +0400, George Moussalem wrote:
>> Document the Qualcomm IPQ5018 Bluetooth controller.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>>  .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    | 63 ++++++++++++++++++++++
>>  1 file changed, 63 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
>> new file mode 100644
>> index 000000000000..afd33f851858
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
>> @@ -0,0 +1,63 @@
>> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/net/bluetooth/qcom,ipq5018-bt.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Qualcomm IPQ5018 Bluetooth
>> +
>> +maintainers:
>> +  - George Moussalem <george.moussalem@outlook.com>
>> +
>> +properties:
>> +  compatible:
>> +    enum:
>> +      - qcom,ipq5018-bt
>> +
>> +  interrupts:
>> +    items:
>> +      - description:
>> +          Interrupt line from the M0 Bluetooth Subsystem to the host processor
> 
> What is M0?

it's a low power Cortex M0 core, for Bluetooth processing in this case.

> 
> Anyway, this part feels completely redundant. Can "interrupts" property
> be anything else than an interrupt line from the device to the host
> processor?
> 
> 
>> +          to notify it of events such as re
> 
> This feels useful, but cut/incomplete.

yeah, c/p error. The interrupt is to notify the host of bluetooth events
running on the m0 core, such as TX/CMD completion and/or availability of
new data frames in the ring buffers.

> 
>> +
>> +  qcom,ipc:
>> +    $ref: /schemas/types.yaml#/definitions/phandle-array
>> +    items:
>> +      - items:
>> +          - description: phandle to a syscon node representing the APCS registers
>> +          - description: u32 representing offset to the register within the syscon
>> +          - description: u32 representing the ipc bit within the register
>> +    description: |
>> +      These entries specify the outgoing IPC bit used for signaling the remote
>> +      M0 BTSS core of a host event or for sending an ACK if the remote processor
>> +      expects it.
>> +
>> +  qcom,rproc:
>> +    $ref: /schemas/types.yaml#/definitions/phandle
>> +    description:
>> +      Phandle to the remote processor node representing the M0 BTSS core.
>> +
>> +required:
>> +  - compatible
>> +  - interrupts
>> +  - qcom,ipc
>> +  - qcom,rproc
>> +
>> +allOf:
>> +  - $ref: bluetooth-controller.yaml#
>> +  - $ref: qcom,bluetooth-common.yaml
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> +  - |
>> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
>> +
>> +    bluetooth: bluetooth {
> 
> Drop unused label

will drop

> 
>> +      compatible = "qcom,ipq5018-bt";
>> +
>> +      qcom,ipc = <&apcs_glb 8 23>;
>> +      interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
> 
> No firmware to load?

firmware is loaded by the remoteproc in patch 1

> 
> It feels like remoteproc node split is fake. The property qcom,rproc is
> even more supporting that case. Shouldn't this be simply one device -
> bluetooth? What sort of two devices do you have exactly? How can I
> identify them in the hardware?

I wasn't sure how to represent the HW. Should I make this bluetooth node
a childnode of the rproc? Essentially, this is the transport layer
(using shared memory space and IPC/interrupt).

Most QCA BT controllers are also childnodes of a serdev/uart node as
they use serdev for transport.

From what I understand, it's simply BT firmware running on this
dedicated M0 core in the SoC itself connected to an RF.

> 
>> +
>> +      qcom,rproc = <&m0_btss>;
> 
> Best regards,
> Krzysztof
> 


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

* Re: [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-06-26 11:20     ` George Moussalem
@ 2026-06-26 11:30       ` Konrad Dybcio
  0 siblings, 0 replies; 22+ messages in thread
From: Konrad Dybcio @ 2026-06-26 11:30 UTC (permalink / raw)
  To: George Moussalem, Krzysztof Kozlowski
  Cc: Jens Axboe, Ulf Hansson, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Johannes Berg, Jeff Johnson, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Balakrishna Godavarthi,
	Rocky Liao, Saravana Kannan, Andrew Lunn, Heiner Kallweit,
	Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Bjorn Andersson, Konrad Dybcio,
	Mathieu Poirier, Philipp Zabel, linux-block, linux-kernel,
	linux-mmc, devicetree, linux-wireless, ath10k, linux-arm-msm,
	linux-bluetooth, netdev, linux-remoteproc

On 6/26/26 1:20 PM, George Moussalem wrote:
> On 6/26/26 14:53, Krzysztof Kozlowski wrote:
>> On Thu, Jun 25, 2026 at 06:10:08PM +0400, George Moussalem wrote:
>>> Document the Qualcomm IPQ5018 Bluetooth controller.
>>>
>>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>>> ---

[...]

>>> +      compatible = "qcom,ipq5018-bt";
>>> +
>>> +      qcom,ipc = <&apcs_glb 8 23>;
>>> +      interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
>>
>> No firmware to load?
> 
> firmware is loaded by the remoteproc in patch 1
> 
>>
>> It feels like remoteproc node split is fake. The property qcom,rproc is
>> even more supporting that case. Shouldn't this be simply one device -
>> bluetooth? What sort of two devices do you have exactly? How can I
>> identify them in the hardware?
> 
> I wasn't sure how to represent the HW. Should I make this bluetooth node
> a childnode of the rproc? Essentially, this is the transport layer
> (using shared memory space and IPC/interrupt).
> 
> Most QCA BT controllers are also childnodes of a serdev/uart node as
> they use serdev for transport.
> 
> From what I understand, it's simply BT firmware running on this
> dedicated M0 core in the SoC itself connected to an RF.

Seems like this rhymes with the WPSS remoteproc +ATH1xK_AHB situation
- the Q6 core power sequences and manages the wireless controller,
while Linux gets to drive the device as it would if it were connected
over PCIe/ UART respectively, just with MMIO writes instead.

Konrad

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

* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
  2026-06-26 11:20   ` Konrad Dybcio
@ 2026-06-26 11:32     ` George Moussalem
  0 siblings, 0 replies; 22+ messages in thread
From: George Moussalem @ 2026-06-26 11:32 UTC (permalink / raw)
  To: Konrad Dybcio, Jens Axboe, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Johannes Berg, Jeff Johnson,
	Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, Saravana Kannan, Andrew Lunn,
	Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Bjorn Andersson,
	Konrad Dybcio, Mathieu Poirier, Philipp Zabel
  Cc: linux-block, linux-kernel, linux-mmc, devicetree, linux-wireless,
	ath10k, linux-arm-msm, linux-bluetooth, netdev, linux-remoteproc

On 6/26/26 15:20, Konrad Dybcio wrote:
> On 6/25/26 4:10 PM, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add support to bring up the M0 core of the bluetooth subsystem found in
>> the IPQ5018 SoC.
>>
>> The signed firmware loaded is authenticated by TrustZone. If successful,
>> the M0 core boots the firmware and the peripheral is taken out of reset
>> using a Secure Channel Manager call to TrustZone.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
> 
> Can this not fit inside the existing PAS driver?

I've tried but there were two issues with that:

1. a custom way to load the firmware into memory is required because the
loadable segment needs to be offset by the virtual address in the mbn
file (see 0x20250 below). The standard mdt_loader uses the physical
addresses.

readelf -l bt_fw_patch.mbn

Elf file type is EXEC (Executable file)
Entry point 0x20255
There are 3 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  NULL           0x000000 0x00000000 0x00000000 0x00094 0x00000     0
  NULL           0x001000 0x0001a000 0x0001a000 0x00088 0x01000     0x1000
  LOAD           0x002000 0x00020250 0x00000000 0x06154 0x190f8 RWE 0x4

2. memory needs to be ioremapped using ioremap, not ioremap_wc, else TZ
will complain and throw XPU violations due to strict memory alignment
and non-cache requirements.

> 
> Konrad

Cheers,
George

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

* Re: [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support
  2026-06-25 14:10 ` [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
@ 2026-06-26 14:10   ` sashiko-bot
  0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-06-26 14:10 UTC (permalink / raw)
  To: George Moussalem via B4 Relay; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Out-of-bounds read of tag_id and tag_len via integer underflow in qca_tlv_check_data.
--

commit 513dbc31af180171927da878907af8ede1372fc7
Author: George Moussalem <george.moussalem@outlook.com>

Bluetooth: btqca: Add IPQ5018 support

Add the IPQ5018 SoC type and support for loading its firmware.

> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
> --- a/drivers/bluetooth/btqca.c
> +++ b/drivers/bluetooth/btqca.c
> @@ -378,6 +378,9 @@ static int qca_tlv_check_data(struct hci_dev *hdev,
>  		break;
>  
>  	case TLV_TYPE_NVM:
> +		if (soc_type == QCA_IPQ5018)
> +			break;
> +
>  		if (fw_size < sizeof(struct tlv_type_hdr))
>  			return -EINVAL;

[ ... ]

>  		idx = 0;
>  		data = tlv->data;
>  		while (idx < length - sizeof(struct tlv_type_nvm)) {
>  			tlv_nvm = (struct tlv_type_nvm *)(data + idx);
> 
>  			tag_id = le16_to_cpu(tlv_nvm->tag_id);
>  			tag_len = le16_to_cpu(tlv_nvm->tag_len);

[Severity: High]
This is a pre-existing issue, but does this loop risk an out-of-bounds read
if length is less than the size of struct tlv_type_nvm?

Since sizeof(struct tlv_type_nvm) evaluates to an unsigned size_t, a small
length (like 0 or 1) will cause the subtraction to underflow and wrap around
to a very large positive value.

This would cause the while loop condition in qca_tlv_check_data() to
incorrectly evaluate to true. The assignments to tag_id and tag_len would
then read 4 bytes of memory, which could be out of bounds of the allocated
firmware buffer if a malformed or truncated TLV_TYPE_NVM file is processed.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com?part=3

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

* Re: [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
  2026-06-25 14:10 ` [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
@ 2026-06-26 14:10   ` sashiko-bot
  0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-06-26 14:10 UTC (permalink / raw)
  To: George Moussalem via B4 Relay; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Adding 'syscon' to the 'apcs_glb' compatible string list without updating the devicetree bindings causes a schema validation failure.
--

commit d7e5dd126ba08d18fdb72c0c8149b7aba5e36e82
Author: George Moussalem <george.moussalem@outlook.com>

arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support

Add nodes for the M0 remoteproc, reserved memory carveout, and Bluetooth
to bring up the M0 core and enable the Bluetooth Subsystem.

> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> index 6f8004a22a1ff..4fdf20c87b0a9 100644
> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi

[ ... ]

> @@ -647,7 +678,8 @@ watchdog: watchdog@b017000 {
>  
>  		apcs_glb: mailbox@b111000 {
>  			compatible = "qcom,ipq5018-apcs-apps-global",
> -				     "qcom,ipq6018-apcs-apps-global";
> +				     "qcom,ipq6018-apcs-apps-global",
> +				     "syscon";

[Severity: Low]
Will appending "syscon" to the apcs_glb compatible string list cause a
devicetree schema validation failure?

It looks like the qcom,apcs-kpss-global.yaml bindings enforce that the
qcom,ipq5018-apcs-apps-global compatible list must contain exactly two strings.
Running make dtbs_check might fail here without a corresponding update to the
YAML binding to permit this third item.

>  			reg = <0x0b111000 0x1000>;
>  			#clock-cells = <1>;
>  			clocks = <&a53pll>, <&xo_board_clk>, <&gcc GPLL0>;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com?part=6

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

* Re: [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-06-25 14:10 ` [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
  2026-06-26 10:53   ` Krzysztof Kozlowski
@ 2026-06-26 14:10   ` sashiko-bot
  1 sibling, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-06-26 14:10 UTC (permalink / raw)
  To: George Moussalem via B4 Relay; +Cc: conor+dt, robh, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The description for the 'interrupts' property is abruptly truncated mid-sentence.
--

commit 6f8911a9af73d725c2b86b32d5b5e404664a0809
Author: George Moussalem <george.moussalem@outlook.com>

dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller

This commit documents the Qualcomm IPQ5018 Bluetooth controller by adding a
new device tree binding schema.

> diff --git a/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
> new file mode 100644
> index 0000000000000..afd33f8518588
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
[ ... ]
> +properties:
> +  compatible:
> +    enum:
> +      - qcom,ipq5018-bt
> +
> +  interrupts:
> +    items:
> +      - description:
> +          Interrupt line from the M0 Bluetooth Subsystem to the host processor
> +          to notify it of events such as re

[Severity: Low]
Is this description abruptly truncated mid-sentence? It appears to cut off at
"events such as re".

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com?part=4

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

* Re: [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
  2026-06-25 14:10 ` [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
@ 2026-06-26 14:10   ` sashiko-bot
  0 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-06-26 14:10 UTC (permalink / raw)
  To: George Moussalem via B4 Relay; +Cc: devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 12 potential issue(s) to consider:
- [Critical] Integer underflow when computing auxiliary pointer lengths causes a heap buffer overflow.
- [Critical] Unvalidated shared memory offsets allow the M0 co-processor to trigger arbitrary out-of-bounds reads/writes on the host.
- [High] Missing locking in the TX path corrupts shared IPC ring buffers and state.
- [High] Lifecycle mismanagement of the workqueue and IRQ leads to memory leaks and broken interface toggling.
- [High] Device memory offsets are cast directly to host virtual pointers in ring buffer loops.
- [High] Off-by-one errors during HCI frame length validation allow out-of-bounds memory reads.
- [High] Directly dereferencing __iomem memory via struct pointers violates safe I/O access and triggers synchronous aborts.
- [High] Permanent leak of shared long message buffer capacity on failed short buffer acquires.
- [High] Unvalidated indices from shared memory can hang the workqueue thread in an infinite loop.
- [High] Lockless IPC ring buffers omit memory barriers, risking data corruption due to reordering.
- [Medium] Native arithmetic and bitwise operations on little-endian IPC structures break big-endian compatibility.
- [Medium] Missing acquire/release memory barriers around `desc->running` flag risk NULL pointer dereferences.
--

commit b13642cec5c040da0ea77113de5feea1556c0a79
Author: George Moussalem <george.moussalem@outlook.com>
Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver

This commit adds support for the Qualcomm IPQ5018 Bluetooth chip, using
shared memory for transport between the host and the M0 co-processor.
It implements ring buffers for communication and uses IPC registers
and interrupts for event notifications.

> --- /dev/null
> +++ b/drivers/bluetooth/btqcomipc.c
[ ... ]
> +static struct ring_buffer_info *btss_get_tx_rbuf(struct qcom_btss *desc,
> +						 bool *is_sbuf_full)
> +{
> +	u8 idx;
> +	struct ring_buffer_info *rinfo;
> +
> +	for (rinfo = &(desc->tx_ctxt->sring_buf_info);	rinfo != NULL;
> +		rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {

[Severity: High]
Can this direct pointer cast cause a panic? 

The firmware provides the next ring buffer via rinfo->next, which is an 
__le32 field in shared memory. 

Directly casting this 32-bit device offset to a host virtual pointer might
result in an unmapped address being dereferenced on the next loop iteration.

[ ... ]
> +static int btss_send(struct qcom_btss *desc, u16 msg_hdr,
> +		     struct sk_buff *skb)
> +{
[ ... ]
> +	if (len > IPC_MSG_PLD_SZ) {
> +		hdr |= IPC_HDR_LONG_MSG;
> +
> +		ptr_buf = btss_alloc_lmsg(desc, len,
> +					  &aux_ptr, &is_lbuf_full);
> +		if (IS_ERR(ptr_buf)) {
> +			bt_dev_err(hdev, "long msg buf full");
> +			hdev->stat.err_tx++;
> +			return PTR_ERR(ptr_buf);
> +		}
> +	}
> +
> +	rinfo = btss_get_tx_rbuf(desc, &is_sbuf_full);
> +	if (IS_ERR(rinfo)) {
> +		bt_dev_err(hdev, "short msg buf full");
> +		hdev->stat.err_tx++;
> +		return PTR_ERR(rinfo);
> +	}

[Severity: High]
Does returning early here permanently leak the long message buffer?

When transmitting a large packet, btss_alloc_lmsg() reserves space in the
long message ring. If the short ring is full and btss_get_tx_rbuf() returns
-EAGAIN, the network stack requeues the packet.

The successfully allocated long message space doesn't appear to be freed,
which could exhaust the IPC transport's capability upon retries.

[ ... ]
> +	rbuf->msg_hdr = cpu_to_le16(hdr);
> +	rbuf->len = cpu_to_le16(len);
> +
> +	rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;

[Severity: High]
Are memory barriers required here to prevent reordering? 

The IPC protocol uses lockless ring buffers across the CPU and coprocessor.
Without a wmb() before updating rinfo->widx, the M0 processor might perceive
the incremented write index before the payload writes are fully visible.

[ ... ]
> +static inline int btss_recv_hci_frame(struct qcom_btss *desc, const u8 *data, size_t len)
> +{
[ ... ]
> +	case HCI_EVENT_PKT:
> +	{
> +		if (len < HCI_EVENT_HDR_SIZE)
> +			return -EILSEQ;

[Severity: High]
Could this size check allow out-of-bounds reads?

The len parameter includes the 1-byte packet type prefix in data[0], but
validations like this one do not account for it. If len is exactly
HCI_EVENT_HDR_SIZE (2 bytes), the check passes.

Reading hdr->plen could then access out-of-bounds memory.

[ ... ]
> +	if (pkt_len > len)
> +		return -EINVAL;

[Severity: High]
Similarly, does this permit an out-of-bounds read during the skb data copy?

If pkt_len is exactly equal to len, the subsequent call to
skb_put_data(skb, data + 1, pkt_len) will copy len bytes from a buffer tail
that only contains len - 1 bytes.

[ ... ]
> +static inline int btss_process_rx(struct qcom_btss *desc,
> +				  struct ring_buffer_info *rinfo,
> +				  bool *ack, u8 *rx_count)
> +{
[ ... ]
> +	while (ridx != rinfo->widx) {

[Severity: High]
Can this loop hang the workqueue thread? 

The termination relies on ridx matching rinfo->widx, which is read from
shared memory. If the firmware provides a corrupted widx that is greater
than or equal to ring_buf_cnt, the modulo-wrapped ridx will never match it.

This could potentially cause an infinite loop, halting IPC processing.

[ ... ]
> +		if (rbuf->msg_hdr & IPC_HDR_LONG_MSG) {

[Severity: Medium]
Does this code safely handle endianness? 

The IPC protocol structures define sizes and offsets as explicit __le16 and
__le32 fields. Performing bitwise operations like rbuf->msg_hdr &
IPC_HDR_LONG_MSG without conversion could yield reversed bytes on big-endian
architectures.

> +			rxbuf = TO_APPS_ADDR(rbuf->payload.lmsg_data);

[Severity: Critical]
Does this translation of the payload offset trust the M0 firmware too much?

TO_APPS_ADDR() translates the untrusted 32-bit value into a direct host
pointer. If the coprocessor supplies a maliciously large offset, the pointer
could aim outside the ioremapped mem_region.

This could allow arbitrary out-of-bounds reads.

[ ... ]
> +				blks_consumed = desc->rx_ctxt->lmsg_buf_cnt -
> +					lbuf_idx;
> +				aux_ptr.len = rbuf->len - (blks_consumed * lsz);

[Severity: Critical]
Can this calculation underflow and cause a heap buffer overflow? 

Both blks_consumed and lsz are derived from inputs supplied by the
coprocessor. If (blks_consumed * lsz) is greater than rbuf->len,
aux_ptr.len will wrap to an excessively large 32-bit value.

[ ... ]
> +		case IPC_HDR_PKT_TYPE_HCI:
> +			buf = kmalloc(rbuf->len, GFP_ATOMIC);

[Severity: Medium]
As noted above, using the __le16 field rbuf->len directly in kmalloc() on
big-endian architectures could lead to excessively large allocation requests.

[ ... ]
> +			memcpy_fromio(buf, rxbuf, rbuf->len - aux_ptr.len);

[Severity: Critical]
If aux_ptr.len underflows, this subtraction evaluates back to the large
(blks_consumed * lsz) value. 

Since buf is only allocated to rbuf->len, could this copy operation
write far out-of-bounds?

[ ... ]
> +static void btss_worker(struct work_struct *work)
> +{
[ ... ]
> +	if (unlikely(!READ_ONCE(desc->running))) {
> +		// FW sets offset of RX context info at start of memory region upon boot
> +		offset = readl(desc->mem_region);
> +		dev_dbg(desc->dev, "offset after M0 boot: 0x%08x\n", offset);
> +		desc->rx_ctxt = (struct context_info *)(desc->mem_region + offset);

[Severity: Critical]
Does this offset need bounds checking? 

The offset is read directly from the coprocessor and used to set the context
pointer. If an abnormally large offset is provided, this could point outside
the allocated mem_size, leading to out-of-bounds accesses.

[Severity: High]
Does this directly dereference I/O memory using a host struct pointer?

The shared memory region is mapped as void __iomem, but here it is cast to
a native struct context_info pointer. Because the struct is packed, the
compiler might generate unaligned or byte-wise loads when its fields are
accessed. 

On architectures like ARM64, unaligned loads to strongly-ordered device
memory can trigger synchronous external aborts.

[ ... ]
> +static int btss_init(struct qcom_btss *desc)
> +{
[ ... ]
> +	desc->wq = create_singlethread_workqueue("btss_wq");
> +	if (!desc->wq) {
> +		dev_err(dev, "Failed to initialize workqueue\n");
> +		return -EAGAIN;
> +	}
> +
> +	INIT_WORK(&desc->work, btss_worker);
> +
> +	ret = devm_request_threaded_irq(dev, desc->irq, NULL, btss_irq_handler,
> +					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +					"btss_irq", desc);

[ ... ]
> +static int btqcomipc_close(struct hci_dev *hdev)
> +{
> +	struct qcom_btss *desc = hci_get_drvdata(hdev);
> +
> +	rproc_shutdown(desc->rproc);
> +	msleep(POWER_CONTROL_DELAY_MS);
> +
> +	return 0;
> +}

[Severity: High]
Does this implementation leak the workqueue and break interface toggling?

During btqcomipc_open(), btss_init() allocates a new workqueue and requests
the IRQ line. However, btqcomipc_close() doesn't tear down the workqueue or
free the IRQ. 

Toggling the interface via 'hciconfig hci0 down && hciconfig hci0 up'
would leak the workqueue, and the subsequent open might fail with -EBUSY
since the IRQ is already registered.

[ ... ]
> +static int btqcomipc_send(struct hci_dev *hdev, struct sk_buff *skb)
> +{
> +	u16 hdr = FIELD_PREP(IPC_HDR_PKT_TYPE_MASK, IPC_HDR_PKT_TYPE_HCI);
> +	struct qcom_btss *desc = hci_get_drvdata(hdev);
> +	int ret;
> +
> +	if (unlikely(!READ_ONCE(desc->running))) {

[Severity: Medium]
Is a memory barrier needed here to safely access the dependent context
pointers? 

In btss_recv_cust_frame(), pointers to shared context regions are populated
before calling WRITE_ONCE(desc->running, true). Without smp_store_release()
and smp_load_acquire(), weakly-ordered architectures could reorder the
execution.

This could allow the path below to observe desc->running as true while
reading a stale or NULL tx_ctxt.

> +		bt_dev_err(hdev, "BTSS not initialized, failed to send message");
> +		ret = -ENODEV;
> +		goto free_skb;
> +	}
> +
> +	ret = btss_send(desc, hdr, skb);

[Severity: High]
Can this lockless call corrupt the shared IPC ring buffers? 

btqcomipc_send() executes locklessly from the network stack context, while
btss_worker() can process the TX queue simultaneously. Both paths modify the
exact same ring buffer indices like rinfo->widx and internal state. 

Could this race lead to ring buffer corruption or packet loss without
acquiring desc->lock?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com?part=5

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

* Re: [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver
  2026-06-25 14:10 ` [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver George Moussalem via B4 Relay
  2026-06-25 14:18   ` Philipp Zabel
  2026-06-26 11:20   ` Konrad Dybcio
@ 2026-06-26 14:10   ` sashiko-bot
  2 siblings, 0 replies; 22+ messages in thread
From: sashiko-bot @ 2026-06-26 14:10 UTC (permalink / raw)
  To: George Moussalem via B4 Relay; +Cc: devicetree, conor+dt, robh

Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] Redundant request_firmware call leaks newly allocated firmware and metadata on success path.
- [High] Direct return on kstrdup failure leaks previously acquired metadata and firmware objects.
- [High] Heap buffer underflow and overflow when formatting split segment filenames with sprintf.
- [High] 32-bit integer overflow in ELF segment bounds validation allows out-of-bounds memory writes.
- [High] Missing size validation for split firmware segments leads to out-of-bounds reads.
- [High] Out-of-bounds read due to unvalidated ELF headers and hardcoded program header offset.
- [High] Time-of-Check to Time-of-Use (TOCTOU) vulnerability due to parsing ELF headers from a different firmware copy than the one authenticated.
- [Medium] Subsystem guide violation: Mixing goto-based error handling with scope-based __free() cleanup.
- [Medium] Failure to zero-initialize the BSS section of ELF segments exposes stale memory to the peripheral.
- [Medium] Missing reset assertion on late probe failures and driver removal leads to resource leaks and state mismatch.
--

commit a3d2212c4a13a5646a7fb048e4d391fc0784f11e
Author: George Moussalem <george.moussalem@outlook.com>
Subject: remoteproc: qcom: Add M0 BTSS secure PIL driver

This commit introduces a new Peripheral Image Loader driver for the M0
bluetooth subsystem found in the IPQ5018 SoC. The driver brings up the
M0 core by loading signed firmware, authenticating it via TrustZone,
and starting the peripheral using a Secure Channel Manager call.

> diff --git a/drivers/remoteproc/qcom_m0_btss_pil.c b/drivers/remoteproc/qcom_m0_btss_pil.c
> new file mode 100644
> index 0000000000000..7168e270e4d46
> --- /dev/null
> +++ b/drivers/remoteproc/qcom_m0_btss_pil.c

[ ... ]

> +static int m0_btss_load(struct rproc *rproc, const struct firmware *fw)
> +{
> +	struct m0_btss *desc = rproc->priv;
> +	const struct elf32_phdr *phdrs;
> +	const struct firmware *seg_fw;
> +	const struct elf32_phdr *phdr;
> +	const struct elf32_hdr *ehdr;
> +	void __iomem *metadata;
> +	size_t metadata_size;
> +	int i, ret;
> +
> +	ehdr = (const struct elf32_hdr *)fw->data;
> +	phdrs = (const struct elf32_phdr *)(ehdr + 1);

[Severity: High]
Does this code perform an out-of-bounds read if the firmware file is smaller
than the ELF header structure? The pointer is cast and dereferenced without
validating that the provided firmware is large enough.

[Severity: High]
Is there an issue with using a hardcoded offset to locate the program headers
instead of using the standard e_phoff field from the ELF header? If the file
uses a non-standard layout, it could parse arbitrary memory as program
headers.

> +
> +	ret = request_firmware(&fw, rproc->firmware, rproc->dev.parent);

[Severity: High]
Does this code introduce a time-of-check to time-of-use vulnerability? The
ehdr and phdrs pointers are established from the original fw parameter, but
then a new fw copy is requested and authenticated by TrustZone. If the file
changes between these reads, could malicious segment locations from the first
unauthenticated read be used to subvert the loading of the authenticated data?

[Severity: High]
Is this call to request_firmware() redundantly overwriting the local fw
parameter, preventing the original structure from being accessed?

> +	if (ret) {
> +		dev_err(rproc->dev.parent, "Failed to request firmware: %d\n",
> +			ret);
> +		return ret;
> +	}
> +
> +	metadata = qcom_mdt_read_metadata(fw, &metadata_size, rproc->firmware,
> +					  rproc->dev.parent);
> +	if (IS_ERR(metadata)) {
> +		ret = PTR_ERR(metadata);
> +		dev_err(rproc->dev.parent,
> +			"Failed to read firmware metadata: %d\n", ret);
> +		goto release_fw;
> +	}
> +
> +	ret = qcom_scm_pas_init_image(BTSS_PAS_ID, metadata,
> +				      metadata_size, NULL);
> +	if (ret) {
> +		dev_err(rproc->dev.parent, "PAS init image failed: %d\n", ret);
> +		goto free_metadata;
> +	}
> +
> +	for (i = 0; i < ehdr->e_phnum; i++) {
> +		char *seg_name __free(kfree) = kstrdup(rproc->firmware,
> +						       GFP_KERNEL);

[Severity: Medium]
Is it safe to mix goto-based error handling with scope-based cleanup helpers
like __free(kfree) in the same function? The kernel cleanup subsystem
guidelines indicate that functions should either use goto-based cleanup
exclusively, or scope-based cleanup exclusively, to prevent confusing
ownership semantics.

> +		if (!seg_name)
> +			return -ENOMEM;

[Severity: High]
Does returning directly here leak the metadata buffer and the locally
requested fw structure? It appears this direct return bypasses the
free_metadata and release_fw cleanup labels.

> +
> +		phdr = &phdrs[i];
> +
> +		/* Only process valid loadable data segments */
> +		if (phdr->p_type != PT_LOAD || !phdr->p_memsz)
> +			continue;
> +
> +		if (phdr->p_vaddr + phdr->p_filesz > desc->mem_size) {

[Severity: High]
Can the addition of phdr->p_vaddr and phdr->p_filesz overflow? Since both
are read directly from the ELF header as 32-bit integers, a crafted firmware
could provide values that wrap around the 32-bit limit, passing the bounds
check against desc->mem_size and allowing out-of-bounds memory writes.

> +			dev_err(rproc->dev.parent,
> +				"Segment data exceeds the reserved memory area!\n");
> +			goto free_metadata;
> +		}
> +
> +		/* Check if firmware is split across multiple segment files */
> +		if (phdr->p_offset > fw->size ||
> +		    phdr->p_offset + phdr->p_filesz > fw->size) {
> +			sprintf(seg_name + strlen(seg_name) - 3, "b%02d", i);

[Severity: High]
Could this sprintf() call result in a heap buffer underflow or overflow?
If the provided firmware-name is fewer than 3 characters long, the pointer
arithmetic could underflow. Additionally, if the unvalidated ehdr->e_phnum
produces a large index, the formatted string could overflow the allocated
buffer.

> +			ret = request_firmware(&seg_fw, seg_name,
> +					       rproc->dev.parent);
> +			if (ret) {
> +				dev_err(rproc->dev.parent,
> +					"Could not find split segment binary: %s\n",
> +					seg_name);
> +				goto free_metadata;
> +			}
> +
> +			/*
> +			 * Use the virtual instead of the physical address as
> +			 * the offset
> +			 */
> +			memcpy_toio(desc->mem_region + phdr->p_vaddr,
> +				    seg_fw->data, phdr->p_filesz);

[Severity: High]
Does this memcpy_toio() read past the end of seg_fw->data? It uses
phdr->p_filesz from the main ELF header, but there doesn't seem to be a check
ensuring that the loaded seg_fw->size is actually at least that large.

> +
> +			release_firmware(seg_fw);
> +		} else {
> +			memcpy_toio(desc->mem_region + phdr->p_vaddr,
> +				    fw->data + phdr->p_offset, phdr->p_filesz);

[Severity: Medium]
If an ELF segment's memory size (p_memsz) exceeds its file size (p_filesz),
does the remaining memory need to be zero-initialized for the BSS section?
The driver currently only copies p_filesz bytes, which might leave stale
kernel memory exposed to the peripheral.

> +		}
> +	}
> +
> +	return 0;

[Severity: High]
Does this success path leak the dynamically allocated metadata buffer and the
firmware structure? The function returns directly without freeing them.

> +
> +free_metadata:
> +	kfree(metadata);
> +release_fw:
> +	release_firmware(fw);
> +	return ret;
> +}

[ ... ]

> +static int m0_btss_pil_probe(struct platform_device *pdev)
> +{

[ ... ]

> +	desc->btss_reset = devm_reset_control_get(dev, "btss_reset");
> +	if (IS_ERR_OR_NULL(desc->btss_reset))
> +		return dev_err_probe(dev, PTR_ERR(desc->btss_reset),
> +				     "unable to acquire btss_reset\n");
> +
> +	ret = reset_control_deassert(desc->btss_reset);

[Severity: Medium]
Is a cleanup action needed to assert the reset if probe fails later or if the
driver is unbound? The probe function deasserts desc->btss_reset but
doesn't seem to register a corresponding devm_add_action_or_reset() to handle
teardown.

> +	if (ret)
> +		return dev_err_probe(rproc->dev.parent, ret,
> +				     "Failed to deassert reset\n");
> +
> +	rproc->auto_boot = false;
> +	ret = devm_rproc_add(dev, rproc);
> +	if (ret)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, rproc);
> +
> +	return 0;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com?part=2

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

end of thread, other threads:[~2026-06-26 14:10 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-25 14:10 [PATCH 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
2026-06-25 14:10 ` [PATCH 1/6] dt-bindings: remoteproc: document M0 Bluetooth Subsystem secure PIL George Moussalem via B4 Relay
2026-06-26 10:47   ` Krzysztof Kozlowski
2026-06-26 10:51     ` George Moussalem
2026-06-26 11:16       ` Krzysztof Kozlowski
2026-06-25 14:10 ` [PATCH 2/6] remoteproc: qcom: Add M0 BTSS secure PIL driver George Moussalem via B4 Relay
2026-06-25 14:18   ` Philipp Zabel
2026-06-25 14:24     ` George Moussalem
2026-06-26 11:20   ` Konrad Dybcio
2026-06-26 11:32     ` George Moussalem
2026-06-26 14:10   ` sashiko-bot
2026-06-25 14:10 ` [PATCH 3/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
2026-06-26 14:10   ` sashiko-bot
2026-06-25 14:10 ` [PATCH 4/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
2026-06-26 10:53   ` Krzysztof Kozlowski
2026-06-26 11:20     ` George Moussalem
2026-06-26 11:30       ` Konrad Dybcio
2026-06-26 14:10   ` sashiko-bot
2026-06-25 14:10 ` [PATCH 5/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
2026-06-26 14:10   ` sashiko-bot
2026-06-25 14:10 ` [PATCH 6/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
2026-06-26 14:10   ` sashiko-bot

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