Devicetree
 help / color / mirror / Atom feed
* [PATCH v5 0/6] Add support for IPQ5018 Bluetooth
@ 2026-07-09  9:18 George Moussalem via B4 Relay
  2026-07-09  9:18 ` [PATCH v5 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
                   ` (5 more replies)
  0 siblings, 6 replies; 21+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-09  9:18 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,
	Konrad Dybcio, Dmitry Baryshkov

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 FW build version: 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 v5:
- Updated the bindings file to change from using a memory-region
  property to using a reg property for the shared memory area and made
  reg a required property.
- Added Bartosz G. to the maintainers list in the bindings file.
- Updated the driver to acquire the memory region as platform resource
  using devm_platform_get_and_ioremap_resource()
- Replaced macros with unsafe arithmetic with inline functions that
  safely calculate the size by reading values using endian functions
  le**_to_cpu().
- Update btss_lbuf_size to guard against division by zero and against
  the offset extending beyond the total size of the context info struct.
- Updated btqcomipc_send to enqueue packets before releasing the
  spinlock to avoid stranding packets in the queue indefinitely.
- Added an explicit DMA read barrier in the RX loop before reading and
  processing each ring entry to guarantee memory ordering.
- Added strict arithmetic validation for long-message metadata handling
  before computing wrap segments and hardened the non-contiguous path to
  prevent underflow.
- Hardened split-segment firmware loading error handling. If
  request_firmware() for a segment fails, the code now exits that path
  immediately instead of falling through to memcpy_toio() and added
  explicit split-file size validation before copy.
- Updated HCI dev close path state teardown to avoid stale transport
  stale after PAS shutdown. btqcomipc_close() now explicitly sets
  running state to false under lock after shutdown.
- Made IRQ registration safe against spurious interrupts by keeping the
  line disabled after request (flags includes now IRQF_NO_AUTOEN).
  IRQ is now enabled only in btqcomipc_open() right before firmware boot
  and disabled again on open-failure and close paths.
- Reordered remove-path teardown to eliminate worker vs HCI lifetime
  race. It now unregisters HCI first to stop new traffic, then calls
  btqcomipc_deinit() to stop async processing, and only then frees hdev.
- Removed reserved memory area from the dtsi, moved bluetooth node under
  the soc node, and added unit address and reg property.
- Link to v4: https://lore.kernel.org/r/20260706-ipq5018-bluetooth-v4-0-350262a30959@outlook.com

Changes in v4:
- Ordered switch statements in btqca.c. NOTE: left enum qca_bt_soc_type
  unordered in the header file as the btqca driver has conditionally
  checks whether bt_soc_type is above a certain value:
  'if (soc_type >= QCA_WCN3991)' is found in 5 places
- Fixed TX path serialization by protecting btqcomipc_send and
  btss_send ring access with desc->lock, preventing concurrent updates
  of ring indices and free counters from TX and worker contexts.
- Fixed and simplified firmware ring traversal to decode little-endian
  next and translate firmware offsets via TO_APPS_ADDR in all ring
  walks, avoiding invalid pointer dereferences.
- Added short-message RX length validation (msg_len must not exceed
  IPC_MSG_PLD_SZ when not long message), preventing out-of-bounds read
  from inline shared-memory payloads.
- Fixed RX allocation failure behavior to advance ring index and
  continue, preventing permanent RX ring stall on transient kmalloc
  failures.
- Replace macro for calculting large buffer size that performed unsafe
  arithmetic by an inline function that safely calculates the size by
  reading values using endian functions le**_to_cpu().
- Declared aux_ptr as a zero-initialized struct as opposed to using
  memset and changed struct definition by updating its member to use
  u32 as opposed to __le32 as these values aren't set by firmware and
  need not be validated on endianness.
- Fixed long-message slot leak when short TX ring is full by
  snapshotting and rolling back long-buffer accounting on
  allocation-success/short-ring-fail path.
- Updated firmware load logic to remove call to qcom_mdt_pas_init for
  which the export was recently removed and replaced it by calls to
  read the metadata and the validation of it using SCM.
  NOTE: Sashiko correctly pointed out that the firmware ELF/mdt headers
  weren't validated in the driver. However, this is done by the call to
  qcom_mdt_read_metadata which returns -EINVAL if the mdt header isn't
  valid.
- Hardened firmware segment bounds checks against integer overflow by
  using subtraction form (filesz <= mem_size - vaddr).
- Replaced unsafe firmware split name rewrite with validated stem
  handling and kasprintf formatting, removing underflow/overflow risk.
- Converted workqueue allocation to devm_alloc_ordered_workqueue to fix
  workqueue leak in IRQ registration failure path.
- Added cleanup path in btqcomipc_open to shut down controller on
  startup timeout, avoiding leaving peripheral powered after failed open
- Reordered probe sequence to allocate/attach hdev before IRQ
  registration, eliminating early-spurious-IRQ dereference window for
  desc->hdev.
- Reordered remove path to unregister/free hdev before driver deinit,
  preventing send path from racing against queue/work teardown.
- Corrected HCI command packet length parsing to use 8-bit plen directly
  instead of using le16_to_cpu.
- Fixed endianness and address translation in TX ACK processing path.
  Converted firmware-owned ring metadata before use in btss_process_ack:
  rbuf base offset, msg header, long-message pointer, and message length
  are now read via le32_to_cpu or le16_to_cpu prior to reclaim logic.
- Added explicit producer ordering barrier in TX ring publish path.
  Inserted dma_wmb between payload or header writes and widx update so
  firmware cannot observe producer index advancement before data becomes
  visible.
- Removed info message printing FW build info as that was recently
  introduced in the kernel in commit bdea21b3be43
- Added dependency on OF and ARCH_QCOM symbols to Kconfig. In addition,
  also select QCOM_MDT_LOADER and QCOM_SCM.
- Removed syscon compatible from mailbox node, avoiding dt_binding_check
  failures and update the syscon acquisition logic in driver probe
  accordingly.
- Link to v3: https://lore.kernel.org/r/20260703-ipq5018-bluetooth-v3-0-62da72818ab3@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 node required for Bluetooth support
      MAINTAINERS: Add IPQ5018 driver to Qualcomm Bluetooth driver entry

 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    |   87 ++
 MAINTAINERS                                        |    1 +
 arch/arm64/boot/dts/qcom/ipq5018.dtsi              |   16 +
 drivers/bluetooth/Kconfig                          |   14 +
 drivers/bluetooth/Makefile                         |    1 +
 drivers/bluetooth/btqca.c                          |   16 +
 drivers/bluetooth/btqca.h                          |    3 +
 drivers/bluetooth/btqcomipc.c                      | 1230 ++++++++++++++++++++
 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, 1414 insertions(+)
---
base-commit: ca75417ab1793f23b3ddc91869f0809b593d6954
change-id: 20260625-ipq5018-bluetooth-06ff66c9d753
prerequisite-message-id: <20260703-block-as-nvmem-v8-0-98ae32bfc49a@oss.qualcomm.com>
prerequisite-patch-id: 467ba5e0fde0e9a1e9d0a6faf3d22ba2126c62a7
prerequisite-patch-id: 9168930e40551e842c8171d5433a6f39ad4b78a4
prerequisite-patch-id: 64fecfbd1e085d7d2ab0ae23295ca34ec8e14c5e
prerequisite-patch-id: 7bf282ed9312b2ffdbe420429bbe355622620272
prerequisite-patch-id: 29a7d652948d13f91bdd4fc5fb8e6dbcd6e5fcc5
prerequisite-patch-id: c03136820a7749fd9630080e6d4459a298b3b630
prerequisite-patch-id: 43975fc425bf2f5dd5c1de66aaa78c4b4bde568c
prerequisite-patch-id: 75caa99e3bbcdf41b6462b9f5f703bea1d4a65fa
prerequisite-patch-id: b9b2a2f80a4827b457166b20f54644e57d90b824

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



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

