Devicetree
 help / color / mirror / Atom feed
* [PATCH v3 0/6] Add support for IPQ5018 Bluetooth
@ 2026-07-03  5:01 George Moussalem via B4 Relay
  2026-07-03  5:01 ` [PATCH v3 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-03  5:01 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	George Moussalem, Krzysztof Kozlowski, Bartosz Golaszewski

Hello,

This patch series introduces Bluetooth support for IPQ5018.

Bluetooth 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 Bluetooth controller.
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>
---
Changes in v3:
- Consolidated the SCM and helper call into one function which now
  checks availability of the SCM call inline.
- Changed argument to boolean type to set ECO power mode on or off as
  opposed to passing a raw power mode value.
- Removed unneeded parentheses in if statement
- Declared aux_ptr as a zero-initialized struct as opposed to using
  memset
- Changed for loop to a while loop for better readability
- Reordered function definition of btqcomipc_update_stats to avoid the
  need for a forward declaration.
- Removed unneeded header files from btqcomipc.c (bits.h, device.h,
  init.h, kernel.h, of_irq.h, types.h)
- Link to v2: https://lore.kernel.org/r/20260629-ipq5018-bluetooth-v2-0-02770f03b6bb@outlook.com

Changes in v2:
- removed unused struct member btss_reset from m0_btss struct.
- used devm_reset_control_get_exclusive_deasserted to acquire and
  deassert btss_reset in one call. No need to explicitly assert upon
  unbind since devres will handle it.
- As per Bjorn's comments, collapsed the rproc and BT driver into one
  which now handles firmware loading and transport.
- As per further review comments and above change, squashed the two
  devicetree bindings into one to represent the Bluetooth controller
- Added new SCM call to drive Bluetooth power modes. Based on testing on
  more router boards, this is required during start/stop sequences to
  avoid the controller hanging. This is only supported on relatively
  newer QSEE versions, so the SCM call is checked for availability
  before use.
- Refactored resource lifecycle management in the driver so that IRQ and
  work queue are managed at the driver level (probe/remove) while FW
  loading and powering on/off the controller is handled upon hdev
  open/close.
- Consolidated TX send functions for custom IPC and HCI frames into one
  and solved a potential NULL pointer dereference issue under TX
  pressure.
- Replace code to load and initialize firmware metadate by existing
  qcom_mdt_pas_init function. 
- Solved an off byone calculation error in the RX parser.
- Added entry to MAINTAINERS file
- Link to v1: https://lore.kernel.org/r/20260625-ipq5018-bluetooth-v1-0-d999be0e04f7@outlook.com

---
George Moussalem (6):
      dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
      Bluetooth: btqca: Add IPQ5018 support
      firmware: qcom: scm: Add support for setting Bluetooth power modes
      Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
      arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
      MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver

 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    |   86 ++
 MAINTAINERS                                        |    7 +
 arch/arm64/boot/dts/qcom/ipq5018.dtsi              |   25 +-
 drivers/bluetooth/Kconfig                          |   11 +
 drivers/bluetooth/Makefile                         |    1 +
 drivers/bluetooth/btqca.c                          |   16 +
 drivers/bluetooth/btqca.h                          |    3 +
 drivers/bluetooth/btqcomipc.c                      | 1041 ++++++++++++++++++++
 drivers/firmware/qcom/qcom_scm.c                   |   44 +
 drivers/firmware/qcom/qcom_scm.h                   |    1 +
 include/linux/firmware/qcom/qcom_scm.h             |    1 +
 11 files changed, 1235 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] 20+ messages in thread

* [PATCH v3 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-07-03  5:01 [PATCH v3 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
@ 2026-07-03  5:01 ` George Moussalem via B4 Relay
  2026-07-03  8:32   ` Bartosz Golaszewski
  2026-07-03  5:01 ` [PATCH v3 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-03  5:01 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	George Moussalem, Krzysztof Kozlowski

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

Document the Qualcomm IPQ5018 Bluetooth controller.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    | 86 ++++++++++++++++++++++
 1 file changed, 86 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..2119162994e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
@@ -0,0 +1,86 @@
+# 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
+
+  clocks:
+    items:
+      - description: Bluetooth Subsystem low power oscillator clock
+
+  clock-names:
+    items:
+      - const: lpo
+
+  firmware-name:
+    maxItems: 1
+
+  memory-region:
+    items:
+      - description: |
+          Reserved memory carveout for firmware loading and runtime data
+          transport between the host and the Bluetooth controller.
+
+  interrupts:
+    maxItems: 1
+
+  qcom,ipc:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: syscon node representing the APCS registers
+          - description: offset to the register within the syscon
+          - description: IPC bit within the register
+    description: |
+      The outgoing IPC bit used for signaling the Bluetooth controller of a host
+      event or for sending an ACK if the remote processor expects it.
+
+  resets:
+    items:
+      - description: Bluetooth Subsystem reset
+
+required:
+  - compatible
+  - clocks
+  - clock-names
+  - firmware-name
+  - interrupts
+  - qcom,ipc
+  - resets
+
+allOf:
+  - $ref: bluetooth-controller.yaml#
+  - $ref: qcom,bluetooth-common.yaml
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-ipq5018.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/reset/qcom,gcc-ipq5018.h>
+
+    bluetooth {
+      compatible = "qcom,ipq5018-bt";
+
+      firmware-name = "qca/bt_fw_patch.mbn";
+
+      clocks = <&gcc GCC_BTSS_LPO_CLK>;
+      clock-names = "lpo";
+      resets = <&gcc GCC_BTSS_BCR>;
+
+      qcom,ipc = <&apcs_glb 8 23>;
+      interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+      memory-region = <&btss_region>;
+    };

-- 
2.53.0



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

* [PATCH v3 2/6] Bluetooth: btqca: Add IPQ5018 support
  2026-07-03  5:01 [PATCH v3 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
  2026-07-03  5:01 ` [PATCH v3 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
@ 2026-07-03  5:01 ` George Moussalem via B4 Relay
  2026-07-03 23:32   ` Dmitry Baryshkov
  2026-07-03  5:01 ` [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-03  5:01 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	George Moussalem, Bartosz Golaszewski

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

Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
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] 20+ messages in thread

* [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-03  5:01 [PATCH v3 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
  2026-07-03  5:01 ` [PATCH v3 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
  2026-07-03  5:01 ` [PATCH v3 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
@ 2026-07-03  5:01 ` George Moussalem via B4 Relay
  2026-07-03  5:13   ` sashiko-bot
                     ` (3 more replies)
  2026-07-03  5:01 ` [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
                   ` (2 subsequent siblings)
  5 siblings, 4 replies; 20+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-03  5:01 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	George Moussalem, Bartosz Golaszewski

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

The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
modes which are required to be configured through a Secure Channel
Manager (SCM) call to TrustZone. However, not all Trusted Execution
Environment (QSEE) images support this call, so first check if the call
is available.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 drivers/firmware/qcom/qcom_scm.c       | 44 ++++++++++++++++++++++++++++++++++
 drivers/firmware/qcom/qcom_scm.h       |  1 +
 include/linux/firmware/qcom/qcom_scm.h |  1 +
 3 files changed, 46 insertions(+)

diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 6b601a4b89db..a34e0f75df3c 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -1094,6 +1094,50 @@ bool qcom_scm_pas_supported(u32 pas_id)
 }
 EXPORT_SYMBOL_GPL(qcom_scm_pas_supported);
 
+/**
+ * qcom_scm_pas_set_bluetooth_power_mode() - Configure power mode for the
+ *					     Bluetooth subsystem (BTSS)
+ * @pas_id:	peripheral authentication service id
+ * @eco_mode:	true for ECO mode, false for normal operation
+ *
+ * Return: 0 on success, negative errno on failure.
+ * Returns -EOPNOTSUPP if the firmware configuration call is unavailable.
+ */
+int qcom_scm_pas_set_bluetooth_power_mode(u32 pas_id, bool eco_mode)
+{
+	struct qcom_scm_desc desc = {
+		.svc = QCOM_SCM_SVC_PIL,
+		.cmd = QCOM_SCM_PIL_PAS_BT_PWR_MODE,
+		.arginfo = QCOM_SCM_ARGS(2),
+		.args[0] = pas_id,
+		.args[1] = eco_mode ? 0x4 : 0x0,
+		.owner = ARM_SMCCC_OWNER_SIP,
+	};
+	struct qcom_scm_res res;
+	int ret;
+
+	if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
+					  QCOM_SCM_PIL_PAS_BT_PWR_MODE))
+		return -EOPNOTSUPP;
+
+	ret = qcom_scm_clk_enable();
+	if (ret)
+		return ret;
+
+	ret = qcom_scm_bw_enable();
+	if (ret)
+		goto disable_clk;
+
+	ret = qcom_scm_call(__scm->dev, &desc, &res);
+	qcom_scm_bw_disable();
+
+disable_clk:
+	qcom_scm_clk_disable();
+
+	return ret ? : res.result[0];
+}
+EXPORT_SYMBOL_GPL(qcom_scm_pas_set_bluetooth_power_mode);
+
 static int __qcom_scm_pas_mss_reset(struct device *dev, bool reset)
 {
 	struct qcom_scm_desc desc = {
diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
index caab80a73e17..5579df5a2aca 100644
--- a/drivers/firmware/qcom/qcom_scm.h
+++ b/drivers/firmware/qcom/qcom_scm.h
@@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
 #define QCOM_SCM_PIL_PAS_SHUTDOWN	0x06
 #define QCOM_SCM_PIL_PAS_IS_SUPPORTED	0x07
 #define QCOM_SCM_PIL_PAS_MSS_RESET	0x0a
+#define QCOM_SCM_PIL_PAS_BT_PWR_MODE	0x21
 #define QCOM_SCM_PIL_PAS_GET_RSCTABLE	0x21
 
 #define QCOM_SCM_SVC_IO			0x05
diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmware/qcom/qcom_scm.h
index 5747bd191bf1..aca937ac8ddd 100644
--- a/include/linux/firmware/qcom/qcom_scm.h
+++ b/include/linux/firmware/qcom/qcom_scm.h
@@ -93,6 +93,7 @@ struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *c
 						  size_t *output_rt_size);
 
 int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx);
+int qcom_scm_pas_set_bluetooth_power_mode(u32 pas_id, bool eco_mode);
 
 int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);
 int qcom_scm_io_writel(phys_addr_t addr, unsigned int val);

-- 
2.53.0



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

* [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
  2026-07-03  5:01 [PATCH v3 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (2 preceding siblings ...)
  2026-07-03  5:01 ` [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
@ 2026-07-03  5:01 ` George Moussalem via B4 Relay
  2026-07-03  5:15   ` sashiko-bot
  2026-07-03 12:55   ` Bartosz Golaszewski
  2026-07-03  5:01 ` [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
  2026-07-03  5:01 ` [PATCH v3 6/6] MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver George Moussalem via B4 Relay
  5 siblings, 2 replies; 20+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-03  5:01 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	George Moussalem

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

Add support for the Bluetooth controller found in the IPQ5018 SoC.
This driver implements firmware loading and the transport layer between
the HCI core and the Bluetooth controller.

The firmware is loaded by the host into the 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 controller.

An outgoing APCS IPC bit and an incoming GIC interrupt handle host/guest
signaling.

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

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e8c24d757e9..c9785f43c87c 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 load firmware and bridge HCI data onto shared
+	  memory between the host and the Bluetooth controller.
+
+	  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..191937ccbd78
--- /dev/null
+++ b/drivers/bluetooth/btqcomipc.c
@@ -0,0 +1,1041 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/firmware.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/mdt_loader.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 BTSS_PAS_ID		0xc
+
+#define BTSS_PWR_CTRL_DELAY_MS	50
+
+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 hci_dev *hdev;
+
+	struct regmap *regmap;
+	u32 offset;
+	u32 bit;
+	int irq;
+
+	const char *firmware;
+	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)
+{
+	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 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)
+{
+	struct ring_buffer_info *rinfo;
+	u8 idx;
+
+	rinfo = &(desc->tx_ctxt->sring_buf_info);
+	while (rinfo != NULL) {
+		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;
+		}
+
+		rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next);
+	}
+
+	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 ipc_aux_ptr aux_ptr = { };
+	struct ring_buffer_info *rinfo;
+	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 ? skb->len + 1 : 0;
+
+	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, "TX long buffers 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, "TX short buffers full");
+		hdev->stat.err_tx++;
+		return PTR_ERR(rinfo);
+	}
+
+	rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(rinfo->rbuf)))[rinfo->widx];
+
+	if (!skb)
+		goto complete_tx;
+
+	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 free buffer count is low, send ACK request to signal to the
+	 * firmware to process and free up queued buffers in the TX ring.
+	 */
+	if (is_sbuf_full || is_lbuf_full)
+		hdr |= IPC_HDR_REQ_ACK;
+
+complete_tx:
+	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_recv_cust_frame(struct qcom_btss *desc, u8 cmd)
+{
+	u16 msg_hdr = 0;
+	int ret;
+
+	switch (cmd) {
+	case IPC_CMD_STOP:
+		spin_unlock(&desc->lock);
+		ret = qcom_scm_pas_set_bluetooth_power_mode(BTSS_PAS_ID, true);
+		spin_lock(&desc->lock);
+		if (ret && ret != -EOPNOTSUPP) {
+			bt_dev_err(desc->hdev,
+				   "Failed to apply BTSS power-save mode: %d",
+				   ret);
+			return ret;
+		}
+
+		WRITE_ONCE(desc->running, false);
+
+		msg_hdr |= cmd;
+		ret = btss_send(desc, msg_hdr, NULL);
+		if (ret)
+			bt_dev_err(desc->hdev,
+				   "Failed to send control message");
+		break;
+	case IPC_CMD_START:
+		spin_unlock(&desc->lock);
+		ret = qcom_scm_pas_set_bluetooth_power_mode(BTSS_PAS_ID, false);
+		spin_lock(&desc->lock);
+		if (ret && ret != -EOPNOTSUPP) {
+			bt_dev_err(desc->hdev,
+				   "Failed to apply BTSS active power mode: %d",
+				   ret);
+			return ret;
+		}
+
+		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);
+		break;
+	default:
+		bt_dev_err(desc->hdev, "Unsupported CMD ID: %u", cmd);
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+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 < 1 + 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 < 1 + 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 < 1 + 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 < 1 + 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 + 1 > 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);
+			ret = btss_recv_cust_frame(desc, cmd);
+			if (ret)
+				bt_dev_warn(desc->hdev,
+					    "Failed to process custom frame: %d",
+					    ret);
+			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;
+
+	spin_lock(&desc->lock);
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		/*
+		 * FW sets offset of RX context info at the start of the memory
+		 * region upon boot
+		 */
+		offset = readl(desc->mem_region);
+		dev_dbg(desc->dev, "offset after firmware 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 btcomqipc_firmware_load(struct qcom_btss *desc)
+{
+	const struct elf32_phdr *phdrs;
+	const struct firmware *seg_fw;
+	const struct elf32_phdr *phdr;
+	const struct elf32_hdr *ehdr;
+	const struct firmware *fw;
+	int i, ret;
+
+	ret = request_firmware(&fw, desc->firmware, desc->dev);
+	if (ret) {
+		dev_err(desc->dev, "Failed to request firmware: %d\n",
+			ret);
+		return ret;
+	}
+
+	ehdr = (const struct elf32_hdr *)fw->data;
+	phdrs = (const struct elf32_phdr *)(ehdr + 1);
+
+	ret = qcom_mdt_pas_init(desc->dev, fw, desc->firmware,
+				BTSS_PAS_ID, desc->mem_phys, NULL);
+	if (ret) {
+		dev_err(desc->dev, "PAS init failed: %d\n", ret);
+		goto release_fw;
+	}
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		char *seg_name __free(kfree) = kstrdup(desc->firmware,
+						       GFP_KERNEL);
+		if (!seg_name) {
+			ret = -ENOMEM;
+			goto release_fw;
+		}
+
+		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(desc->dev,
+				"Segment data exceeds the reserved memory area!\n");
+			goto release_fw;
+		}
+
+		/* 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,
+					       desc->dev);
+			if (ret) {
+				dev_err(desc->dev,
+					"Could not find split segment binary: %s\n",
+					seg_name);
+				goto release_fw;
+			}
+
+			/*
+			 * 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);
+		}
+	}
+
+release_fw:
+	release_firmware(fw);
+	return ret;
+}
+
+static int btqcomipc_open(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+	int ret;
+
+	if (!qcom_scm_pas_supported(BTSS_PAS_ID)) {
+		bt_dev_err(hdev,
+			   "PAS is not available for peripheral: 0x%x",
+			   BTSS_PAS_ID);
+		return -ENODEV;
+	}
+
+	ret = btcomqipc_firmware_load(desc);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to load firmware: %d", ret);
+		return ret;
+	}
+
+	/* Boot firmware */
+	ret = qcom_scm_pas_auth_and_reset(BTSS_PAS_ID);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to boot firmware: %d", ret);
+		return ret;
+	}
+
+	msleep(BTSS_PWR_CTRL_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)
+{
+	int ret;
+
+	ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to stop firmware: %d", ret);
+		return ret;
+	}
+
+	msleep(BTSS_PWR_CTRL_DELAY_MS);
+
+	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_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_deinit(struct qcom_btss *desc)
+{
+	disable_irq(desc->irq);
+	if (desc->wq != NULL) {
+		flush_workqueue(desc->wq);
+		skb_queue_purge(&desc->tx_q);
+		destroy_workqueue(desc->wq);
+		desc->wq = NULL;
+	}
+}
+
+static int btqcomipc_alloc_memory_region(struct qcom_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 btqcomipc_probe(struct platform_device *pdev)
+{
+	struct reset_control *btss_reset;
+	struct device *dev = &pdev->dev;
+	struct qcom_btss *desc;
+	struct hci_dev *hdev;
+	unsigned int args[2];
+	struct clk *lpo_clk;
+	int ret;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc)
+		return -ENOMEM;
+
+	desc->dev = dev;
+
+	ret = of_property_read_string(dev->of_node, "firmware-name",
+				      &desc->firmware);
+	if (ret < 0)
+		return ret;
+
+	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];
+
+	lpo_clk = devm_clk_get_enabled(dev, "lpo");
+	if (IS_ERR(lpo_clk))
+		return dev_err_probe(dev, PTR_ERR(lpo_clk),
+				     "Failed to get lpo clock\n");
+
+	btss_reset = devm_reset_control_get_exclusive_deasserted(dev, NULL);
+	if (IS_ERR_OR_NULL(btss_reset))
+		return dev_err_probe(dev, PTR_ERR(btss_reset),
+				     "unable to deassert reset\n");
+
+	desc->irq = platform_get_irq(pdev, 0);
+	if (desc->irq < 0)
+		return dev_err_probe(dev, desc->irq, "Failed to acquire IRQ\n");
+
+	ret = btqcomipc_init(desc);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to initialize\n");
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		btqcomipc_deinit(desc);
+		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->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) {
+		btqcomipc_deinit(desc);
+		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)
+		return;
+
+	btqcomipc_deinit(desc);
+
+	if (desc->hdev) {
+		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] 20+ messages in thread

* [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
  2026-07-03  5:01 [PATCH v3 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (3 preceding siblings ...)
  2026-07-03  5:01 ` [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
@ 2026-07-03  5:01 ` George Moussalem via B4 Relay
  2026-07-03  5:10   ` sashiko-bot
                     ` (2 more replies)
  2026-07-03  5:01 ` [PATCH v3 6/6] MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver George Moussalem via B4 Relay
  5 siblings, 3 replies; 20+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-03  5:01 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	George Moussalem

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

Add nodes for the reserved memory carveout and Bluetooth.

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

diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
index 6f8004a22a1f..65a47ba7d3a3 100644
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -17,6 +17,23 @@ / {
 	#address-cells = <2>;
 	#size-cells = <2>;
 
+	bluetooth: bluetooth {
+		compatible = "qcom,ipq5018-bt";
+
+		firmware-name = "qca/bt_fw_patch.mbn";
+
+		clocks = <&gcc GCC_BTSS_LPO_CLK>;
+		clock-names = "lpo";
+		resets = <&gcc GCC_BTSS_BCR>;
+
+		qcom,ipc = <&apcs_glb 8 23>;
+		interrupts = <GIC_SPI 162 IRQ_TYPE_EDGE_RISING>;
+
+		memory-region = <&btss_region>;
+
+		status = "disabled";
+	};
+
 	clocks {
 		gephy_rx_clk: gephy-rx-clk {
 			compatible = "fixed-clock";
@@ -136,6 +153,11 @@ reserved-memory {
 		#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 +669,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] 20+ messages in thread

* [PATCH v3 6/6] MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver
  2026-07-03  5:01 [PATCH v3 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (4 preceding siblings ...)
  2026-07-03  5:01 ` [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
@ 2026-07-03  5:01 ` George Moussalem via B4 Relay
  2026-07-03  8:33   ` Bartosz Golaszewski
  5 siblings, 1 reply; 20+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-03  5:01 UTC (permalink / raw)
  To: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	George Moussalem

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

Add maintainers entry for Qualcomm IPQ5018 Bluetooth driver.

Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 0b9d7c8276ac..60f7251d1a16 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22289,6 +22289,13 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/regulator/vqmmc-ipq4019-regulator.yaml
 F:	drivers/regulator/vqmmc-ipq4019-regulator.c
 
+QUALCOMM IPQ5018 BLUETOOTH DRIVER
+M:	George Moussalem <george.moussalem@outlook.com>
+L:	linux-bluetooth@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
+F:	drivers/bluetooth/btqcomipc.c
+
 QUALCOMM IRIS VIDEO ACCELERATOR DRIVER
 M:	Vikash Garodia <vikash.garodia@oss.qualcomm.com>
 M:	Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com>

-- 
2.53.0



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

* Re: [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
  2026-07-03  5:01 ` [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
@ 2026-07-03  5:10   ` sashiko-bot
  2026-07-03  8:33   ` Bartosz Golaszewski
  2026-07-03 10:38   ` Konrad Dybcio
  2 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-07-03  5:10 UTC (permalink / raw)
  To: George Moussalem; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Adding the 'syscon' compatible string to the 'apcs_glb' node without updating the device tree binding schema violates the schema and causes dtbs_check failures.
--

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

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

This commit adds device tree nodes for the reserved memory carveout and
Bluetooth on the ipq5018 platform. It also modifies the apcs_glb
mailbox node to include "syscon" in its compatible list.

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

[ ... ]

> @@ -647,7 +669,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 adding "syscon" here without updating the device tree binding schema
cause a make dtbs_check validation failure?

Looking at Documentation/devicetree/bindings/mailbox/qcom,apcs-kpss-global.yaml,
the schema currently restricts the compatible list for this hardware to exactly
two items:

      - items:
          - enum:
              - qcom,ipq5018-apcs-apps-global
              - qcom,ipq5332-apcs-apps-global
              - qcom,ipq5424-apcs-apps-global
              - qcom,ipq8074-apcs-apps-global
              - qcom,ipq9574-apcs-apps-global
          - const: qcom,ipq6018-apcs-apps-global

Does the yaml binding need to be updated alongside this patch to permit
"syscon" as a third compatible string for this device?

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

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-ipq5018-bluetooth-v3-0-62da72818ab3@outlook.com?part=5

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

* Re: [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-03  5:01 ` [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
@ 2026-07-03  5:13   ` sashiko-bot
  2026-07-03 11:15   ` Konrad Dybcio
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-07-03  5:13 UTC (permalink / raw)
  To: George Moussalem; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Command ID collision in TrustZone SMC calls causing potential physical memory corruption
--

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

firmware: qcom: scm: Add support for setting Bluetooth power modes

This commit adds support for configuring the Bluetooth subsystem power modes
on IPQ5018 through a new Secure Channel Manager call. It includes an
availability check to ensure the call is supported by the QSEE image.

> diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h
> index caab80a73e17f..5579df5a2aca7 100644
> --- a/drivers/firmware/qcom/qcom_scm.h
> +++ b/drivers/firmware/qcom/qcom_scm.h
> @@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
>  #define QCOM_SCM_PIL_PAS_SHUTDOWN	0x06
>  #define QCOM_SCM_PIL_PAS_IS_SUPPORTED	0x07
>  #define QCOM_SCM_PIL_PAS_MSS_RESET	0x0a
> +#define QCOM_SCM_PIL_PAS_BT_PWR_MODE	0x21
>  #define QCOM_SCM_PIL_PAS_GET_RSCTABLE	0x21

[Severity: Critical]
Does this new macro collide with the existing QCOM_SCM_PIL_PAS_GET_RSCTABLE
command? Both are defined as 0x21 within the QCOM_SCM_SVC_PIL service.

Because GET_RSCTABLE is widely supported on Qualcomm platforms, the
availability check in qcom_scm_pas_set_bluetooth_power_mode() will pass
even if the firmware does not actually support the new Bluetooth power mode
command:

drivers/firmware/qcom/qcom_scm.c:qcom_scm_pas_set_bluetooth_power_mode() {
    ...
    if (!__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_PIL,
                                      QCOM_SCM_PIL_PAS_BT_PWR_MODE))
        return -EOPNOTSUPP;
    ...
}

When the SMC call is subsequently issued with only 2 arguments, TrustZone's
GET_RSCTABLE handler will receive the call but expect 5 arguments. Could
this cause TrustZone to treat the uninitialized remaining arguments as
physical addresses, resulting in an attempt to write the resource table to
physical address 0x0?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-ipq5018-bluetooth-v3-0-62da72818ab3@outlook.com?part=3

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

* Re: [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
  2026-07-03  5:01 ` [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
@ 2026-07-03  5:15   ` sashiko-bot
  2026-07-03 12:55   ` Bartosz Golaszewski
  1 sibling, 0 replies; 20+ messages in thread
From: sashiko-bot @ 2026-07-03  5:15 UTC (permalink / raw)
  To: George Moussalem; +Cc: robh, devicetree, conor+dt

Thank you for your contribution! Sashiko AI review found 16 potential issue(s) to consider:
- [High] Data race in `btqcomipc_send` corrupts shared ring buffer state.
- [High] Invalid pointer cast of 32-bit DSP value causes kernel panic.
- [High] Missing and incorrect endianness conversions break functionality on big-endian architectures.
- [High] Unsafe firmware segment name formatting is vulnerable to buffer underflow and overflow.
- [High] The RX worker inadvertently corrupts the ring buffer size configuration.
- [High] Missing length bounds check on received short messages.
- [High] Spurious IRQ can trigger a NULL pointer dereference of `desc->hdev`.
- [High] Integer overflow in firmware segment address validation allows out-of-bounds write.
- [High] Incorrect teardown order in `btqcomipc_remove` leaks TX packets.
- [High] Missing memory barriers and improper I/O memory handling.
- [Medium] The workqueue is leaked if IRQ registration fails.
- [Medium] Resource leak on `btqcomipc_open` timeout.
- [Medium] Long message slots are leaked when the short TX ring is full.
- [Medium] Reopen state corruption due to missing flag reset in `btqcomipc_close`.
- [Medium] Missing structural validation of ELF file size.
- [Medium] Memory allocation failure in RX path causes permanent ring stall.
--

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

Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver

Add support for the Bluetooth controller found in the IPQ5018 SoC.
This driver implements firmware loading and the transport layer between
the HCI core and the Bluetooth controller.

> diff --git a/drivers/bluetooth/btqcomipc.c b/drivers/bluetooth/btqcomipc.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/bluetooth/btqcomipc.c
[ ... ]
> +#define IPC_LBUF_SZ(w, x, y, z)	(((TO_BT_ADDR((void *)w) + w->x) - w->y) / w->z)

[Severity: High]
Does this macro perform native arithmetic on little-endian fields?

When used with desc->tx_ctxt, fields like total_size and lring_buf are __le16
and __le32. Without endianness conversion, this could produce incorrect
results on big-endian architectures.

[ ... ]
> +	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, "TX long buffers 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, "TX short buffers full");
> +		hdev->stat.err_tx++;
> +		return PTR_ERR(rinfo);
> +	}

[Severity: Medium]
What happens to the allocated long message buffer if the short message ring
is full?

If btss_alloc_lmsg() succeeds but btss_get_tx_rbuf() subsequently fails, the
function returns an error without undoing the long message reservation. Could
this permanently leak long message slots under heavy traffic?

[ ... ]
> +	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]
Does this index update require a memory barrier?

The payload is written to the shared memory ring buffer, and then the widx
is updated. Without a dma_wmb(), is it possible for the CPU to reorder the
index update before the payload writes are fully visible to the firmware?

[ ... ]
> +	case HCI_COMMAND_PKT: {
> +		if (len < 1 + 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;
> +	}

[Severity: High]
Is it safe to use le16_to_cpu() on the plen field?

The plen field in struct hci_command_hdr is an 8-bit unsigned integer. Calling
le16_to_cpu() on it will zero-extend the value and byte-swap it on big-endian
architectures, effectively shifting the length left by 8 bits and causing
large over-allocations.

[ ... ]
> +		case IPC_HDR_PKT_TYPE_HCI:
> +			buf = kmalloc(rbuf->len, GFP_ATOMIC);
> +			if (!buf) {
> +				rinfo->ridx = ridx;
> +				return -ENOMEM;
> +			}

[Severity: Medium]
Will this memory allocation failure permanently stall the RX queue?

If kmalloc() fails, the function returns -ENOMEM without advancing the ring
index. The next time the worker runs, it will attempt to process the exact
same packet. Unless the packet is dropped and the index advanced, this could
permanently block the ring.

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

[Severity: High]
Does this code safely handle potentially large payload lengths from firmware?

If the packet is a short message, rxbuf points to the inline 40-byte smsg_data
buffer. The driver uses the unvalidated rbuf->len for the copy length. Could a
malfunctioning firmware provide a length greater than 40, leading to an
out-of-bounds read from shared memory?

[ ... ]
> +	for (rinfo = &(desc->rx_ctxt->sring_buf_info);
> +	     rinfo != NULL;
> +	     rinfo = (struct ring_buffer_info *)(uintptr_t)(rinfo->next)) {

[Severity: High]
Does this properly convert the next pointer from the firmware?

The next field in struct ring_buffer_info is a little-endian offset or address
value. Casting it directly to uintptr_t without address translation or endian
conversion could cause a kernel panic due to an invalid page fault when
dereferenced on the next loop iteration.

> +		ret = btss_process_rx(desc, rinfo, &ack,
> +				      &desc->rx_ctxt->smsg_buf_cnt);

[Severity: High]
Are we inadvertently overwriting the ring buffer configuration here?

The fourth argument to btss_process_rx() is treated as an output counter and
incremented for every processed packet. However, we are passing the address of
the firmware's fixed smsg_buf_cnt configuration parameter. Incrementing this
will break the modulo arithmetic used for ring buffer indexing.

[ ... ]
> +	for (i = 0; i < ehdr->e_phnum; i++) {

[Severity: Medium]
Does this loop verify that the program headers exist within the firmware image?

The loop iterates based on the untrusted e_phnum field from the ELF header.
Could a malformed firmware file specify a high number of program headers,
causing out-of-bounds reads if fw->size is not properly validated?

[ ... ]
> +		if (phdr->p_vaddr + phdr->p_filesz > desc->mem_size) {
> +			dev_err(desc->dev,
> +				"Segment data exceeds the reserved memory area!\n");
> +			goto release_fw;
> +		}

[Severity: High]
Can this bounds check be bypassed by an integer overflow?

The check uses 32-bit arithmetic based on untrusted ELF header fields.
If p_vaddr and p_filesz sum to a value that exceeds 32 bits, they could wrap
around to a small result and pass this check, leading to an out-of-bounds
write when copying the segment data.

[ ... ]
> +		/* 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]
Is this string formatting safe from buffer underflows and overflows?

If the original firmware name is less than 3 characters long, strlen - 3 will
underflow to a huge value, writing out of bounds. Additionally, if the segment
index i is 100 or greater, the format string writes 5 bytes into a location
where only 4 bytes were expected, overflowing the kstrdup allocation.

[ ... ]
> +	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;

[Severity: Medium]
Does this error path leak the newly created workqueue?

If devm_request_threaded_irq() fails, the function returns immediately. The
workqueue created by create_singlethread_workqueue() is never destroyed,
leading to a memory leak.

[ ... ]
> +	msleep(BTSS_PWR_CTRL_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;
> +	}

[Severity: Medium]
Should we shut down the peripheral on this timeout path?

If the wait times out, the function returns -ETIMEDOUT. Because the open
operation failed, the networking core will not call btqcomipc_close(), which
abandons the DSP in a powered-on state without calling qcom_scm_pas_shutdown().

[ ... ]
> +static int btqcomipc_close(struct hci_dev *hdev)
> +{
> +	int ret;
> +
> +	ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
> +	if (ret) {
> +		bt_dev_err(hdev, "Failed to stop firmware: %d", ret);
> +		return ret;
> +	}
> +
> +	msleep(BTSS_PWR_CTRL_DELAY_MS);
> +
> +	return 0;
> +}

[Severity: Medium]
Should desc->running be reset to false when closing the device?

The shutdown process stops the DSP, but does not clear the desc->running flag.
If the interface is brought up again, the next btqcomipc_open() might bypass
its wait_event_timeout() check because the flag is still true from the
previous session.

[ ... ]
> +	ret = btqcomipc_init(desc);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to initialize\n");
> +
> +	hdev = hci_alloc_dev();

[Severity: High]
Does enabling the IRQ before the HCI device is allocated expose a race condition?

btqcomipc_init() requests the threaded IRQ, which enables it immediately. If a
spurious interrupt fires before hdev is allocated and assigned to desc->hdev,
the worker thread could trigger a NULL pointer dereference when accessing
desc->hdev for logging or processing.

[ ... ]
> +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);

[Severity: High]
Is there a data race when accessing the TX ring buffer?

This entry point from the HCI stack calls btss_send() without acquiring
desc->lock. Concurrently, btss_worker() can also call btss_send() with
desc->lock held. Executing both paths simultaneously could corrupt the
ring buffer indices and free space counters.

[ ... ]
> +static void btqcomipc_remove(struct platform_device *pdev)
> +{
> +	struct qcom_btss *desc = platform_get_drvdata(pdev);
> +
> +	if (!desc)
> +		return;
> +
> +	btqcomipc_deinit(desc);
> +
> +	if (desc->hdev) {
> +		hci_unregister_dev(desc->hdev);
> +		hci_free_dev(desc->hdev);
> +	}
> +}

[Severity: High]
Could the teardown order lead to use-after-free or leaked packets?

btqcomipc_deinit() purges the TX queue and destroys the workqueue. If this
happens before hci_unregister_dev() removes the device from the networking
core, the core could still call hdev->send(), which would place packets into
the destroyed TX queue or attempt to schedule the destroyed workqueue.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260703-ipq5018-bluetooth-v3-0-62da72818ab3@outlook.com?part=4

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

* Re: [PATCH v3 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-07-03  5:01 ` [PATCH v3 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
@ 2026-07-03  8:32   ` Bartosz Golaszewski
  0 siblings, 0 replies; 20+ messages in thread
From: Bartosz Golaszewski @ 2026-07-03  8:32 UTC (permalink / raw)
  To: george.moussalem
  Cc: George Moussalem via B4 Relay, linux-arm-msm, linux-bluetooth,
	devicetree, linux-kernel, Krzysztof Kozlowski,
	Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel

On Fri, 3 Jul 2026 07:01:49 +0200, George Moussalem via B4 Relay
<devnull+george.moussalem.outlook.com@kernel.org> said:
> From: George Moussalem <george.moussalem@outlook.com>
>
> Document the Qualcomm IPQ5018 Bluetooth controller.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---

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

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

* Re: [PATCH v3 6/6] MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver
  2026-07-03  5:01 ` [PATCH v3 6/6] MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver George Moussalem via B4 Relay
@ 2026-07-03  8:33   ` Bartosz Golaszewski
  0 siblings, 0 replies; 20+ messages in thread
From: Bartosz Golaszewski @ 2026-07-03  8:33 UTC (permalink / raw)
  To: george.moussalem
  Cc: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel, George Moussalem via B4 Relay,
	linux-arm-msm, linux-bluetooth, devicetree, linux-kernel

On Fri, 3 Jul 2026 07:01:54 +0200, George Moussalem via B4 Relay
<devnull+george.moussalem.outlook.com@kernel.org> said:
> From: George Moussalem <george.moussalem@outlook.com>
>
> Add maintainers entry for Qualcomm IPQ5018 Bluetooth driver.
>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  MAINTAINERS | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0b9d7c8276ac..60f7251d1a16 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -22289,6 +22289,13 @@ S:	Maintained
>  F:	Documentation/devicetree/bindings/regulator/vqmmc-ipq4019-regulator.yaml
>  F:	drivers/regulator/vqmmc-ipq4019-regulator.c
>
> +QUALCOMM IPQ5018 BLUETOOTH DRIVER
> +M:	George Moussalem <george.moussalem@outlook.com>
> +L:	linux-bluetooth@vger.kernel.org
> +S:	Maintained
> +F:	Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
> +F:	drivers/bluetooth/btqcomipc.c
> +
>  QUALCOMM IRIS VIDEO ACCELERATOR DRIVER
>  M:	Vikash Garodia <vikash.garodia@oss.qualcomm.com>
>  M:	Dikshita Agarwal <dikshita.agarwal@oss.qualcomm.com>
>
> --
> 2.53.0
>
>
>

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

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

* Re: [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
  2026-07-03  5:01 ` [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
  2026-07-03  5:10   ` sashiko-bot
@ 2026-07-03  8:33   ` Bartosz Golaszewski
  2026-07-03 10:38   ` Konrad Dybcio
  2 siblings, 0 replies; 20+ messages in thread
From: Bartosz Golaszewski @ 2026-07-03  8:33 UTC (permalink / raw)
  To: george.moussalem
  Cc: George Moussalem via B4 Relay, linux-arm-msm, linux-bluetooth,
	devicetree, linux-kernel, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Philipp Zabel

On Fri, 3 Jul 2026 07:01:53 +0200, George Moussalem via B4 Relay
<devnull+george.moussalem.outlook.com@kernel.org> said:
> From: George Moussalem <george.moussalem@outlook.com>
>
> Add nodes for the reserved memory carveout and Bluetooth.
>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---

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

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

* Re: [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
  2026-07-03  5:01 ` [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
  2026-07-03  5:10   ` sashiko-bot
  2026-07-03  8:33   ` Bartosz Golaszewski
@ 2026-07-03 10:38   ` Konrad Dybcio
  2026-07-03 14:11     ` George Moussalem
  2 siblings, 1 reply; 20+ messages in thread
From: Konrad Dybcio @ 2026-07-03 10:38 UTC (permalink / raw)
  To: george.moussalem, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel

On 7/3/26 7:01 AM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Add nodes for the reserved memory carveout and Bluetooth.
> 
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---

[...]

>  		apcs_glb: mailbox@b111000 {
>  			compatible = "qcom,ipq5018-apcs-apps-global",
> -				     "qcom,ipq6018-apcs-apps-global";
> +				     "qcom,ipq6018-apcs-apps-global",
> +				     "syscon";

You'd have to alter dt-bindings for this to be allowed. But I
don't think it is. The functions to grab a regmap from an OF
node create one on the fly

Konrad

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

* Re: [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-03  5:01 ` [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
  2026-07-03  5:13   ` sashiko-bot
@ 2026-07-03 11:15   ` Konrad Dybcio
  2026-07-03 11:15   ` Konrad Dybcio
  2026-07-03 23:34   ` Dmitry Baryshkov
  3 siblings, 0 replies; 20+ messages in thread
From: Konrad Dybcio @ 2026-07-03 11:15 UTC (permalink / raw)
  To: george.moussalem, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	Bartosz Golaszewski, Sumit Garg, Mukesh Ojha, Jorge Ramirez-Ortiz

On 7/3/26 7:01 AM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
> modes which are required to be configured through a Secure Channel
> Manager (SCM) call to TrustZone. However, not all Trusted Execution
> Environment (QSEE) images support this call, so first check if the call
> is available.
> 
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---

[...]

> @@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
>  #define QCOM_SCM_PIL_PAS_SHUTDOWN	0x06
>  #define QCOM_SCM_PIL_PAS_IS_SUPPORTED	0x07
>  #define QCOM_SCM_PIL_PAS_MSS_RESET	0x0a
> +#define QCOM_SCM_PIL_PAS_BT_PWR_MODE	0x21
>  #define QCOM_SCM_PIL_PAS_GET_RSCTABLE	0x21

0_0

collisions here are *bad*.. +cc a couple folks for awareness..

Konrad

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

* Re: [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-03  5:01 ` [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
  2026-07-03  5:13   ` sashiko-bot
  2026-07-03 11:15   ` Konrad Dybcio
@ 2026-07-03 11:15   ` Konrad Dybcio
  2026-07-03 23:34   ` Dmitry Baryshkov
  3 siblings, 0 replies; 20+ messages in thread
From: Konrad Dybcio @ 2026-07-03 11:15 UTC (permalink / raw)
  To: george.moussalem, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	Bartosz Golaszewski

On 7/3/26 7:01 AM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
> modes which are required to be configured through a Secure Channel
> Manager (SCM) call to TrustZone. However, not all Trusted Execution
> Environment (QSEE) images support this call, so first check if the call
> is available.
> 
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

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

* Re: [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
  2026-07-03  5:01 ` [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
  2026-07-03  5:15   ` sashiko-bot
@ 2026-07-03 12:55   ` Bartosz Golaszewski
  1 sibling, 0 replies; 20+ messages in thread
From: Bartosz Golaszewski @ 2026-07-03 12:55 UTC (permalink / raw)
  To: george.moussalem
  Cc: George Moussalem via B4 Relay, linux-arm-msm, linux-bluetooth,
	devicetree, linux-kernel, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Philipp Zabel

On Fri, 3 Jul 2026 07:01:52 +0200, George Moussalem via B4 Relay
<devnull+george.moussalem.outlook.com@kernel.org> said:
> From: George Moussalem <george.moussalem@outlook.com>
>
> Add support for the Bluetooth controller found in the IPQ5018 SoC.
> This driver implements firmware loading and the transport layer between
> the HCI core and the Bluetooth controller.
>
> The firmware is loaded by the host into the 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 controller.
>
> An outgoing APCS IPC bit and an incoming GIC interrupt handle host/guest
> signaling.
>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  drivers/bluetooth/Kconfig     |   11 +
>  drivers/bluetooth/Makefile    |    1 +
>  drivers/bluetooth/btqcomipc.c | 1041 +++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1053 insertions(+)
>
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index 4e8c24d757e9..c9785f43c87c 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

You seem to need to depend on CONFIG_OF.

> +	help
> +	  Qualcomm IPQ5018 IPC based HCI driver.
> +	  This driver is used to load firmware and bridge HCI data onto shared
> +	  memory between the host and the Bluetooth controller.
> +
> +	  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)

...

Sashiko already generated a wall-of-text review but just a few more nits:

> +
> +static int btqcomipc_probe(struct platform_device *pdev)
> +{
> +	struct reset_control *btss_reset;
> +	struct device *dev = &pdev->dev;
> +	struct qcom_btss *desc;
> +	struct hci_dev *hdev;
> +	unsigned int args[2];
> +	struct clk *lpo_clk;
> +	int ret;
> +
> +	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
> +	if (!desc)
> +		return -ENOMEM;
> +
> +	desc->dev = dev;
> +
> +	ret = of_property_read_string(dev->of_node, "firmware-name",
> +				      &desc->firmware);
> +	if (ret < 0)
> +		return ret;
> +
> +	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];
> +
> +	lpo_clk = devm_clk_get_enabled(dev, "lpo");
> +	if (IS_ERR(lpo_clk))
> +		return dev_err_probe(dev, PTR_ERR(lpo_clk),
> +				     "Failed to get lpo clock\n");
> +
> +	btss_reset = devm_reset_control_get_exclusive_deasserted(dev, NULL);
> +	if (IS_ERR_OR_NULL(btss_reset))
> +		return dev_err_probe(dev, PTR_ERR(btss_reset),
> +				     "unable to deassert reset\n");
> +
> +	desc->irq = platform_get_irq(pdev, 0);
> +	if (desc->irq < 0)
> +		return dev_err_probe(dev, desc->irq, "Failed to acquire IRQ\n");
> +
> +	ret = btqcomipc_init(desc);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "Failed to initialize\n");
> +
> +	hdev = hci_alloc_dev();
> +	if (!hdev) {
> +		btqcomipc_deinit(desc);
> +		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->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) {
> +		btqcomipc_deinit(desc);
> +		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)
> +		return;

This is only called if probe succeeded so desc can't be NULL.

> +
> +	btqcomipc_deinit(desc);
> +

This is a different ordering of cleanup from the initialization, is this
on purpose?

> +	if (desc->hdev) {
> +		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
>
>
>

Bart

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

* Re: [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support
  2026-07-03 10:38   ` Konrad Dybcio
@ 2026-07-03 14:11     ` George Moussalem
  0 siblings, 0 replies; 20+ messages in thread
From: George Moussalem @ 2026-07-03 14:11 UTC (permalink / raw)
  To: Konrad Dybcio, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Bjorn Andersson, Konrad Dybcio, Philipp Zabel
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel

On 7/3/26 14:38, Konrad Dybcio wrote:
> On 7/3/26 7:01 AM, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add nodes for the reserved memory carveout and Bluetooth.
>>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
> 
> [...]
> 
>>  		apcs_glb: mailbox@b111000 {
>>  			compatible = "qcom,ipq5018-apcs-apps-global",
>> -				     "qcom,ipq6018-apcs-apps-global";
>> +				     "qcom,ipq6018-apcs-apps-global",
>> +				     "syscon";
> 
> You'd have to alter dt-bindings for this to be allowed. But I
> don't think it is. The functions to grab a regmap from an OF
> node create one on the fly

got it, will remove in the next version and adjust parsing the syscon in
the driver probe accordingly.

> 
> Konrad

Thanks,
George


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

* Re: [PATCH v3 2/6] Bluetooth: btqca: Add IPQ5018 support
  2026-07-03  5:01 ` [PATCH v3 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
@ 2026-07-03 23:32   ` Dmitry Baryshkov
  0 siblings, 0 replies; 20+ messages in thread
From: Dmitry Baryshkov @ 2026-07-03 23:32 UTC (permalink / raw)
  To: george.moussalem
  Cc: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel, linux-arm-msm, linux-bluetooth,
	devicetree, linux-kernel, Bartosz Golaszewski

On Fri, Jul 03, 2026 at 09:01:50AM +0400, George Moussalem via B4 Relay wrote:
> 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
> 
> Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> 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;

Please keep the switch-cases sorted.

>  		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:

Sorted too.

>  		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:

Sorted too.

>  		/* 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,

Sorted too.

>  };
>  
>  #if IS_ENABLED(CONFIG_BT_QCA)
> 
> -- 
> 2.53.0
> 
> 

-- 
With best wishes
Dmitry

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

* Re: [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-03  5:01 ` [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
                     ` (2 preceding siblings ...)
  2026-07-03 11:15   ` Konrad Dybcio
@ 2026-07-03 23:34   ` Dmitry Baryshkov
  3 siblings, 0 replies; 20+ messages in thread
From: Dmitry Baryshkov @ 2026-07-03 23:34 UTC (permalink / raw)
  To: george.moussalem
  Cc: Bartosz Golaszewski, Marcel Holtmann, Luiz Augusto von Dentz,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
	Konrad Dybcio, Philipp Zabel, linux-arm-msm, linux-bluetooth,
	devicetree, linux-kernel, Bartosz Golaszewski

On Fri, Jul 03, 2026 at 09:01:51AM +0400, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
> modes which are required to be configured through a Secure Channel
> Manager (SCM) call to TrustZone. However, not all Trusted Execution
> Environment (QSEE) images support this call, so first check if the call
> is available.
> 
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  drivers/firmware/qcom/qcom_scm.c       | 44 ++++++++++++++++++++++++++++++++++
>  drivers/firmware/qcom/qcom_scm.h       |  1 +
>  include/linux/firmware/qcom/qcom_scm.h |  1 +
>  3 files changed, 46 insertions(+)
> 

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>


-- 
With best wishes
Dmitry

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

end of thread, other threads:[~2026-07-03 23:34 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-03  5:01 [PATCH v3 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
2026-07-03  5:01 ` [PATCH v3 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
2026-07-03  8:32   ` Bartosz Golaszewski
2026-07-03  5:01 ` [PATCH v3 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
2026-07-03 23:32   ` Dmitry Baryshkov
2026-07-03  5:01 ` [PATCH v3 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
2026-07-03  5:13   ` sashiko-bot
2026-07-03 11:15   ` Konrad Dybcio
2026-07-03 11:15   ` Konrad Dybcio
2026-07-03 23:34   ` Dmitry Baryshkov
2026-07-03  5:01 ` [PATCH v3 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
2026-07-03  5:15   ` sashiko-bot
2026-07-03 12:55   ` Bartosz Golaszewski
2026-07-03  5:01 ` [PATCH v3 5/6] arm64: dts: qcom: ipq5018: add nodes required for Bluetooth support George Moussalem via B4 Relay
2026-07-03  5:10   ` sashiko-bot
2026-07-03  8:33   ` Bartosz Golaszewski
2026-07-03 10:38   ` Konrad Dybcio
2026-07-03 14:11     ` George Moussalem
2026-07-03  5:01 ` [PATCH v3 6/6] MAINTAINERS: Add entry for Qualcomm IPQ5018 Bluetooth driver George Moussalem via B4 Relay
2026-07-03  8:33   ` Bartosz Golaszewski

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