* [PATCH v5 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller
  2026-07-09  9:18 [PATCH v5 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
@ 2026-07-09  9:18 ` George Moussalem via B4 Relay
  2026-07-09  9:18 ` [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 21+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-09  9:18 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

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

Document the Qualcomm IPQ5018 Bluetooth controller.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 .../bindings/net/bluetooth/qcom,ipq5018-bt.yaml    | 87 ++++++++++++++++++++++
 1 file changed, 87 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..6bd74c01b821
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/bluetooth/qcom,ipq5018-bt.yaml
@@ -0,0 +1,87 @@
+# 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:
+  - Bartosz Golaszewski <brgl@kernel.org>
+  - George Moussalem <george.moussalem@outlook.com>
+
+properties:
+  compatible:
+    enum:
+      - qcom,ipq5018-bt
+
+  reg:
+    items:
+      - description: |
+          Shared memory for firmware loading and runtime data transport between
+          the host and the Bluetooth controller.
+
+  clocks:
+    items:
+      - description: Bluetooth Subsystem low power oscillator clock
+
+  clock-names:
+    items:
+      - const: lpo
+
+  firmware-name:
+    maxItems: 1
+
+  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
+  - reg
+  - 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@7000000 {
+      compatible = "qcom,ipq5018-bt";
+      reg = <0x07000000 0x58000>;
+
+      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>;
+    };

-- 
2.53.0



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

* [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support
  2026-07-09  9:18 [PATCH v5 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
  2026-07-09  9:18 ` [PATCH v5 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
@ 2026-07-09  9:18 ` George Moussalem via B4 Relay
  2026-07-09  9:31   ` sashiko-bot
  2026-07-09  9:18 ` [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 21+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-09  9:18 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 08600b0fc041..eed30a4480e0 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -361,6 +361,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;
 
@@ -778,6 +781,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);
 
@@ -865,6 +871,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) {
@@ -878,6 +885,10 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 		}
 	} else {
 		switch (soc_type) {
+		case QCA_IPQ5018:
+			snprintf(config.fwname, sizeof(config.fwname),
+				 "qca/mpnv%02x.bin", rom_ver);
+			break;
 		case QCA_QCA2066:
 			qca_get_nvm_name_by_board(config.fwname,
 						  sizeof(config.fwname),
@@ -942,7 +953,11 @@ 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_IPQ5018:
 	case QCA_QCA2066:
 	case QCA_QCA6390:
 	case QCA_WCN3991:
@@ -982,6 +997,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate,
 	bt_dev_dbg(hdev, "QCA HCI_RESET succeed");
 
 	switch (soc_type) {
+	case QCA_IPQ5018:
 	case QCA_WCN3991:
 	case QCA_WCN6750:
 	case QCA_WCN6855:
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] 21+ messages in thread

* [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-09  9:18 [PATCH v5 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
  2026-07-09  9:18 ` [PATCH v5 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
  2026-07-09  9:18 ` [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
@ 2026-07-09  9:18 ` George Moussalem via B4 Relay
  2026-07-09  9:27   ` sashiko-bot
  2026-07-09  9:18 ` [PATCH v5 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 21+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-09  9:18 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, Konrad Dybcio,
	Dmitry Baryshkov

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>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@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 9b06a69d3a6d..d51e3f4339d8 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] 21+ messages in thread

* [PATCH v5 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
  2026-07-09  9:18 [PATCH v5 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (2 preceding siblings ...)
  2026-07-09  9:18 ` [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
@ 2026-07-09  9:18 ` George Moussalem via B4 Relay
  2026-07-09  9:30   ` sashiko-bot
  2026-07-09  9:18 ` [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support George Moussalem via B4 Relay
  2026-07-09  9:18 ` [PATCH v5 6/6] MAINTAINERS: Add IPQ5018 driver to Qualcomm Bluetooth driver entry George Moussalem via B4 Relay
  5 siblings, 1 reply; 21+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-09  9:18 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 memory area shared by the
host and the controller and is authenticated by TrustZone. A Secure
Channel Manager (SCM) call 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     |   14 +
 drivers/bluetooth/Makefile    |    1 +
 drivers/bluetooth/btqcomipc.c | 1230 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1245 insertions(+)

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e8c24d757e9..a2d82f27f07a 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -413,6 +413,20 @@ 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"
+	depends on OF && ARCH_QCOM
+	select BT_QCA
+	select QCOM_MDT_LOADER
+	select QCOM_SCM
+	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..9d71fe611772
--- /dev/null
+++ b/drivers/bluetooth/btqcomipc.c
@@ -0,0 +1,1230 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/firmware.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/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 by IPC interrupt is required by firmware.
+ *
+ * - 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)		((u8 *)desc->base + (a))
+#define	TO_BT_ADDR(a)		(a - desc->base)
+
+#define	GET_NO_OF_BLOCKS(a, b) ((a + b - 1) / b)
+
+#define BTSS_PAS_ID		0xc
+
+#define BTSS_PWR_CTRL_DELAY_MS	50
+
+struct long_msg_info {
+	u16 smsg_free_cnt;
+	u16 lmsg_free_cnt;
+	u8 ridx;
+	u8 widx;
+} __packed;
+
+struct ipc_aux_ptr {
+	u32 len;
+	u32 buf;
+};
+
+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 *base;
+	phys_addr_t mem_phys;
+	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 inline u32 btss_lbuf_size(struct qcom_btss *desc,
+				 const struct context_info *ctxt)
+{
+	u32 end, lring_buf, total_size;
+
+	total_size = le16_to_cpu(ctxt->total_size);
+	lring_buf = le32_to_cpu(ctxt->lring_buf);
+	end = TO_BT_ADDR((void *)ctxt) + total_size;
+
+	if (!ctxt->lmsg_buf_cnt)
+		return 0;
+
+	if (lring_buf > end)
+		return 0;
+
+	return (end - lring_buf) / ctxt->lmsg_buf_cnt;
+}
+
+static inline u8 btss_get_buf_idx(const struct context_info *ctxt,
+				  u32 lmsg_data, u32 lsz)
+{
+	u32 lring_buf = le32_to_cpu(ctxt->lring_buf);
+
+	return (u8)((lmsg_data - lring_buf) / lsz);
+}
+
+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)
+{
+	u8 idx, blks, blks_consumed;
+	u32 lring_buf, lsz;
+	void *ret_ptr;
+
+	lring_buf = le32_to_cpu(desc->tx_ctxt->lring_buf);
+
+	if (!lring_buf)
+		return ERR_PTR(-ENODEV);
+
+	lsz = btss_lbuf_size(desc, desc->tx_ctxt);
+	if (!lsz)
+		return ERR_PTR(-EINVAL);
+
+	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(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;
+	u32 next;
+	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;
+		}
+
+		next = le32_to_cpu(rinfo->next);
+		rinfo = next ? (struct ring_buffer_info *)TO_APPS_ADDR(next) : NULL;
+	}
+
+	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;
+	u16 lmsg_free_cnt;
+	void *ptr_buf;
+	u8 lmsg_widx;
+	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;
+
+		/* snapshot long message context in case we need to reverse */
+		lmsg_free_cnt = le16_to_cpu(desc->lmsg_ctxt.lmsg_free_cnt);
+		lmsg_widx = desc->lmsg_ctxt.widx;
+
+		ptr_buf = btss_alloc_lmsg(desc, len,
+					  &aux_ptr, &is_lbuf_full);
+		if (IS_ERR(ptr_buf)) {
+			bt_dev_err(hdev,
+				   "Failed to allocate long message buffer: %ld",
+				   PTR_ERR(ptr_buf));
+			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++;
+		if (len > IPC_MSG_PLD_SZ) {
+			/* reverse long message context if we allocated one */
+			desc->lmsg_ctxt.lmsg_free_cnt = cpu_to_le16(lmsg_free_cnt);
+			desc->lmsg_ctxt.widx = lmsg_widx;
+		}
+		return PTR_ERR(rinfo);
+	}
+
+	rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(le32_to_cpu(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);
+
+	dma_wmb();
+
+	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 blks, idx;
+	u32 lsz;
+
+	lsz = btss_lbuf_size(desc, desc->tx_ctxt);
+	if (!lsz) {
+		bt_dev_err(desc->hdev, "long message buffer size is zero");
+		return;
+	}
+
+	idx = btss_get_buf_idx(desc->tx_ctxt, 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;
+
+	/*
+	 * During firmware patch download, the controller temporarily stops the
+	 * IPC transport and signals the host with IPC_CMD_STOP. The host must
+	 * stop sending new frames until the controller signals IPC_CMD_START
+	 * back to the host after the patch download has completed.
+	 */
+	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;
+		}
+
+		msg_hdr |= cmd;
+		ret = btss_send(desc, msg_hdr, NULL);
+		if (ret)
+			bt_dev_err(desc->hdev,
+				   "Failed to send control message");
+
+		WRITE_ONCE(desc->running, false);
+		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 + 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, widx, lbuf_idx, blks_consumed, pkt_type, cmd;
+	u32 lmsg_data, lsz, lring_buf, lring_end;
+	struct ring_buffer *rbuf;
+	uint8_t *rxbuf = NULL;
+	unsigned char *buf;
+	u16 msg_hdr;
+	u16 msg_len;
+	int ret;
+
+	ridx = rinfo->ridx;
+	lring_buf = le32_to_cpu(desc->rx_ctxt->lring_buf);
+	widx = READ_ONCE(rinfo->widx);
+
+	while (ridx != widx) {
+		struct ipc_aux_ptr aux_ptr = { };
+
+		dma_rmb();
+
+		rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(le32_to_cpu(rinfo->rbuf))))[ridx];
+		msg_hdr = le16_to_cpu(rbuf->msg_hdr);
+		msg_len = le16_to_cpu(rbuf->len);
+
+		if (!(msg_hdr & IPC_HDR_LONG_MSG) && msg_len > IPC_MSG_PLD_SZ) {
+			desc->hdev->stat.err_rx++;
+			bt_dev_err(desc->hdev,
+				   "Dropping short RX frame with invalid len %u",
+				   msg_len);
+			goto continue_rx;
+		}
+
+		if (msg_hdr & IPC_HDR_LONG_MSG) {
+			lmsg_data = le32_to_cpu(rbuf->payload.lmsg_data);
+			rxbuf = TO_APPS_ADDR(lmsg_data);
+			lsz = btss_lbuf_size(desc, desc->rx_ctxt);
+			if (!lsz) {
+				bt_dev_err(desc->hdev,
+					   "Long message buffer size is zero");
+				goto continue_rx;
+			} else if (lsz > (U32_MAX - lring_buf) / desc->rx_ctxt->lmsg_buf_cnt) {
+				bt_dev_err(desc->hdev,
+					   "Long message buffer size is too large");
+				goto continue_rx;
+			}
+
+			lring_end = lring_buf + lsz * desc->rx_ctxt->lmsg_buf_cnt;
+			if (lmsg_data < lring_buf || lmsg_data >= lring_end) {
+				desc->hdev->stat.err_rx++;
+				bt_dev_err(desc->hdev,
+					   "Dropping long RX frame with invalid address 0x%08x",
+					   lmsg_data);
+				goto continue_rx;
+			}
+
+			/*
+			 * Check if the long message buffer extends past the end
+			 * of the ring buffer
+			 */
+			if (msg_len > lring_end - lmsg_data) {
+				lbuf_idx = btss_get_buf_idx(desc->rx_ctxt,
+							    lmsg_data, lsz);
+
+				blks_consumed = desc->rx_ctxt->lmsg_buf_cnt - lbuf_idx;
+				if (blks_consumed > msg_len / lsz) {
+					desc->hdev->stat.err_rx++;
+					bt_dev_err(desc->hdev,
+						   "Dropping long RX frame with invalid length %u",
+						   msg_len);
+					goto continue_rx;
+				}
+				aux_ptr.len = msg_len - (blks_consumed * lsz);
+				aux_ptr.buf = lring_buf;
+			}
+		} else {
+			rxbuf = rbuf->payload.smsg_data;
+		}
+
+		*ack = msg_hdr & IPC_HDR_REQ_ACK;
+
+		pkt_type = FIELD_GET(IPC_HDR_PKT_TYPE_MASK, msg_hdr);
+
+		switch (pkt_type) {
+		case IPC_HDR_PKT_TYPE_HCI:
+			buf = kmalloc(msg_len, GFP_ATOMIC);
+			if (!buf) {
+				desc->hdev->stat.err_rx++;
+				bt_dev_err(desc->hdev,
+					   "Dropping HCI frame due to RX allocation failure");
+				ridx = (ridx + 1) % rinfo->ring_buf_cnt;
+				rinfo->ridx = ridx;
+				if (rx_count)
+					(*rx_count)++;
+				continue;
+			}
+
+			memcpy_fromio(buf, rxbuf, msg_len - aux_ptr.len);
+
+			if (aux_ptr.buf)
+				memcpy_fromio(buf + (msg_len - aux_ptr.len),
+					      TO_APPS_ADDR(aux_ptr.buf),
+					      aux_ptr.len);
+
+			ret = btss_recv_hci_frame(desc, buf, msg_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, 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;
+		}
+
+continue_rx:
+		ridx = (ridx + 1) % rinfo->ring_buf_cnt;
+		rinfo->ridx = ridx;
+
+		if (rx_count)
+			(*rx_count)++;
+	}
+
+	return 0;
+}
+
+static void btss_process_ack(struct qcom_btss *desc)
+{
+	struct ring_buffer_info *rinfo;
+	struct ring_buffer *rbuf;
+	u16 msg_hdr;
+	u16 msg_len;
+	u32 next;
+	u32 lmsg;
+	u8 tidx;
+
+	rinfo = &desc->tx_ctxt->sring_buf_info;
+	while (rinfo != NULL) {
+		tidx = rinfo->tidx;
+		rbuf = (struct ring_buffer *)TO_APPS_ADDR(le32_to_cpu(rinfo->rbuf));
+
+		while (tidx != rinfo->ridx) {
+			msg_hdr = le16_to_cpu(rbuf[tidx].msg_hdr);
+			if (msg_hdr & IPC_HDR_LONG_MSG) {
+				lmsg = le32_to_cpu(rbuf[tidx].payload.lmsg_data);
+				msg_len = le16_to_cpu(rbuf[tidx].len);
+				btss_free_lmsg(desc, lmsg, msg_len);
+			}
+
+			tidx = (tidx + 1) % desc->tx_ctxt->smsg_buf_cnt;
+			desc->lmsg_ctxt.smsg_free_cnt++;
+		}
+
+		rinfo->tidx = tidx;
+
+		btss_process_tx_queue(desc);
+
+		next = le32_to_cpu(rinfo->next);
+		rinfo = next ? (struct ring_buffer_info *)TO_APPS_ADDR(next) : NULL;
+	}
+}
+
+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, next;
+	u16 total_size;
+	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->base);
+		if (offset > desc->mem_size - sizeof(struct context_info)) {
+			bt_dev_err(desc->hdev,
+				   "Invalid offset after firmware boot: 0x%08x",
+				   offset);
+			goto spin_unlock;
+		}
+		dev_dbg(desc->dev, "offset after firmware boot: 0x%08x\n",
+			offset);
+
+		desc->rx_ctxt = (struct context_info *)(desc->base + offset);
+
+		total_size = le16_to_cpu(desc->rx_ctxt->total_size);
+		if (total_size < sizeof(struct context_info) ||
+		total_size > desc->mem_size - offset) {
+			bt_dev_err(desc->hdev, "Invalid total size: %u",
+				   total_size);
+			desc->rx_ctxt = NULL;
+			goto spin_unlock;
+		}
+	} else {
+		btss_process_ack(desc);
+	}
+
+	rinfo = &(desc->rx_ctxt->sring_buf_info);
+	while (rinfo != NULL) {
+		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;
+		}
+		next = le32_to_cpu(rinfo->next);
+		rinfo = next ? (struct ring_buffer_info *)TO_APPS_ADDR(next) : NULL;
+	}
+
+	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 char *dot = strrchr(desc->firmware, '.');
+	const struct elf32_phdr *phdrs;
+	const struct firmware *seg_fw;
+	const struct elf32_phdr *phdr;
+	const struct elf32_hdr *ehdr;
+	const struct firmware *fw;
+	size_t metadata_len;
+	size_t stem_len;
+	void *metadata;
+	char *seg_name;
+	int i, ret;
+
+	if (!dot || dot != desc->firmware + strlen(desc->firmware) - 4) {
+		dev_err(desc->dev, "Invalid firmware name: %s\n",
+			desc->firmware);
+		return -EINVAL;
+	}
+
+	stem_len = dot - desc->firmware;
+
+	ret = request_firmware(&fw, desc->firmware, desc->dev);
+	if (ret) {
+		dev_err(desc->dev, "Failed to request firmware: %d\n",
+			ret);
+		return ret;
+	}
+
+	metadata = qcom_mdt_read_metadata(fw, &metadata_len, desc->firmware, desc->dev);
+	if (IS_ERR(metadata)) {
+		ret = PTR_ERR(metadata);
+		dev_err(desc->dev, "error %d reading firmware %s metadata\n",
+			ret, desc->firmware);
+		goto release_fw;
+	}
+
+	ret = qcom_scm_pas_init_image(BTSS_PAS_ID, metadata, metadata_len, NULL);
+	kfree(metadata);
+	if (ret) {
+		dev_err(desc->dev, "error %d initializing firmware %s\n",
+			ret, desc->firmware);
+		goto release_fw;
+	}
+
+	/*
+	 * qcom_mdt_read_metadata has already validated the ELF/mdt header, so
+	 * no need to revalidate it again here.
+	 */
+	ehdr = (const struct elf32_hdr *)fw->data;
+	phdrs = (const struct elf32_phdr *)(ehdr + 1);
+
+	/* guard against a large (incorrect) number of program headers */
+	if (ehdr->e_phnum > 99) {
+		dev_err(desc->dev, "Too many program headers: %d\n",
+			ehdr->e_phnum);
+		ret = -EINVAL;
+		goto release_fw;
+	}
+
+	for (i = 0; i < ehdr->e_phnum; i++) {
+		phdr = &phdrs[i];
+
+		/* Only process valid loadable data segments */
+		if (phdr->p_type != PT_LOAD || !phdr->p_memsz)
+			continue;
+
+		/*
+		 * The virtual instead of the physical address must be used as
+		 * the offset.
+		 */
+		if (phdr->p_vaddr > desc->mem_size ||
+		    phdr->p_filesz > desc->mem_size - phdr->p_vaddr) {
+			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_filesz > fw->size - phdr->p_offset) {
+			seg_name = kasprintf(GFP_KERNEL, "%.*sb%02d",
+					     (int)stem_len, desc->firmware, i);
+			if (!seg_name) {
+				ret = -ENOMEM;
+				goto release_fw;
+			}
+
+			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_seg_name;
+			}
+
+			if (seg_fw->size != phdr->p_filesz) {
+				dev_err(desc->dev,
+					"Segment binary %s has unexpected file size!\n",
+					seg_name);
+				ret = -EINVAL;
+				goto release_seg_fw;
+			}
+			memcpy_toio(desc->base + phdr->p_vaddr,
+				    seg_fw->data, phdr->p_filesz);
+			if (phdr->p_memsz > phdr->p_filesz)
+				memset_io(desc->base + phdr->p_vaddr +
+					  phdr->p_filesz, 0,
+					  phdr->p_memsz - phdr->p_filesz);
+
+release_seg_fw:
+			release_firmware(seg_fw);
+release_seg_name:
+			kfree(seg_name);
+			seg_name = NULL;
+
+			if (ret)
+				goto release_fw;
+		} else {
+			memcpy_toio(desc->base + phdr->p_vaddr,
+				    fw->data + phdr->p_offset, phdr->p_filesz);
+			if (phdr->p_memsz > phdr->p_filesz)
+				memset_io(desc->base + phdr->p_vaddr +
+					  phdr->p_filesz, 0,
+					  phdr->p_memsz - 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;
+	}
+
+	enable_irq(desc->irq);
+
+	/* Boot firmware */
+	ret = qcom_scm_pas_auth_and_reset(BTSS_PAS_ID);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to boot firmware: %d", ret);
+		goto disable_irq;
+	}
+
+	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");
+		ret = -ETIMEDOUT;
+		goto shutdown;
+	}
+
+	return 0;
+
+shutdown:
+	qcom_scm_pas_shutdown(BTSS_PAS_ID);
+disable_irq:
+	disable_irq(desc->irq);
+
+	return ret;
+}
+
+static int btqcomipc_close(struct hci_dev *hdev)
+{
+	struct qcom_btss *desc = hci_get_drvdata(hdev);
+	unsigned long flags;
+	int ret;
+
+	ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to stop firmware: %d", ret);
+		return ret;
+	}
+
+	disable_irq(desc->irq);
+
+	spin_lock_irqsave(&desc->lock, flags);
+	WRITE_ONCE(desc->running, false);
+	spin_unlock_irqrestore(&desc->lock, flags);
+
+	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, NULL);
+	if (ret) {
+		bt_dev_err(hdev, "Failed to setup controller: %d\n", ret);
+		return ret;
+	}
+
+	/* 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);
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&desc->lock, flags);
+
+	if (unlikely(!READ_ONCE(desc->running))) {
+		bt_dev_err(hdev,
+			   "BTSS not initialized, failed to send message");
+		ret = -ENODEV;
+		goto spin_unlock;
+	}
+
+	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);
+				ret = 0;
+				skb = NULL;
+			}
+		} else {
+			bt_dev_err(hdev, "Failed to send message: %d", ret);
+			hdev->stat.err_tx++;
+		}
+	}
+
+spin_unlock:
+	spin_unlock_irqrestore(&desc->lock, flags);
+
+	if (!ret && skb) {
+		btqcomipc_update_stats(desc->hdev, 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 = devm_alloc_ordered_workqueue(dev, "btss_wq", 0);
+	if (!desc->wq) {
+		dev_err(dev, "Failed to initialize workqueue\n");
+		return -ENOMEM;
+	}
+
+	INIT_WORK(&desc->work, btss_worker);
+
+	ret = devm_request_threaded_irq(dev, desc->irq, NULL, btss_irq_handler,
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT | IRQF_NO_AUTOEN,
+					"btss_irq", desc);
+
+	if (ret) {
+		dev_err(dev, "error registering irq[%d] ret = %d\n",
+			desc->irq, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void btqcomipc_deinit(struct qcom_btss *desc)
+{
+	if (desc->wq)
+		flush_workqueue(desc->wq);
+	skb_queue_purge(&desc->tx_q);
+}
+
+static int btqcomipc_probe(struct platform_device *pdev)
+{
+	struct reset_control *btss_reset;
+	struct device *dev = &pdev->dev;
+	struct of_phandle_args args;
+	struct device_node *ipc_np;
+	struct qcom_btss *desc;
+	struct resource *res;
+	struct hci_dev *hdev;
+	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;
+
+	desc->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+	if (IS_ERR(desc->base)) {
+		dev_err(dev, "unable to acquire memory-region resource\n");
+		return dev_err_probe(dev, PTR_ERR(desc->base),
+				     "unable to acquire memory-region resource\n");
+	}
+
+	desc->mem_phys = res->start;
+	desc->mem_size = resource_size(res);
+
+	ret = of_parse_phandle_with_fixed_args(dev->of_node, "qcom,ipc", 2, 0,
+					       &args);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to get ipc node\n");
+
+	ipc_np = args.np;
+
+	desc->regmap = device_node_to_regmap(ipc_np);
+	if (IS_ERR(desc->regmap)) {
+		dev_err(dev, "Failed to get regmap from ipc node\n");
+		of_node_put(ipc_np);
+		return PTR_ERR(desc->regmap);
+	}
+
+	desc->offset = args.args[0];
+	desc->bit = args.args[1];
+
+	of_node_put(ipc_np);
+
+	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");
+
+	hdev = hci_alloc_dev();
+	if (!hdev)
+		return -ENOMEM;
+
+	hci_set_drvdata(hdev, desc);
+	desc->hdev = hdev;
+	SET_HCIDEV_DEV(hdev, &pdev->dev);
+	hdev->bus = HCI_IPC;
+
+	hdev->open = btqcomipc_open;
+	hdev->close = btqcomipc_close;
+	hdev->setup = btqcomipc_setup;
+	hdev->send = btqcomipc_send;
+	hdev->flush = btqcomipc_flush;
+	hdev->set_bdaddr = qca_set_bdaddr;
+
+	ret = btqcomipc_init(desc);
+	if (ret) {
+		hci_free_dev(hdev);
+		return dev_err_probe(dev, ret, "Failed to initialize\n");
+	}
+
+	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);
+
+	/* first unregister to stop incoming HCI traffic */
+	hci_unregister_dev(desc->hdev);
+
+	/* disable interrupt and asynchronous operations before teardown */
+	btqcomipc_deinit(desc);
+
+	hci_free_dev(desc->hdev);
+	desc->hdev = NULL;
+}
+
+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] 21+ messages in thread

* [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09  9:18 [PATCH v5 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (3 preceding siblings ...)
  2026-07-09  9:18 ` [PATCH v5 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
@ 2026-07-09  9:18 ` George Moussalem via B4 Relay
  2026-07-09  9:31   ` sashiko-bot
                     ` (2 more replies)
  2026-07-09  9:18 ` [PATCH v5 6/6] MAINTAINERS: Add IPQ5018 driver to Qualcomm Bluetooth driver entry George Moussalem via B4 Relay
  5 siblings, 3 replies; 21+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-09  9:18 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 node to enable Bluetooth support on the IPQ5018 platform.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
index 6f8004a22a1f..8c252fa3ff5b 100644
--- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
+++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
@@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
 			reg = <0x01937000 0x21000>;
 		};
 
+		bluetooth: bluetooth@7000000 {
+			compatible = "qcom,ipq5018-bt";
+			reg = <0x07000000 0x58000>;
+
+			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>;
+
+			status = "disabled";
+		};
+
 		sdhc_1: mmc@7804000 {
 			compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
 			reg = <0x7804000 0x1000>;

-- 
2.53.0



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

* [PATCH v5 6/6] MAINTAINERS: Add IPQ5018 driver to Qualcomm Bluetooth driver entry
  2026-07-09  9:18 [PATCH v5 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
                   ` (4 preceding siblings ...)
  2026-07-09  9:18 ` [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support George Moussalem via B4 Relay
@ 2026-07-09  9:18 ` George Moussalem via B4 Relay
  5 siblings, 0 replies; 21+ messages in thread
From: George Moussalem via B4 Relay @ 2026-07-09  9:18 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 Qualcomm IPQ5018 Bluetooth driver to the existing entry for
Qualcomm Bluetooth drivers.

Acked-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: George Moussalem <george.moussalem@outlook.com>
---
 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index eb8cdcc76324..33dd0c70c277 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21876,6 +21876,7 @@ L:	linux-arm-msm@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/net/bluetooth/qcom,*
 F:	drivers/bluetooth/btqca.[ch]
+F:	drivers/bluetooth/btqcomipc.c
 F:	drivers/bluetooth/btqcomsmd.c
 F:	drivers/bluetooth/hci_qca.c
 

-- 
2.53.0



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

* Re: [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-09  9:18 ` [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
@ 2026-07-09  9:27   ` sashiko-bot
  2026-07-09  9:29     ` George Moussalem
  0 siblings, 1 reply; 21+ messages in thread
From: sashiko-bot @ 2026-07-09  9:27 UTC (permalink / raw)
  To: George Moussalem; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The new `QCOM_SCM_PIL_PAS_BT_PWR_MODE` macro uses the exact same command ID (`0x21`) as the existing `QCOM_SCM_PIL_PAS_GET_RSCTABLE` macro within the `QCOM_SCM_SVC_PIL` service.
--

--- Patch [3]: [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes ---
commit 7001f29bdf69c1e84287e5d8734b48feda414e41
Author: George Moussalem <george.moussalem@outlook.com>

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

The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
modes which are configured through a Secure Channel Manager (SCM) call to
TrustZone. This commit adds a function to perform this call after checking
if the feature 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: High]
Is it intended for QCOM_SCM_PIL_PAS_BT_PWR_MODE to use the exact same
command ID (0x21) as QCOM_SCM_PIL_PAS_GET_RSCTABLE?

Because both definitions share 0x21 within the QCOM_SCM_SVC_PIL service,
when __qcom_scm_is_call_available() queries the firmware for
QCOM_SCM_PIL_PAS_BT_PWR_MODE, it will falsely return true on any SoC that
already supports GET_RSCTABLE.

This would allow qcom_scm_pas_set_bluetooth_power_mode() to incorrectly
proceed and execute a 2-argument SCM call to TrustZone. Could this cause
the firmware to route the call to the GET_RSCTABLE handler (which expects
5 arguments, including physical addresses for DMA), potentially leading
to a Secure World memory fault?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ipq5018-bluetooth-v5-0-e476c41f03b8@outlook.com?part=3

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

* Re: [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes
  2026-07-09  9:27   ` sashiko-bot
@ 2026-07-09  9:29     ` George Moussalem
  0 siblings, 0 replies; 21+ messages in thread
From: George Moussalem @ 2026-07-09  9:29 UTC (permalink / raw)
  To: sashiko-reviews, Bartosz Golaszewski, Konrad Dybcio,
	Dmitry Baryshkov
  Cc: robh, conor+dt, devicetree

adding Bartosz, Konrad, and Dmitry for awareness.

On 7/9/26 13:27, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] The new `QCOM_SCM_PIL_PAS_BT_PWR_MODE` macro uses the exact same command ID (`0x21`) as the existing `QCOM_SCM_PIL_PAS_GET_RSCTABLE` macro within the `QCOM_SCM_SVC_PIL` service.
> --
> 
> --- Patch [3]: [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes ---
> commit 7001f29bdf69c1e84287e5d8734b48feda414e41
> Author: George Moussalem <george.moussalem@outlook.com>
> 
> firmware: qcom: scm: Add support for setting Bluetooth power modes
> 
> The Bluetooth subsystem (BTSS) on the IPQ5018 SoC supports setting power
> modes which are configured through a Secure Channel Manager (SCM) call to
> TrustZone. This commit adds a function to perform this call after checking
> if the feature 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: High]
> Is it intended for QCOM_SCM_PIL_PAS_BT_PWR_MODE to use the exact same
> command ID (0x21) as QCOM_SCM_PIL_PAS_GET_RSCTABLE?

Yes, this is intended. This is the documented power mode register for
Bluetooth on IPQ5018.

> 
> Because both definitions share 0x21 within the QCOM_SCM_SVC_PIL service,
> when __qcom_scm_is_call_available() queries the firmware for
> QCOM_SCM_PIL_PAS_BT_PWR_MODE, it will falsely return true on any SoC that
> already supports GET_RSCTABLE.
> 
> This would allow qcom_scm_pas_set_bluetooth_power_mode() to incorrectly
> proceed and execute a 2-argument SCM call to TrustZone. Could this cause
> the firmware to route the call to the GET_RSCTABLE handler (which expects
> 5 arguments, including physical addresses for DMA), potentially leading
> to a Secure World memory fault?
> 

Best regards,
George

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

* Re: [PATCH v5 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver
  2026-07-09  9:18 ` [PATCH v5 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
@ 2026-07-09  9:30   ` sashiko-bot
  0 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-09  9:30 UTC (permalink / raw)
  To: George Moussalem; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 11 potential issue(s) to consider:
- [Critical] Unvalidated ring buffer offsets read from shared memory allow arbitrary kernel memory read and write via `TO_APPS_ADDR`.
- [High] Missing bounds check on ELF segment `p_memsz` during firmware load allows out-of-bounds memory zeroing.
- [High] Inconsistent spinlock context for `desc->lock` causes a potential deadlock between `btss_worker()` and `btqcomipc_send()`.
- [High] Unvalidated ring buffer linked list traversal allows untrusted firmware to hang the host CPU.
- [High] Flawed mathematical validation for wrapped long RX frames allows out-of-bounds I/O memory reads.
- [High] Missing workqueue cancellation and unsafe firmware shutdown order in `btqcomipc_close()` leads to a hardware fault or UAF.
- [High] Time-of-check to time-of-use (TOCTOU) and missing size bounds checks on `rx_ctxt->total_size` lead to an out-of-bounds `tx_ctxt` pointer.
- [High] Missing validation of buffer counts from shared memory triggers divide-by-zero panics.
- [Medium] Directly sending short messages bypasses queued long messages, violating HCI FIFO ordering.
- [Medium] Overwriting the `ack` parameter in `btss_process_rx` leads to silently dropped interrupt ACKs.
- [Medium] The driver accesses I/O-mapped memory via direct C pointer dereferences, causing potential alignment faults.
--

--- Patch [4]: [PATCH v5 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver ---
commit bc9200dabd0dfb475dc74e5c716cbd6ea1df35df
Author: George Moussalem <george.moussalem@outlook.com>
Subject: Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver

This commit adds support for the Bluetooth controller found in the IPQ5018
SoC. It implements firmware loading and the transport layer between the
HCI core and the controller using a shared memory ring buffer topology.

> diff --git a/drivers/bluetooth/btqcomipc.c b/drivers/bluetooth/btqcomipc.c
> index 0000000000000..9d71fe6117723
> --- /dev/null
> --- b/drivers/bluetooth/btqcomipc.c

[ ... ]

> @@ -275,12 +275,13 @@ static int btss_send(struct qcom_btss *desc, u16 msg_hdr,
>      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);
>  
>      dma_wmb();
>  
[Severity: High]
Does this modulo operation rely on desc->tx_ctxt->smsg_buf_cnt being
non-zero?

If the firmware modifies the count to zero in shared memory, could this
cause a kernel panic via division-by-zero?

>      rinfo->widx = (rinfo->widx + 1) % desc->tx_ctxt->smsg_buf_cnt;
>  
>      regmap_set_bits(desc->regmap, desc->offset, BIT(desc->bit));
>  
>      return 0;
>  }

[ ... ]

> @@ -375,15 +376,16 @@ static int btss_recv_cust_frame(struct qcom_btss *desc, u8 cmd)
>          WRITE_ONCE(desc->running, false);
>          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;
>          }
>  
[Severity: High]
Does this code read total_size directly from shared memory without bounds
checking?

Since this bypasses the validation in btss_worker(), could it result in
desc->tx_ctxt pointing to unallocated memory if the firmware modifies it
after initialization?

>          desc->tx_ctxt = (struct context_info *)((void *)desc->rx_ctxt +
>                  le16_to_cpu(desc->rx_ctxt->total_size));
>          desc->lmsg_ctxt.widx = 0;

[ ... ]

> @@ -458,16 +460,25 @@ static inline int btss_process_rx(struct qcom_btss *desc,
>      lring_buf = le32_to_cpu(desc->rx_ctxt->lring_buf);
>      widx = READ_ONCE(rinfo->widx);
>  
>      while (ridx != widx) {
>          struct ipc_aux_ptr aux_ptr = { };
>  
>          dma_rmb();
>  
[Severity: Critical]
Are rinfo->rbuf and ridx validated against the bounds of the shared memory
region before being used?

If the controller writes arbitrary offsets here, could TO_APPS_ADDR result
in reading from or writing to arbitrary host kernel memory?

>          rbuf = &((struct ring_buffer *)(TO_APPS_ADDR(le32_to_cpu(rinfo->rbuf))))[ridx];
[Severity: Medium]
Is it safe to directly dereference iomem-mapped memory pointers like
rbuf->msg_hdr and rbuf->len without using the proper readw() or readl()
I/O accessors?

Could this trigger alignment faults on certain architectures?

>          msg_hdr = le16_to_cpu(rbuf->msg_hdr);
>          msg_len = le16_to_cpu(rbuf->len);
>  
>          if (!(msg_hdr & IPC_HDR_LONG_MSG) && msg_len > IPC_MSG_PLD_SZ) {
>              desc->hdev->stat.err_rx++;
>              bt_dev_err(desc->hdev,
>                     "Dropping short RX frame with invalid len %u",
>                     msg_len);
>              goto continue_rx;
>          }
>  
>          if (msg_hdr & IPC_HDR_LONG_MSG) {
[Severity: Critical]
Is there a risk of leaking arbitrary kernel memory here?

Since lmsg_data is read straight from the shared ring buffer, an attacker
controlling the firmware could provide an arbitrary pointer that becomes
converted to a host address via TO_APPS_ADDR.

>              lmsg_data = le32_to_cpu(rbuf->payload.lmsg_data);
>              rxbuf = TO_APPS_ADDR(lmsg_data);
>              lsz = btss_lbuf_size(desc, desc->rx_ctxt);

[ ... ]

>              /*
>               * Check if the long message buffer extends past the end
>               * of the ring buffer
>               */
>              if (msg_len > lring_end - lmsg_data) {
>                  lbuf_idx = btss_get_buf_idx(desc->rx_ctxt,
>                                  lmsg_data, lsz);
>  
>                  blks_consumed = desc->rx_ctxt->lmsg_buf_cnt - lbuf_idx;
[Severity: High]
Can this validation ever evaluate to true?

Based on the preceding checks, if msg_len > blks_consumed * lsz, then
mathematically msg_len / lsz must be greater than or equal to blks_consumed.

Could a malformed msg_len completely bypass this check and cause an
out-of-bounds memcpy_fromio()?

>                  if (blks_consumed > msg_len / lsz) {
>                      desc->hdev->stat.err_rx++;
>                      bt_dev_err(desc->hdev,
>                             "Dropping long RX frame with invalid length %u",
>                             msg_len);
>                      goto continue_rx;
>                  }
>                  aux_ptr.len = msg_len - (blks_consumed * lsz);
>                  aux_ptr.buf = lring_buf;
>              }
>          } else {
>              rxbuf = rbuf->payload.smsg_data;
>          }
>  
[Severity: Medium]
Will this overwrite the ack flag instead of accumulating it?

If an earlier message in the loop requested an ACK but the final message
does not, will the hardware signaling loop silently drop the ACK request?

>          *ack = msg_hdr & IPC_HDR_REQ_ACK;
>  
>          pkt_type = FIELD_GET(IPC_HDR_PKT_TYPE_MASK, msg_hdr);

[ ... ]

>  continue_rx:
[Severity: High]
Does this modulo operation rely on rinfo->ring_buf_cnt being non-zero?

If the firmware modifies the count to zero in shared memory, could this
cause a kernel panic via division-by-zero?

>          ridx = (ridx + 1) % rinfo->ring_buf_cnt;
>          rinfo->ridx = ridx;
>  
>          if (rx_count)
>              (*rx_count)++;
>      }

[ ... ]

> @@ -515,16 +526,26 @@ 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, next;
>      u16 total_size;
>      int ret;
>  
[Severity: High]
Could this lead to a deadlock?

The btss_worker runs in process context and acquires desc->lock without
disabling softirqs. If a network softirq interrupts this CPU and calls
btqcomipc_send() which uses spin_lock_irqsave(), will it spin forever
waiting for this lock?

>      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->base);
>          if (offset > desc->mem_size - sizeof(struct context_info)) {
>              bt_dev_err(desc->hdev,
>                     "Invalid offset after firmware boot: 0x%08x",
>                     offset);
>              goto spin_unlock;
>          }
>          dev_dbg(desc->dev, "offset after firmware boot: 0x%08x\n",
>              offset);
>  
>          desc->rx_ctxt = (struct context_info *)(desc->base + offset);
>  
>          total_size = le16_to_cpu(desc->rx_ctxt->total_size);
[Severity: High]
Is this bounds check sufficient for the tx_ctxt pointer?

The check ensures rx_ctxt total_size is within mem_size, but tx_ctxt is
later set to rx_ctxt + total_size. Should it also verify that there is
enough room for the sizeof(struct context_info) of tx_ctxt itself at the
end of the region?

>          if (total_size < sizeof(struct context_info) ||
>          total_size > desc->mem_size - offset) {
>              bt_dev_err(desc->hdev, "Invalid total size: %u",
>                     total_size);
>              desc->rx_ctxt = NULL;
>              goto spin_unlock;
>          }

[ ... ]

>      rinfo = &(desc->rx_ctxt->sring_buf_info);
>      while (rinfo != NULL) {
>          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;
>          }
[Severity: High]
Could untrusted firmware hang the host CPU here?

The next pointer is read from shared memory. If the firmware intentionally
creates a cycle in the linked list, will this while loop execute
indefinitely and cause a Denial of Service?

>          next = le32_to_cpu(rinfo->next);
>          rinfo = next ? (struct ring_buffer_info *)TO_APPS_ADDR(next) : NULL;
>      }

[ ... ]

> @@ -661,16 +672,21 @@ static int btcomqipc_firmware_load(struct qcom_btss *desc)
>              memcpy_toio(desc->base + phdr->p_vaddr,
>                      seg_fw->data, phdr->p_filesz);
>              if (phdr->p_memsz > phdr->p_filesz)
>                  memset_io(desc->base + phdr->p_vaddr +
>                        phdr->p_filesz, 0,
>                        phdr->p_memsz - phdr->p_filesz);
>  
>  release_seg_fw:
>              release_firmware(seg_fw);
>  release_seg_name:
>              kfree(seg_name);
>              seg_name = NULL;
>  
>              if (ret)
>                  goto release_fw;
>          } else {
>              memcpy_toio(desc->base + phdr->p_vaddr,
>                      fw->data + phdr->p_offset, phdr->p_filesz);
[Severity: High]
Does this memset_io call lack a bounds check on phdr->p_memsz?

The earlier validation only ensures that p_filesz fits within
desc->mem_size. If a malformed ELF file has a massive p_memsz, could this
zero out kernel memory beyond the mapped desc->base region?

>              if (phdr->p_memsz > phdr->p_filesz)
>                  memset_io(desc->base + phdr->p_vaddr +
>                        phdr->p_filesz, 0,
>                        phdr->p_memsz - phdr->p_filesz);
>          }

[ ... ]

> @@ -731,16 +747,21 @@ static int btqcomipc_close(struct hci_dev *hdev)
>      struct qcom_btss *desc = hci_get_drvdata(hdev);
>      unsigned long flags;
>      int ret;
>  
>      ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
>      if (ret) {
>          bt_dev_err(hdev, "Failed to stop firmware: %d", ret);
>          return ret;
>      }
>  
>      disable_irq(desc->irq);
>  
[Severity: High]
Is it safe to shut down the physical IP without canceling the asynchronous
worker first?

If btss_worker() is actively running and reading from desc->base, could
powering down the controller via SCM cause a Synchronous External Abort or
hardware fault before the running flag is set to false?

>      spin_lock_irqsave(&desc->lock, flags);
>      WRITE_ONCE(desc->running, false);
>      spin_unlock_irqrestore(&desc->lock, flags);

[ ... ]

> @@ -779,16 +800,21 @@ 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);
>      unsigned long flags;
>      int ret;
>  
>      spin_lock_irqsave(&desc->lock, flags);
>  
>      if (unlikely(!READ_ONCE(desc->running))) {
>          bt_dev_err(hdev,
>                 "BTSS not initialized, failed to send message");
>          ret = -ENODEV;
>          goto spin_unlock;
>      }
>  
[Severity: Medium]
Will this bypass the FIFO order for HCI frames?

If a previous long frame failed to send and was queued to desc->tx_q via
-EAGAIN, will attempting to send a new short frame directly leapfrog the
backlog, causing silent protocol violations?

>      ret = btss_send(desc, hdr, skb);
>      if (ret) {
>          if (ret == -EAGAIN) {
>              if (skb_queue_len(&desc->tx_q) >= IPC_TX_QSIZE) {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ipq5018-bluetooth-v5-0-e476c41f03b8@outlook.com?part=4

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09  9:18 ` [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support George Moussalem via B4 Relay
@ 2026-07-09  9:31   ` sashiko-bot
  2026-07-09 18:16   ` Konrad Dybcio
  2026-07-09 18:17   ` Konrad Dybcio
  2 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-07-09  9:31 UTC (permalink / raw)
  To: George Moussalem; +Cc: robh, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Out-of-bounds write in `memset_io` during firmware ELF loading.
- [High] Unvalidated hardware/firmware inputs from shared memory lead to division-by-zero panics and out-of-bounds accesses.
- [High] Direct pointer dereferences to I/O memory without using proper I/O accessors, leading to potential alignment faults.
- [High] Incorrect teardown sequence in `btqcomipc_close` causing a race condition and unrecoverable bus faults.
--

--- Patch [5]: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support ---
commit 30194f675ebc660c7f509c3ca16e858f9db019e2
Author: George Moussalem <george.moussalem@outlook.com>
Subject: arm64: dts: qcom: ipq5018: add node required for Bluetooth support

This commit adds a device tree node to enable Bluetooth support on the
IPQ5018 platform. It sets up the bluetooth node with the appropriate
register space, firmware name, clocks, resets, and interrupts.

> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> index 6f8004a22a1ff..8c252fa3ff5bf 100644
> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>  			reg = <0x01937000 0x21000>;
>  		};
>  
> +		bluetooth: bluetooth@7000000 {
> +			compatible = "qcom,ipq5018-bt";
> +			reg = <0x07000000 0x58000>;
> +
> +			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>;
> +
> +			status = "disabled";
> +		};
> +
>  		sdhc_1: mmc@7804000 {
>  			compatible = "qcom,ipq5018-sdhci", "qcom,sdhci-msm-v5";
>  			reg = <0x7804000 0x1000>;

While reviewing the driver code associated with this hardware, a few questions
came up regarding the implementation in drivers/bluetooth/btqcomipc.c.

drivers/bluetooth/btqcomipc.c:btcomqipc_firmware_load() {
...
			if (phdr->p_memsz > phdr->p_filesz)
				memset_io(desc->base + phdr->p_vaddr +
					  phdr->p_filesz, 0,
					  phdr->p_memsz - phdr->p_filesz);
...
}

[Severity: High]
Can this lead to an out-of-bounds I/O memory write?

When the firmware ELF is parsed in btcomqipc_firmware_load(), it appears
we validate that phdr->p_filesz fits within the allocated IO memory, but
is there a similar check for phdr->p_memsz?

If a malformed firmware image provides an arbitrarily large p_memsz, could
this memset_io() write zeroes out-of-bounds past the end of the desc->base
mapping?


drivers/bluetooth/btqcomipc.c:btss_process_rx() {
...
		ridx = (ridx + 1) % rinfo->ring_buf_cnt;
		rinfo->ridx = ridx;
...
}

drivers/bluetooth/btqcomipc.c:btss_get_tx_rbuf() {
...
		idx = (rinfo->widx + 1) % (desc->tx_ctxt->smsg_buf_cnt);
...
}

[Severity: High]
Could these modulo operations cause a division-by-zero panic?

The firmware running on the Bluetooth controller populates the context_info
and ring_buffer_info structures in shared memory. If the firmware sets
rinfo->ring_buf_cnt or desc->tx_ctxt->smsg_buf_cnt to 0, could the kernel
crash here?

Additionally, are offsets like rinfo->rbuf checked against desc->mem_size
to prevent out-of-bounds I/O memory accesses?


drivers/bluetooth/btqcomipc.c:btss_process_rx() {
...
		msg_hdr = le16_to_cpu(rbuf->msg_hdr);
		msg_len = le16_to_cpu(rbuf->len);
...
}

drivers/bluetooth/btqcomipc.c:btss_send() {
...
	rbuf->msg_hdr = cpu_to_le16(hdr);
	rbuf->len = cpu_to_le16(len);
...
}

drivers/bluetooth/btqcomipc.c:btss_worker() {
...
		total_size = le16_to_cpu(desc->rx_ctxt->total_size);
...
}

[Severity: High]
Does this code perform direct pointer dereferences to I/O memory without
using proper I/O accessors?

The memory mapped via devm_platform_get_and_ioremap_resource() is Device
memory on ARM64. Since the driver drops the __iomem annotation and accesses
packed structures using direct C pointer assignments, could the compiler
emit unaligned load/store instructions?

Executing these instructions against ARM64 Device memory violates
architectural rules and could trigger an alignment fault.


drivers/bluetooth/btqcomipc.c:btqcomipc_close() {
...
	ret = qcom_scm_pas_shutdown(BTSS_PAS_ID);
	if (ret) {
		bt_dev_err(hdev, "Failed to stop firmware: %d", ret);
		return ret;
	}

	disable_irq(desc->irq);

	spin_lock_irqsave(&desc->lock, flags);
	WRITE_ONCE(desc->running, false);
...
}

[Severity: High]
Is the teardown sequence in btqcomipc_close() safe from race conditions?

The driver calls qcom_scm_pas_shutdown() to power down the Bluetooth
subsystem before disabling the interrupt, setting desc->running = false,
and flushing the workqueue.

If an interrupt fires during or immediately after shutdown, or if btss_worker
is already running concurrently, could the worker attempt to read from or
write to the BTSS memory-mapped I/O registers? Accessing a powered-off
peripheral's registers typically results in a fatal bus error.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ipq5018-bluetooth-v5-0-e476c41f03b8@outlook.com?part=5

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

* Re: [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support
  2026-07-09  9:18 ` [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
@ 2026-07-09  9:31   ` sashiko-bot
  2026-07-09  9:35     ` George Moussalem
  0 siblings, 1 reply; 21+ messages in thread
From: sashiko-bot @ 2026-07-09  9:31 UTC (permalink / raw)
  To: George Moussalem; +Cc: conor+dt, devicetree, robh

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Incomplete implementation resulting in dead code: the QCA_IPQ5018 SoC type is not mapped in any device tree match table.
- [High] The implementation unconditionally skips downloading the required rampatch firmware `qca/bt_fw_patch.mbn`.
--

--- Patch [2]: [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support ---
commit 388a60ab03a30caa9b4a4e153c1ccb4a83f4283c
Author: George Moussalem <george.moussalem@outlook.com>

Bluetooth: btqca: Add IPQ5018 support

This commit adds the IPQ5018 SoC type and support for loading its firmware
for Bluetooth support.

> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
> index 22b08ab05b829..f5ded8fb1e3ab 100644
> --- a/drivers/bluetooth/btqca.c
> +++ b/drivers/bluetooth/btqca.c
> @@ -775,6 +778,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;
> +

[Severity: High]
The commit message explicitly states that qca/bt_fw_patch.mbn is needed,
but doesn't this goto statement unconditionally skip the entire rampatch
download block?

If a firmware-name is provided via the device tree, it appears it would
also be incorrectly loaded as the NVM configuration instead of the rampatch
due to this jump.

[ ... ]

> diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
> index 8f3c1b1c77b3d..343cd62d11371 100644
> --- a/drivers/bluetooth/btqca.h
> +++ b/drivers/bluetooth/btqca.h
> @@ -158,6 +160,7 @@ enum qca_btsoc_type {
>  	QCA_WCN6750,
>  	QCA_WCN6855,
>  	QCA_WCN7850,
> +	QCA_IPQ5018,
>  };

[Severity: High]
Is there a missing device tree match mapping for QCA_IPQ5018 in hci_qca.c?

Without adding an entry like qcom,ipq5018-bt to qca_bluetooth_of_match,
it seems the driver will never probe the device and soc_type will never
be set to QCA_IPQ5018. This would make the newly added firmware loading
logic completely unreachable.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709-ipq5018-bluetooth-v5-0-e476c41f03b8@outlook.com?part=2

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

* Re: [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support
  2026-07-09  9:31   ` sashiko-bot
@ 2026-07-09  9:35     ` George Moussalem
  0 siblings, 0 replies; 21+ messages in thread
From: George Moussalem @ 2026-07-09  9:35 UTC (permalink / raw)
  To: sashiko-reviews, Bartosz Golaszewski, Konrad Dybcio,
	Dmitry Baryshkov
  Cc: conor+dt, devicetree, robh

adding Bartosz, Konrad, and Dmitry for awareness.

On 7/9/26 13:31, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] Incomplete implementation resulting in dead code: the QCA_IPQ5018 SoC type is not mapped in any device tree match table.
> - [High] The implementation unconditionally skips downloading the required rampatch firmware `qca/bt_fw_patch.mbn`.
> --
> 
> --- Patch [2]: [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support ---
> commit 388a60ab03a30caa9b4a4e153c1ccb4a83f4283c
> Author: George Moussalem <george.moussalem@outlook.com>
> 
> Bluetooth: btqca: Add IPQ5018 support
> 
> This commit adds the IPQ5018 SoC type and support for loading its firmware
> for Bluetooth support.
> 
>> diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
>> index 22b08ab05b829..f5ded8fb1e3ab 100644
>> --- a/drivers/bluetooth/btqca.c
>> +++ b/drivers/bluetooth/btqca.c
>> @@ -775,6 +778,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;
>> +
> 
> [Severity: High]
> The commit message explicitly states that qca/bt_fw_patch.mbn is needed,
> but doesn't this goto statement unconditionally skip the entire rampatch
> download block?
> 
> If a firmware-name is provided via the device tree, it appears it would
> also be incorrectly loaded as the NVM configuration instead of the rampatch
> due to this jump.

firmware loading is handled in the driver itself. The patch mpvn10.bin
is handled here, hence the goto to skip the firmware loading section.

> 
> [ ... ]
> 
>> diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
>> index 8f3c1b1c77b3d..343cd62d11371 100644
>> --- a/drivers/bluetooth/btqca.h
>> +++ b/drivers/bluetooth/btqca.h
>> @@ -158,6 +160,7 @@ enum qca_btsoc_type {
>>  	QCA_WCN6750,
>>  	QCA_WCN6855,
>>  	QCA_WCN7850,
>> +	QCA_IPQ5018,
>>  };
> 
> [Severity: High]
> Is there a missing device tree match mapping for QCA_IPQ5018 in hci_qca.c?
> 
> Without adding an entry like qcom,ipq5018-bt to qca_bluetooth_of_match,
> it seems the driver will never probe the device and soc_type will never
> be set to QCA_IPQ5018. This would make the newly added firmware loading
> logic completely unreachable.
> 

the compatible is found in its own dedicated driver: btqcomipc.c

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09  9:18 ` [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support George Moussalem via B4 Relay
  2026-07-09  9:31   ` sashiko-bot
@ 2026-07-09 18:16   ` Konrad Dybcio
  2026-07-09 18:23     ` George Moussalem
  2026-07-09 18:17   ` Konrad Dybcio
  2 siblings, 1 reply; 21+ messages in thread
From: Konrad Dybcio @ 2026-07-09 18:16 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/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Add node to enable Bluetooth support on the IPQ5018 platform.
> 
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> index 6f8004a22a1f..8c252fa3ff5b 100644
> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>  			reg = <0x01937000 0x21000>;
>  		};
>  
> +		bluetooth: bluetooth@7000000 {
> +			compatible = "qcom,ipq5018-bt";
> +			reg = <0x07000000 0x58000>;
> +
> +			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>;
> +
> +			status = "disabled";

Since the firmware is unsigned, I think it could make sense to
enable this by default

any which way

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

Konrad

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09  9:18 ` [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support George Moussalem via B4 Relay
  2026-07-09  9:31   ` sashiko-bot
  2026-07-09 18:16   ` Konrad Dybcio
@ 2026-07-09 18:17   ` Konrad Dybcio
  2026-07-09 18:25     ` George Moussalem
  2 siblings, 1 reply; 21+ messages in thread
From: Konrad Dybcio @ 2026-07-09 18:17 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/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
> From: George Moussalem <george.moussalem@outlook.com>
> 
> Add node to enable Bluetooth support on the IPQ5018 platform.
> 
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
> ---
>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> index 6f8004a22a1f..8c252fa3ff5b 100644
> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>  			reg = <0x01937000 0x21000>;
>  		};
>  
> +		bluetooth: bluetooth@7000000 {
> +			compatible = "qcom,ipq5018-bt";
> +			reg = <0x07000000 0x58000>;
> +
> +			firmware-name = "qca/bt_fw_patch.mbn";

Hm, rethinking this, it must have ipq5018 somewhere in the name

Konrad

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09 18:16   ` Konrad Dybcio
@ 2026-07-09 18:23     ` George Moussalem
  0 siblings, 0 replies; 21+ messages in thread
From: George Moussalem @ 2026-07-09 18:23 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,
	Bartosz Golaszewski

On 7/9/26 22:16, Konrad Dybcio wrote:
> On 7/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add node to enable Bluetooth support on the IPQ5018 platform.
>>
>> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>>  1 file changed, 16 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>> index 6f8004a22a1f..8c252fa3ff5b 100644
>> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>>  			reg = <0x01937000 0x21000>;
>>  		};
>>  
>> +		bluetooth: bluetooth@7000000 {
>> +			compatible = "qcom,ipq5018-bt";
>> +			reg = <0x07000000 0x58000>;
>> +
>> +			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>;
>> +
>> +			status = "disabled";
> 
> Since the firmware is unsigned, I think it could make sense to
> enable this by default
> 
> any which way

I've found that the controller is not wired up on all ipq5018 boards.
For ex. Linksys router boards have it enabled and wired up by default
while you can enable the controller on some Xiaomi and Gl.Inet boards
but the controller isn't connected to anything.

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

George

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09 18:17   ` Konrad Dybcio
@ 2026-07-09 18:25     ` George Moussalem
  2026-07-09 18:40       ` Konrad Dybcio
  0 siblings, 1 reply; 21+ messages in thread
From: George Moussalem @ 2026-07-09 18:25 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,
	Bartosz Golaszewski

On 7/9/26 22:17, Konrad Dybcio wrote:
> On 7/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
>> From: George Moussalem <george.moussalem@outlook.com>
>>
>> Add node to enable Bluetooth support on the IPQ5018 platform.
>>
>> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>> ---
>>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>>  1 file changed, 16 insertions(+)
>>
>> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>> index 6f8004a22a1f..8c252fa3ff5b 100644
>> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>>  			reg = <0x01937000 0x21000>;
>>  		};
>>  
>> +		bluetooth: bluetooth@7000000 {
>> +			compatible = "qcom,ipq5018-bt";
>> +			reg = <0x07000000 0x58000>;
>> +
>> +			firmware-name = "qca/bt_fw_patch.mbn";
> 
> Hm, rethinking this, it must have ipq5018 somewhere in the name

I agree but all QCA BT firmware and rampatches are published in the
/lib/firmware/qca directory, see:
https://git.kernel.org/pub/scm/linux/kernel/git/ath/linux-firmware.git/tree/qca

> 
> Konrad

George

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09 18:25     ` George Moussalem
@ 2026-07-09 18:40       ` Konrad Dybcio
  2026-07-10  9:14         ` Bartosz Golaszewski
  0 siblings, 1 reply; 21+ messages in thread
From: Konrad Dybcio @ 2026-07-09 18:40 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,
	Jeff Johnson, Bartosz Golaszewski
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	Bartosz Golaszewski

On 7/9/26 8:25 PM, George Moussalem wrote:
> On 7/9/26 22:17, Konrad Dybcio wrote:
>> On 7/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
>>> From: George Moussalem <george.moussalem@outlook.com>
>>>
>>> Add node to enable Bluetooth support on the IPQ5018 platform.
>>>
>>> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>>> ---
>>>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>>>  1 file changed, 16 insertions(+)
>>>
>>> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>> index 6f8004a22a1f..8c252fa3ff5b 100644
>>> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>>>  			reg = <0x01937000 0x21000>;
>>>  		};
>>>  
>>> +		bluetooth: bluetooth@7000000 {
>>> +			compatible = "qcom,ipq5018-bt";
>>> +			reg = <0x07000000 0x58000>;
>>> +
>>> +			firmware-name = "qca/bt_fw_patch.mbn";
>>
>> Hm, rethinking this, it must have ipq5018 somewhere in the name
> 
> I agree but all QCA BT firmware and rampatches are published in the
> /lib/firmware/qca directory, see:
> https://git.kernel.org/pub/scm/linux/kernel/git/ath/linux-firmware.git/tree/qca

+Jeff, Bartosz?

Konrad

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-09 18:40       ` Konrad Dybcio
@ 2026-07-10  9:14         ` Bartosz Golaszewski
  2026-07-10  9:23           ` Konrad Dybcio
  0 siblings, 1 reply; 21+ messages in thread
From: Bartosz Golaszewski @ 2026-07-10  9:14 UTC (permalink / raw)
  To: Konrad Dybcio
  Cc: linux-arm-msm, linux-bluetooth, devicetree, linux-kernel,
	Bartosz Golaszewski, George Moussalem, Bartosz Golaszewski,
	Marcel Holtmann, Luiz Augusto von Dentz, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	Philipp Zabel, Jeff Johnson

On Thu, 9 Jul 2026 20:40:04 +0200, Konrad Dybcio
<konrad.dybcio@oss.qualcomm.com> said:
> On 7/9/26 8:25 PM, George Moussalem wrote:
>> On 7/9/26 22:17, Konrad Dybcio wrote:
>>> On 7/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
>>>> From: George Moussalem <george.moussalem@outlook.com>
>>>>
>>>> Add node to enable Bluetooth support on the IPQ5018 platform.
>>>>
>>>> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>>>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>>>> ---
>>>>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>>>>  1 file changed, 16 insertions(+)
>>>>
>>>> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>> index 6f8004a22a1f..8c252fa3ff5b 100644
>>>> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>>>>  			reg = <0x01937000 0x21000>;
>>>>  		};
>>>>
>>>> +		bluetooth: bluetooth@7000000 {
>>>> +			compatible = "qcom,ipq5018-bt";
>>>> +			reg = <0x07000000 0x58000>;
>>>> +
>>>> +			firmware-name = "qca/bt_fw_patch.mbn";
>>>
>>> Hm, rethinking this, it must have ipq5018 somewhere in the name
>>
>> I agree but all QCA BT firmware and rampatches are published in the
>> /lib/firmware/qca directory, see:
>> https://git.kernel.org/pub/scm/linux/kernel/git/ath/linux-firmware.git/tree/qca
>
> +Jeff, Bartosz?
>

I'm not sure what the question is. This particular blob doesn't seem to exist
anywhere in linux-firmware yet. I agree, ideally the platform name should be in
the firmware name so let's upstream it to linux-firmware as such? Am I missing
something?

Bartosz

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

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

On 7/10/26 11:14 AM, Bartosz Golaszewski wrote:
> On Thu, 9 Jul 2026 20:40:04 +0200, Konrad Dybcio
> <konrad.dybcio@oss.qualcomm.com> said:
>> On 7/9/26 8:25 PM, George Moussalem wrote:
>>> On 7/9/26 22:17, Konrad Dybcio wrote:
>>>> On 7/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
>>>>> From: George Moussalem <george.moussalem@outlook.com>
>>>>>
>>>>> Add node to enable Bluetooth support on the IPQ5018 platform.
>>>>>
>>>>> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>>>>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>>>>> ---
>>>>>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>>>>>  1 file changed, 16 insertions(+)
>>>>>
>>>>> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>>> index 6f8004a22a1f..8c252fa3ff5b 100644
>>>>> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>>> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>>> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>>>>>  			reg = <0x01937000 0x21000>;
>>>>>  		};
>>>>>
>>>>> +		bluetooth: bluetooth@7000000 {
>>>>> +			compatible = "qcom,ipq5018-bt";
>>>>> +			reg = <0x07000000 0x58000>;
>>>>> +
>>>>> +			firmware-name = "qca/bt_fw_patch.mbn";
>>>>
>>>> Hm, rethinking this, it must have ipq5018 somewhere in the name
>>>
>>> I agree but all QCA BT firmware and rampatches are published in the
>>> /lib/firmware/qca directory, see:
>>> https://git.kernel.org/pub/scm/linux/kernel/git/ath/linux-firmware.git/tree/qca
>>
>> +Jeff, Bartosz?
>>
> 
> I'm not sure what the question is. This particular blob doesn't seem to exist
> anywhere in linux-firmware yet. I agree, ideally the platform name should be in
> the firmware name so let's upstream it to linux-firmware as such? Am I missing
> something?

That's what I'd like to happen - just wanted to make sure you're in the
loop, so that a platform-specific MBN doesn't end up with such a generic
name in case that was the plan.

Konrad

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

* Re: [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support
  2026-07-10  9:23           ` Konrad Dybcio
@ 2026-07-10  9:32             ` George Moussalem
  0 siblings, 0 replies; 21+ messages in thread
From: George Moussalem @ 2026-07-10  9:32 UTC (permalink / raw)
  To: Konrad Dybcio, Bartosz Golaszewski
  Cc: 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, Jeff Johnson

On 7/10/26 13:23, Konrad Dybcio wrote:
> On 7/10/26 11:14 AM, Bartosz Golaszewski wrote:
>> On Thu, 9 Jul 2026 20:40:04 +0200, Konrad Dybcio
>> <konrad.dybcio@oss.qualcomm.com> said:
>>> On 7/9/26 8:25 PM, George Moussalem wrote:
>>>> On 7/9/26 22:17, Konrad Dybcio wrote:
>>>>> On 7/9/26 11:18 AM, George Moussalem via B4 Relay wrote:
>>>>>> From: George Moussalem <george.moussalem@outlook.com>
>>>>>>
>>>>>> Add node to enable Bluetooth support on the IPQ5018 platform.
>>>>>>
>>>>>> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
>>>>>> Signed-off-by: George Moussalem <george.moussalem@outlook.com>
>>>>>> ---
>>>>>>  arch/arm64/boot/dts/qcom/ipq5018.dtsi | 16 ++++++++++++++++
>>>>>>  1 file changed, 16 insertions(+)
>>>>>>
>>>>>> diff --git a/arch/arm64/boot/dts/qcom/ipq5018.dtsi b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>>>> index 6f8004a22a1f..8c252fa3ff5b 100644
>>>>>> --- a/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>>>> +++ b/arch/arm64/boot/dts/qcom/ipq5018.dtsi
>>>>>> @@ -453,6 +453,22 @@ tcsr: syscon@1937000 {
>>>>>>  			reg = <0x01937000 0x21000>;
>>>>>>  		};
>>>>>>
>>>>>> +		bluetooth: bluetooth@7000000 {
>>>>>> +			compatible = "qcom,ipq5018-bt";
>>>>>> +			reg = <0x07000000 0x58000>;
>>>>>> +
>>>>>> +			firmware-name = "qca/bt_fw_patch.mbn";
>>>>>
>>>>> Hm, rethinking this, it must have ipq5018 somewhere in the name
>>>>
>>>> I agree but all QCA BT firmware and rampatches are published in the
>>>> /lib/firmware/qca directory, see:
>>>> https://git.kernel.org/pub/scm/linux/kernel/git/ath/linux-firmware.git/tree/qca
>>>
>>> +Jeff, Bartosz?
>>>
>>
>> I'm not sure what the question is. This particular blob doesn't seem to exist
>> anywhere in linux-firmware yet. I agree, ideally the platform name should be in
>> the firmware name so let's upstream it to linux-firmware as such? Am I missing
>> something?
> 
> That's what I'd like to happen - just wanted to make sure you're in the
> loop, so that a platform-specific MBN doesn't end up with such a generic
> name in case that was the plan.

Perfect, kindly let me know what the name of the MBN that will be
upstreamed to linux-firmware will be and I'll adjust the name in the
next version. The current name was taken as-is from firmware images
distributed to vendors and as available in code.qualcomm.com.

If it will be provided as MBN format, shall I remove support for loading
MDT in the driver or leave as is?

> 
> Konrad

George

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

end of thread, other threads:[~2026-07-10  9:32 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09  9:18 [PATCH v5 0/6] Add support for IPQ5018 Bluetooth George Moussalem via B4 Relay
2026-07-09  9:18 ` [PATCH v5 1/6] dt-bindings: net: bluetooth: Document Qualcomm IPQ5018 Bluetooth controller George Moussalem via B4 Relay
2026-07-09  9:18 ` [PATCH v5 2/6] Bluetooth: btqca: Add IPQ5018 support George Moussalem via B4 Relay
2026-07-09  9:31   ` sashiko-bot
2026-07-09  9:35     ` George Moussalem
2026-07-09  9:18 ` [PATCH v5 3/6] firmware: qcom: scm: Add support for setting Bluetooth power modes George Moussalem via B4 Relay
2026-07-09  9:27   ` sashiko-bot
2026-07-09  9:29     ` George Moussalem
2026-07-09  9:18 ` [PATCH v5 4/6] Bluetooth: Introduce Qualcomm IPQ5018 IPC based HCI driver George Moussalem via B4 Relay
2026-07-09  9:30   ` sashiko-bot
2026-07-09  9:18 ` [PATCH v5 5/6] arm64: dts: qcom: ipq5018: add node required for Bluetooth support George Moussalem via B4 Relay
2026-07-09  9:31   ` sashiko-bot
2026-07-09 18:16   ` Konrad Dybcio
2026-07-09 18:23     ` George Moussalem
2026-07-09 18:17   ` Konrad Dybcio
2026-07-09 18:25     ` George Moussalem
2026-07-09 18:40       ` Konrad Dybcio
2026-07-10  9:14         ` Bartosz Golaszewski
2026-07-10  9:23           ` Konrad Dybcio
2026-07-10  9:32             ` George Moussalem
2026-07-09  9:18 ` [PATCH v5 6/6] MAINTAINERS: Add IPQ5018 driver to Qualcomm Bluetooth driver entry George Moussalem via B4 Relay

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