linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
@ 2026-08-28 14:27 Chandrashekar Devegowda
  2026-08-28 14:27 ` [PATCH v1 2/4] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: Chandrashekar Devegowda @ 2026-08-28 14:27 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan,
	Chandrashekar Devegowda

Two identical definitions of BTINTEL_PCIE_MAGIC_NUM were present in
btintel_pcie.c, one indented with spaces and one with a tab. Remove the
space-indented duplicate and the surrounding blank lines. Kernel coding
style requires tabs for indentation, so keep the tab-indented copy.

No functional change.

Assisted-by: Copilot:claude-opus-4.7 sparse
Fixes: 6ed83047389c ("Bluetooth: btintel_pcie: Setup buffers for firmware traces")
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
 drivers/bluetooth/btintel_pcie.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 30923eaabed7..b90f87e8368d 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -73,11 +73,6 @@ struct btintel_pcie_dev_recovery {
 #define BTINTEL_PCIE_HCI_EVT_PKT	0x00000004
 #define BTINTEL_PCIE_HCI_ISO_PKT	0x00000005
 
-#define BTINTEL_PCIE_MAGIC_NUM    0xA5A5A5A5
-
-
-
-
 #define BTINTEL_PCIE_MAGIC_NUM	0xA5A5A5A5
 
 #define BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER	0x17A2
-- 
2.43.0


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

* [PATCH v1 2/4] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
@ 2026-08-28 14:27 ` Chandrashekar Devegowda
  2026-08-28 14:27 ` [PATCH v1 3/4] Bluetooth: btintel_pcie: log mailbox handler latency in msix_gp1_handler Chandrashekar Devegowda
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Chandrashekar Devegowda @ 2026-08-28 14:27 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan,
	Chandrashekar Devegowda

Newer Intel BT PCIe variants (Nova Lake SCP2 and PTL FMP2) require
three independent DRAM debug (DBGC) buffer pools instead of the single
pool used by the existing controllers.

Add multi-DBGC (MDBGC) support by factoring the per-pool buffer
allocation into a common helper and adding a setup path that allocates
three pools using the new fragment context format. The context
information is programmed with the MDBGC fragment when the device
reports an MDBGC-capable variant, otherwise the existing single-pool
DBGC path is used.

Assisted-by: Copilot:claude-opus-4.7 sparse
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
 drivers/bluetooth/btintel_pcie.c | 178 ++++++++++++++++++++++++++-----
 drivers/bluetooth/btintel_pcie.h |  30 ++++++
 2 files changed, 180 insertions(+), 28 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index b90f87e8368d..0946364a63ba 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -134,6 +134,20 @@ struct btintel_pcie_dbgc_ctxt {
 	struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
 };
 
+struct btintel_pcie_mdbgc_ctxt {
+	u32     magic_num;
+	u32     ver;
+	u32     buf1_index;
+	u32     buf1_count;
+	struct btintel_pcie_dbgc_ctxt_buf buf1[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
+	u32     buf2_index;
+	u32     buf2_count;
+	struct btintel_pcie_dbgc_ctxt_buf buf2[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
+	u32     buf3_index;
+	u32     buf3_count;
+	struct btintel_pcie_dbgc_ctxt_buf buf3[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
+};
+
 struct btintel_pcie_trigger_evt {
 	u8 type;
 	u8 len;
@@ -184,57 +198,159 @@ static inline bool btintel_pcie_dbg_to_wifi(struct btintel_pcie_data *data)
 	return data->dbg_path_cache != BTINTEL_PCIE_DRAM;
 }
 
-/* This function initializes the memory for DBGC buffers and formats the
- * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
- * size as the payload
- */
-static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
+/* Helper function to allocate and setup a debug buffer group */
+static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
+				       struct data_buf **buf,
+				       dma_addr_t *p_addr,
+				       void **v_addr,
+				       struct btintel_pcie_dbgc_ctxt_buf *frag,
+				       u32 buf_index,
+				       u32 buf_count)
 {
-	struct btintel_pcie_dbgc_ctxt db_frag;
-	struct data_buf *buf;
+	struct data_buf *b;
 	int i;
 
-	data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
-	data->dbgc.bufs = devm_kcalloc(&data->pdev->dev, data->dbgc.count,
-				       sizeof(*buf), GFP_KERNEL);
-	if (!data->dbgc.bufs)
+	*buf = devm_kcalloc(&data->pdev->dev, buf_count,
+			    sizeof(**buf), GFP_KERNEL);
+	if (!*buf) {
+		BT_ERR("Failed to allocate dbgc buf: %u",
+		       buf_index + 1);
 		return -ENOMEM;
+	}
 
-	data->dbgc.buf_v_addr = dmam_alloc_coherent(&data->pdev->dev,
-						    data->dbgc.count *
-						    BTINTEL_PCIE_DBGC_BUFFER_SIZE,
-						    &data->dbgc.buf_p_addr,
-						    GFP_KERNEL | __GFP_NOWARN);
-	if (!data->dbgc.buf_v_addr)
+	*v_addr = dmam_alloc_coherent(&data->pdev->dev,
+				      buf_count *
+				      BTINTEL_PCIE_DBGC_BUFFER_SIZE,
+				      p_addr,
+				      GFP_KERNEL | __GFP_NOWARN);
+	if (!*v_addr) {
+		BT_ERR("Failed to allocate dbgc buf: %u DMA",
+		       buf_index + 1);
 		return -ENOMEM;
+	}
+
+	for (i = 0; i < buf_count; i++) {
+		b = &(*buf)[i];
+		b->data_p_addr = *p_addr +
+				 i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		b->data = *v_addr +
+			  i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		frag[i].buf_addr_lsb =
+			lower_32_bits(b->data_p_addr);
+		frag[i].buf_addr_msb =
+			upper_32_bits(b->data_p_addr);
+		frag[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+	}
+
+	return 0;
+}
+
+/* This function initializes the memory for MDBGC buffers */
+static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
+{
+	struct btintel_pcie_mdbgc_ctxt db_frag;
+	u32 frag_size = sizeof(db_frag);
+	void *frag_v_addr;
+	int err;
+
+	data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
+
+	frag_v_addr = dmam_alloc_coherent(&data->pdev->dev, frag_size,
+					  &data->mdbgc.frag_p_addr,
+					  GFP_KERNEL | __GFP_NOWARN);
+	if (!frag_v_addr)
+		return -ENOMEM;
+
+	data->mdbgc.frag_v_addr = frag_v_addr;
+	data->mdbgc.frag_size = frag_size;
+
+	memset(&db_frag, 0, sizeof(db_frag));
+	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
+	db_frag.ver = BTINTEL_PCIE_MDBGC_FRAG_VERSION;
+
+	/* Allocate DBGC buffer 1 */
+	db_frag.buf1_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_1;
+	db_frag.buf1_count = data->mdbgc.count;
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->mdbgc.buf1,
+					  &data->mdbgc.buf1_p_addr,
+					  &data->mdbgc.buf1_v_addr,
+					  db_frag.buf1, 0,
+					  data->mdbgc.count);
+	if (err)
+		return err;
+
+	/* Allocate DBGC buffer 2 */
+	db_frag.buf2_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_2;
+	db_frag.buf2_count = data->mdbgc.count;
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->mdbgc.buf2,
+					  &data->mdbgc.buf2_p_addr,
+					  &data->mdbgc.buf2_v_addr,
+					  db_frag.buf2, 1,
+					  data->mdbgc.count);
+	if (err)
+		return err;
+
+	/* Allocate DBGC buffer 3 */
+	db_frag.buf3_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_3;
+	db_frag.buf3_count = data->mdbgc.count;
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->mdbgc.buf3,
+					  &data->mdbgc.buf3_p_addr,
+					  &data->mdbgc.buf3_v_addr,
+					  db_frag.buf3, 2,
+					  data->mdbgc.count);
+	if (err)
+		return err;
+
+	memcpy(data->mdbgc.frag_v_addr, &db_frag, sizeof(db_frag));
+	return 0;
+}
+
+/* This function initializes the memory for DBGC buffers */
+static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
+{
+	struct btintel_pcie_dbgc_ctxt db_frag;
+	int err;
+
+	data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
 
 	data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
-						     sizeof(struct btintel_pcie_dbgc_ctxt),
-						     &data->dbgc.frag_p_addr,
-						     GFP_KERNEL | __GFP_NOWARN);
+				sizeof(struct btintel_pcie_dbgc_ctxt),
+				&data->dbgc.frag_p_addr,
+				GFP_KERNEL | __GFP_NOWARN);
 	if (!data->dbgc.frag_v_addr)
 		return -ENOMEM;
 
 	data->dbgc.frag_size = sizeof(struct btintel_pcie_dbgc_ctxt);
 
+	memset(&db_frag, 0, sizeof(db_frag));
 	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
 	db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
 	db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
 	db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
 
-	for (i = 0; i < data->dbgc.count; i++) {
-		buf = &data->dbgc.bufs[i];
-		buf->data_p_addr = data->dbgc.buf_p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		buf->data = data->dbgc.buf_v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		db_frag.bufs[i].buf_addr_lsb = lower_32_bits(buf->data_p_addr);
-		db_frag.bufs[i].buf_addr_msb = upper_32_bits(buf->data_p_addr);
-		db_frag.bufs[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-	}
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->dbgc.bufs,
+					  &data->dbgc.buf_p_addr,
+					  &data->dbgc.buf_v_addr,
+					  db_frag.bufs, 0,
+					  data->dbgc.count);
+	if (err)
+		return err;
 
 	memcpy(data->dbgc.frag_v_addr, &db_frag, sizeof(db_frag));
 	return 0;
 }
 
+static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
+{
+	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
+}
+
 static inline void ipc_print_ia_ring(struct hci_dev *hdev, struct ia *ia,
 				     u16 queue_num)
 {
@@ -2247,10 +2363,14 @@ static void btintel_pcie_init_ci(struct btintel_pcie_data *data,
 		 */
 		ci->dbgc_addr = 0;
 		ci->dbgc_size = 0;
+	} else if (btintel_pcie_is_mdbgc_supported(data)) {
+		ci->dbgc_addr = data->mdbgc.frag_p_addr;
+		ci->dbgc_size = data->mdbgc.frag_size;
 	} else {
 		ci->dbgc_addr = data->dbgc.frag_p_addr;
 		ci->dbgc_size = data->dbgc.frag_size;
 	}
+
 	ci->dbg_preset = 0x00;
 }
 
@@ -2483,6 +2603,8 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
 		 * buffer allocation entirely.
 		 */
 		err = 0;
+	} else if (btintel_pcie_is_mdbgc_supported(data)) {
+		err = btintel_pcie_setup_mdbgc(data);
 	} else {
 		err = btintel_pcie_setup_dbgc(data);
 	}
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 9baa214d9bbe..5c35e65d3e81 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -106,8 +106,19 @@
  *   Bits[2:3] DBGI O/P : 01 = WiFi DBGC
  */
 #define BTINTEL_PCIE_DRAM	0x01
+#define BTINTEL_PCIE_FW_MON_MODE_DRAM	0x02
 #define BTINTEL_PCIE_WIFI_DBGC	0x06
 
+#define BTINTEL_PCIE_MDBGC_FRAG_VERSION		2
+
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_1		0
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_2		1
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_3		2
+
+#define BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2	0x6E74
+#define BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2	0xD346
+#define BTINTEL_PCIE_DEVICE_ID_PTL_FMP2		0xE476
+
 /* Causes for the FH register interrupts */
 enum msix_fh_int_causes {
 	BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0	= BIT(0),	/* cause 0 */
@@ -459,6 +470,24 @@ struct btintel_pcie_dbgc {
 	struct data_buf *bufs;
 };
 
+struct btintel_pcie_mdbgc {
+	u32		count;
+
+	void		*frag_v_addr;
+	dma_addr_t	frag_p_addr;
+	u32		frag_size;
+
+	dma_addr_t	buf1_p_addr;
+	void		*buf1_v_addr;
+	dma_addr_t	buf2_p_addr;
+	void		*buf2_v_addr;
+	dma_addr_t	buf3_p_addr;
+	void		*buf3_v_addr;
+	struct data_buf *buf1;
+	struct data_buf *buf2;
+	struct data_buf *buf3;
+};
+
 struct btintel_pcie_dump_mem_info {
 	u32	exception_dump_addr;
 	u32	exception_dump_len;
@@ -599,6 +628,7 @@ struct btintel_pcie_data {
 	u32	alive_intr_ctxt;
 	enum btintel_pcie_reset_type	reset_type;
 	struct btintel_pcie_dbgc	dbgc;
+	struct btintel_pcie_mdbgc	mdbgc;
 	struct btintel_pcie_dump_header dmp_hdr;
 	u8	pm_sx_event;
 	u32	debug_evt_addr;
-- 
2.43.0


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

* [PATCH v1 3/4] Bluetooth: btintel_pcie: log mailbox handler latency in msix_gp1_handler
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
  2026-08-28 14:27 ` [PATCH v1 2/4] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
@ 2026-08-28 14:27 ` Chandrashekar Devegowda
  2026-08-28 14:27 ` [PATCH v1 4/4] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Chandrashekar Devegowda @ 2026-08-28 14:27 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan,
	Chandrashekar Devegowda

Add ktime-based measurement around btintel_pcie_msix_gp1_handler() so
the time taken to service the mailbox interrupt and acknowledge the FW
can be observed via bt_dev_dbg. Drop the informational log emitted on
every mailbox interrupt (it is noisy at bt_dev_info level and the same
timestamp is now available through the new debug print) and collapse a
couple of over-wrapped lines for readability.

No functional change beyond the log verbosity adjustment.

Assisted-by: Copilot:claude-opus-4.7 sparse
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
 drivers/bluetooth/btintel_pcie.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 0946364a63ba..6617ad157ca0 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -1331,6 +1331,10 @@ static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
 {
 	bool target_access = false;
 	u32 addr = 0, size = 0;
+	ktime_t calltime, delta, rettime;
+	unsigned long long duration;
+
+	calltime = ktime_get();
 
 	/* Read the Mail box status and registers */
 	data->mbox.mbox_status = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MBOX_STATUS_REG);
@@ -1338,8 +1342,7 @@ static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
 		data->mbox.mbox1 = btintel_pcie_rd_reg32(data, BTINTEL_PCIE_CSR_MBOX_1_REG);
 		if (data->mbox.mbox1 ==
 		    BTINTEL_PCIE_BUILD_SPECIFIC_RESOURCES_MAPPING) {
-			bt_dev_info(data->hdev,
-				    "mailbox for target access");
+			bt_dev_info(data->hdev, "mailbox for target access");
 			target_access = true;
 		}
 	}
@@ -1373,10 +1376,6 @@ static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
 		set_bit(BTINTEL_PCIE_MBOX_PARSE_PENDING, &data->flags);
 		data->mbox_intr_ts = ktime_get();
 
-		bt_dev_info(data->hdev,
-			    "mbox interrupt received at %lld ns; queuing mbox_work",
-			    ktime_to_ns(data->mbox_intr_ts));
-
 		WRITE_ONCE(data->debug_table_addr, addr);
 		WRITE_ONCE(data->debug_table_size, size);
 		if (!queue_work(data->dump_workqueue,
@@ -1387,9 +1386,13 @@ static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
 	}
 
 	/* Mailbox is read, ack to FW */
-	btintel_pcie_set_reg_bits(data,
-				  BTINTEL_PCIE_CSR_IPC_DOORBELL_VEC_REG,
+	btintel_pcie_set_reg_bits(data, BTINTEL_PCIE_CSR_IPC_DOORBELL_VEC_REG,
 				  BTINTEL_PCIE_CSR_DOORBELL_MBOX_READ_CONFIRM);
+
+	rettime = ktime_get();
+	delta = ktime_sub(rettime, calltime);
+	duration = (unsigned long long)ktime_to_ns(delta) >> 10;
+	bt_dev_dbg(data->hdev, "Mailbox acked in %llu usecs", duration);
 }
 
 /* This function handles the MSI-X interrupt for gp0 cause (bit 0 in
-- 
2.43.0


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

* [PATCH v1 4/4] Bluetooth: btintel_pcie: unified decoder coredump format
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
  2026-08-28 14:27 ` [PATCH v1 2/4] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
  2026-08-28 14:27 ` [PATCH v1 3/4] Bluetooth: btintel_pcie: log mailbox handler latency in msix_gp1_handler Chandrashekar Devegowda
@ 2026-08-28 14:27 ` Chandrashekar Devegowda
  2026-08-28 22:15 ` [v1,1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition bluez.test.bot
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: Chandrashekar Devegowda @ 2026-08-28 14:27 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan,
	Chandrashekar Devegowda

Replace the legacy TLV-based coredump with a unified INI-format dump
that is compatible with the iwlwifi decoder tool, so a single decoder
can parse coredumps across all Intel BT PCIe controller variants.

The dump is emitted through dev_coredumpsg() as a scatter-gather list
of structured regions built from the firmware-provided memory
addresses parsed from the mailbox TLVs:

 - DRAM monitor buffers (single or multi-buffer per MDBGC support)
 - SMEM monitor region
 - Exception event buffer
 - DCCM, SDS, SDS IOSF and ECL memory regions
 - Dump info metadata (FW version, HW variant, trigger reason)

Regions are collected only when the firmware advertises a valid
address and size. Only the regions actually collected have their
corresponding bit set in the dump-info regions_mask, so the decoder
knows which region IDs to expect.

The dump-info metadata is populated from the mailbox-advertised CNVi
and CNVr registers using the following bit-field encoding, matching
the iwlwifi decoder expectations:

  HwType     = CNVi bits 11:0
  HwStep     = CNVi bits 27:24
  RfIdType   = CNVr bits 11:0
  RfIdDash   = CNVr bits 23:20
  RfIdStep   = CNVr bits 27:24
  RfIdFlavor = CNVr bits 31:28

Two new extractor macros INTEL_CNVX_TOP_DASH() and
INTEL_CNVX_TOP_FLAVOR() are added to btintel.h alongside the existing
INTEL_CNVX_TOP_TYPE() and INTEL_CNVX_TOP_STEP() helpers, so the same
shared bit definitions are reused for the HwType/HwStep and RfId*
fields.

Serialization of the dump path relies on existing infrastructure:
the ordered dump_workqueue guarantees that any companion event
reader (fwtrigger_work / hwexp_work) runs before coredump_work so
dmp_hdr trigger metadata is fully populated when dump_info consumes
it, and the BTINTEL_PCIE_COREDUMP_INPROGRESS bit acts as a
single-writer guard against re-entrant triggers. Teardown drains all
three works via disable_work_sync() before freeing the driver data.

The unified format has no consumer for the legacy debug-trigger
event type / event id fields, so drop the now-dead writes to
dmp_hdr.event_type / event_id and remove those two members from
struct btintel_pcie_dump_header. The values are still logged from
the firmware event via bt_dev_dbg for debugging.

Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
 drivers/bluetooth/btintel.h      |   2 +
 drivers/bluetooth/btintel_pcie.c | 837 ++++++++++++++++++++++++-------
 drivers/bluetooth/btintel_pcie.h | 147 +++++-
 3 files changed, 812 insertions(+), 174 deletions(-)

diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index ef232820c31b..c00f793d9dc8 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -225,7 +225,9 @@ struct btintel_sar_rev2 {
 #define INTEL_HW_PLATFORM(cnvx_bt)	((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
 #define INTEL_HW_VARIANT(cnvx_bt)	((u8)(((cnvx_bt) & 0x003f0000) >> 16))
 #define INTEL_CNVX_TOP_TYPE(cnvx_top)	((cnvx_top) & 0x00000fff)
+#define INTEL_CNVX_TOP_DASH(cnvx_top)	(((cnvx_top) & 0x00f00000) >> 20)
 #define INTEL_CNVX_TOP_STEP(cnvx_top)	(((cnvx_top) & 0x0f000000) >> 24)
+#define INTEL_CNVX_TOP_FLAVOR(cnvx_top)	(((cnvx_top) & 0xf0000000) >> 28)
 #define INTEL_CNVX_TOP_PACK_SWAB(t, s)	__swab16(((__u16)(((t) << 4) | (s))))
 
 enum {
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 6617ad157ca0..f38d7c488809 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -19,6 +19,7 @@
 
 #include <linux/unaligned.h>
 #include <linux/devcoredump.h>
+#include <linux/scatterlist.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -198,7 +199,15 @@ static inline bool btintel_pcie_dbg_to_wifi(struct btintel_pcie_data *data)
 	return data->dbg_path_cache != BTINTEL_PCIE_DRAM;
 }
 
-/* Helper function to allocate and setup a debug buffer group */
+/* Helper function to allocate and setup a debug buffer group
+ * @data: driver data structure
+ * @buf: pointer to data_buf array pointer
+ * @p_addr: pointer to physical DMA address
+ * @v_addr: pointer to virtual address
+ * @frag: pointer to fragment buffer array
+ * @buf_index: buffer index (for error messages)
+ * @buf_count: number of buffers to allocate
+ */
 static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
 				       struct data_buf **buf,
 				       dma_addr_t *p_addr,
@@ -213,39 +222,36 @@ static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
 	*buf = devm_kcalloc(&data->pdev->dev, buf_count,
 			    sizeof(**buf), GFP_KERNEL);
 	if (!*buf) {
-		BT_ERR("Failed to allocate dbgc buf: %u",
-		       buf_index + 1);
+		BT_ERR("Failed to allocate dbgc buf: %u", buf_index + 1);
 		return -ENOMEM;
 	}
 
 	*v_addr = dmam_alloc_coherent(&data->pdev->dev,
 				      buf_count *
-				      BTINTEL_PCIE_DBGC_BUFFER_SIZE,
-				      p_addr,
-				      GFP_KERNEL | __GFP_NOWARN);
+					BTINTEL_PCIE_DBGC_BUFFER_SIZE,
+					p_addr,
+					GFP_KERNEL | __GFP_NOWARN);
 	if (!*v_addr) {
-		BT_ERR("Failed to allocate dbgc buf: %u DMA",
-		       buf_index + 1);
+		BT_ERR("Failed to allocate dbgc buf: %u DMA", buf_index + 1);
 		return -ENOMEM;
 	}
 
 	for (i = 0; i < buf_count; i++) {
 		b = &(*buf)[i];
-		b->data_p_addr = *p_addr +
-				 i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		b->data = *v_addr +
-			  i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		frag[i].buf_addr_lsb =
-			lower_32_bits(b->data_p_addr);
-		frag[i].buf_addr_msb =
-			upper_32_bits(b->data_p_addr);
+		b->data_p_addr = *p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		b->data = *v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		frag[i].buf_addr_lsb = lower_32_bits(b->data_p_addr);
+		frag[i].buf_addr_msb = upper_32_bits(b->data_p_addr);
 		frag[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
 	}
 
 	return 0;
 }
 
-/* This function initializes the memory for MDBGC buffers */
+/* This function initializes the memory for MDBGC buffers and formats the
+ * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
+ * size as the payload
+ */
 static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 {
 	struct btintel_pcie_mdbgc_ctxt db_frag;
@@ -255,15 +261,19 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 
 	data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
 
+	/* Allocate fragment context structure */
 	frag_v_addr = dmam_alloc_coherent(&data->pdev->dev, frag_size,
 					  &data->mdbgc.frag_p_addr,
 					  GFP_KERNEL | __GFP_NOWARN);
-	if (!frag_v_addr)
+	if (!frag_v_addr) {
+		BT_ERR("Failed to allocate mdbgc context");
 		return -ENOMEM;
+	}
 
 	data->mdbgc.frag_v_addr = frag_v_addr;
 	data->mdbgc.frag_size = frag_size;
 
+	/* Initialize fragment header */
 	memset(&db_frag, 0, sizeof(db_frag));
 	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
 	db_frag.ver = BTINTEL_PCIE_MDBGC_FRAG_VERSION;
@@ -275,7 +285,8 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 					  &data->mdbgc.buf1,
 					  &data->mdbgc.buf1_p_addr,
 					  &data->mdbgc.buf1_v_addr,
-					  db_frag.buf1, 0,
+					  db_frag.buf1,
+					  0,
 					  data->mdbgc.count);
 	if (err)
 		return err;
@@ -287,7 +298,8 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 					  &data->mdbgc.buf2,
 					  &data->mdbgc.buf2_p_addr,
 					  &data->mdbgc.buf2_v_addr,
-					  db_frag.buf2, 1,
+					  db_frag.buf2,
+					  1,
 					  data->mdbgc.count);
 	if (err)
 		return err;
@@ -299,58 +311,64 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 					  &data->mdbgc.buf3,
 					  &data->mdbgc.buf3_p_addr,
 					  &data->mdbgc.buf3_v_addr,
-					  db_frag.buf3, 2,
+					  db_frag.buf3,
+					  2,
 					  data->mdbgc.count);
 	if (err)
 		return err;
 
+	/* Copy fragment to DMA coherent memory */
 	memcpy(data->mdbgc.frag_v_addr, &db_frag, sizeof(db_frag));
 	return 0;
 }
 
-/* This function initializes the memory for DBGC buffers */
+/* This function initializes the memory for DBGC buffers and formats the
+ * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
+ * size as the payload
+ */
 static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
 {
 	struct btintel_pcie_dbgc_ctxt db_frag;
+	u32 frag_size = sizeof(db_frag);
 	int err;
 
 	data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
 
+	/* Allocate fragment context structure */
 	data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
-				sizeof(struct btintel_pcie_dbgc_ctxt),
-				&data->dbgc.frag_p_addr,
-				GFP_KERNEL | __GFP_NOWARN);
-	if (!data->dbgc.frag_v_addr)
+						     frag_size,
+						     &data->dbgc.frag_p_addr,
+						     GFP_KERNEL | __GFP_NOWARN);
+	if (!data->dbgc.frag_v_addr) {
+		BT_ERR("Failed to allocate dbgc context");
 		return -ENOMEM;
+	}
 
 	data->dbgc.frag_size = sizeof(struct btintel_pcie_dbgc_ctxt);
 
+	/* Initialize fragment header */
 	memset(&db_frag, 0, sizeof(db_frag));
 	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
 	db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
 	db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
 	db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
 
+	/* Allocate DBGC buffers */
 	err = btintel_pcie_alloc_dbgc_buf(data,
 					  &data->dbgc.bufs,
 					  &data->dbgc.buf_p_addr,
 					  &data->dbgc.buf_v_addr,
-					  db_frag.bufs, 0,
+					  db_frag.bufs,
+					  0,
 					  data->dbgc.count);
 	if (err)
 		return err;
 
+	/* Copy fragment to DMA coherent memory */
 	memcpy(data->dbgc.frag_v_addr, &db_frag, sizeof(db_frag));
 	return 0;
 }
 
-static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
-{
-	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
-		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
-		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
-}
-
 static inline void ipc_print_ia_ring(struct hci_dev *hdev, struct ia *ia,
 				     u16 queue_num)
 {
@@ -753,52 +771,461 @@ static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data)
 	}
 }
 
-static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type,
-				   void *data, size_t size)
+static struct scatterlist *btintel_pcie_alloc_sgtable(ssize_t size)
+{
+	int nents, i;
+	struct page *page;
+	struct scatterlist *sg, *result;
+
+	if (size <= 0)
+		return NULL;
+
+	nents = DIV_ROUND_UP(size, PAGE_SIZE);
+	result = kcalloc(nents, sizeof(*result), GFP_KERNEL);
+	if (!result)
+		return NULL;
+
+	sg_init_table(result, nents);
+	sg = result;
+
+	for (i = 0; size > 0; i++) {
+		ssize_t bytes = min_t(ssize_t, size, PAGE_SIZE);
+
+		page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+		if (!page)
+			goto err_free;
+
+		sg_set_page(sg, page, bytes, 0);
+		sg = sg_next(sg);
+		size -= bytes;
+	}
+	return result;
+
+err_free:
+	for (sg = result; sg; sg = sg_next(sg)) {
+		page = sg_page(sg);
+		if (page)
+			__free_page(page);
+	}
+	kfree(result);
+	return NULL;
+}
+
+static struct btintel_pcie_dump_entry *
+btintel_pcie_dump_entry_alloc(u32 data_size)
 {
-	struct intel_tlv *tlv;
+	struct btintel_pcie_dump_entry *entry;
 
-	tlv = dest;
-	tlv->type = type;
-	tlv->len = size;
-	memcpy(tlv->val, data, tlv->len);
-	return dest + sizeof(*tlv) + size;
+	entry = vzalloc(sizeof(*entry) + data_size);
+	if (!entry)
+		return NULL;
+
+	entry->size = data_size;
+	INIT_LIST_HEAD(&entry->list);
+	return entry;
 }
 
-static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
+static void btintel_pcie_dump_list_free(struct list_head *list)
 {
-	u32 offset, prev_size, wr_ptr_status, dump_size, data_len;
-	u32 status_reg, wrap_reg;
-	struct btintel_pcie_dbgc *dbgc = &data->dbgc;
+	struct btintel_pcie_dump_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, list, list) {
+		list_del(&entry->list);
+		vfree(entry);
+	}
+}
+
+static u32 btintel_pcie_dump_list_total_size(struct list_head *list)
+{
+	struct btintel_pcie_dump_entry *entry;
+	u32 total = 0;
+
+	list_for_each_entry(entry, list, list)
+		total += entry->size;
+
+	return total;
+}
+
+static int btintel_pcie_dump_dram(struct list_head *list,
+				  u8 count, struct data_buf *bufs,
+				  u8 buf_idx, u32 write_ptr, u32 wrap_ctr,
+				  u32 region_id, const char *name)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_ini_dump_data *dump_data;
+	struct btintel_pcie_ini_monitor_dump *mon_dump;
+	struct btintel_pcie_dump_range *range;
+	u32 mon_hdr_size, ranges_size, payload_size, total_size;
+	int i;
+
+	mon_hdr_size = sizeof(*mon_dump);
+	ranges_size = count * (sizeof(*range) + BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+	payload_size = mon_hdr_size + ranges_size;
+	total_size = sizeof(*dump_data) + payload_size;
+
+	entry = btintel_pcie_dump_entry_alloc(total_size);
+	if (!entry)
+		return -ENOMEM;
+
+	dump_data = (void *)entry->data;
+	dump_data->type = BTINTEL_PCIE_INI_REGION_DRAM_BUFFER;
+	dump_data->sub_type = 0;
+	dump_data->sub_type_ver = 0;
+	dump_data->reserved = 0;
+	dump_data->len = cpu_to_le32(payload_size);
+
+	mon_dump = (void *)dump_data->data;
+	mon_dump->header.version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	mon_dump->header.region_id = cpu_to_le32(region_id);
+	mon_dump->header.num_of_ranges = cpu_to_le32(count);
+	mon_dump->header.name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
+	memset(mon_dump->header.name, 0, BTINTEL_PCIE_INI_MAX_NAME);
+	strscpy(mon_dump->header.name, name, BTINTEL_PCIE_INI_MAX_NAME);
+
+	mon_dump->write_ptr = cpu_to_le32(write_ptr);
+	mon_dump->cycle_cnt = cpu_to_le32(wrap_ctr);
+	mon_dump->cur_frag = cpu_to_le32(buf_idx);
+
+	range = (void *)mon_dump->data;
+	for (i = 0; i < count; i++) {
+		range->range_data_size =
+			cpu_to_le32(BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+		range->dram_base_addr = cpu_to_le64(bufs[i].data_p_addr);
+		memcpy(range->data, bufs[i].data,
+		       BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+		range = (void *)range->data + BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+	}
+
+	list_add_tail(&entry->list, list);
+	return 0;
+}
+
+static int btintel_pcie_dump_target_region(struct btintel_pcie_data *data,
+					   struct list_head *list,
+					   u32 region_id, const char *name,
+					   u32 addr_start, u32 size)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_ini_dump_data *dump_data;
+	struct btintel_pcie_ini_dump_header *hdr;
+	struct btintel_pcie_dump_range *range;
+	u32 payload_size, total_size, target_mem_offset, tempdata;
+	u8 *dest;
+	int i;
+
+	if (!size) {
+		bt_dev_warn(data->hdev, "Skipping empty dump region: %s", name);
+		return 0;
+	}
+
+	if (!addr_start) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region with zero address: %s",
+			    name);
+		return 0;
+	}
+
+	if (size > BTINTEL_PCIE_REGION_MAX_SIZE) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: size %u exceeds max %u",
+			    name, size, BTINTEL_PCIE_REGION_MAX_SIZE);
+		return 0;
+	}
+
+	if (addr_start > U32_MAX - size) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: addr_start 0x%08x + size %u would overflow",
+			    name, addr_start, size);
+		return 0;
+	}
+
+	/* Align to 4 bytes - target access reads 32-bit words */
+	size = round_down(size, sizeof(u32));
+
+	bt_dev_dbg(data->hdev,
+		   "Target access: region=%s start=0x%08x size=%u",
+		   name, addr_start, size);
+
+	payload_size = sizeof(*hdr) + sizeof(*range) + size;
+	total_size = sizeof(*dump_data) + payload_size;
+	entry = btintel_pcie_dump_entry_alloc(total_size);
+	if (!entry)
+		return -ENOMEM;
+
+	dump_data = (void *)entry->data;
+	dump_data->type = BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY;
+	dump_data->sub_type = 0;
+	dump_data->sub_type_ver = 0;
+	dump_data->reserved = 0;
+	dump_data->len = cpu_to_le32(payload_size);
+
+	hdr = (void *)dump_data->data;
+	hdr->version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	hdr->region_id = cpu_to_le32(region_id);
+	hdr->num_of_ranges = cpu_to_le32(1);
+	hdr->name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
+	memset(hdr->name, 0, BTINTEL_PCIE_INI_MAX_NAME);
+	strscpy(hdr->name, name, BTINTEL_PCIE_INI_MAX_NAME);
+
+	range = (void *)(hdr + 1);
+	range->range_data_size = cpu_to_le32(size);
+	range->internal_base_addr = cpu_to_le32(addr_start);
+
+	dest = (u8 *)range->data;
+	target_mem_offset = size / sizeof(u32);
+	for (i = 0; i < target_mem_offset; i++) {
+		u32 offset = BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET * i;
+
+		tempdata = btintel_pcie_rd_dev_mem(data,
+						   addr_start + offset);
+		memcpy(dest, &tempdata, sizeof(tempdata));
+		dest += sizeof(tempdata);
+	}
+
+	list_add_tail(&entry->list, list);
+	return 0;
+}
+
+static int btintel_pcie_dump_smem_monitor_region(struct btintel_pcie_data *data,
+						 struct list_head *list,
+						 u32 region_id,
+						 const char *name,
+						 u32 addr_start, u32 size)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_ini_dump_data *dump_data;
+	struct btintel_pcie_ini_monitor_dump *mon;
+	struct btintel_pcie_dump_range *range;
+	u32 payload_size, total_size, target_mem_offset, tempdata;
+	u8 *dest;
+	int i;
+
+	if (!size || !addr_start) {
+		bt_dev_err(data->hdev, "Skipping smem dump: size = %u addr = %8.8x",
+			   size, addr_start);
+		return -EINVAL;
+	}
+
+	payload_size = sizeof(*mon) + sizeof(*range) + size;
+	total_size = sizeof(*dump_data) + payload_size;
+	entry = btintel_pcie_dump_entry_alloc(total_size);
+	if (!entry)
+		return -ENOMEM;
+
+	dump_data = (void *)entry->data;
+	dump_data->type = BTINTEL_PCIE_INI_REGION_INTERNAL_BUFFER;
+	dump_data->sub_type = 0;
+	dump_data->sub_type_ver = 0;
+	dump_data->reserved = 0;
+	dump_data->len = cpu_to_le32(payload_size);
+
+	mon = (void *)dump_data->data;
+	mon->header.version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	mon->header.region_id = cpu_to_le32(region_id);
+	mon->header.num_of_ranges = cpu_to_le32(1);
+	mon->header.name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
+	memset(mon->header.name, 0, BTINTEL_PCIE_INI_MAX_NAME);
+	strscpy(mon->header.name, name, BTINTEL_PCIE_INI_MAX_NAME);
+
+	mon->write_ptr = cpu_to_le32(0);
+	mon->cycle_cnt = cpu_to_le32(0);
+	mon->cur_frag = cpu_to_le32(0);
+
+	range = (void *)mon->data;
+	range->range_data_size = cpu_to_le32(size);
+	range->internal_base_addr = cpu_to_le32(addr_start);
+
+	dest = (u8 *)range->data;
+	target_mem_offset = size / sizeof(u32);
+	for (i = 0; i < target_mem_offset; i++) {
+		u32 offset = BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET * i;
+
+		tempdata = btintel_pcie_rd_dev_mem(data,
+						   addr_start + offset);
+		memcpy(dest, &tempdata, sizeof(tempdata));
+		dest += sizeof(tempdata);
+	}
+
+	list_add_tail(&entry->list, list);
+	return 0;
+}
+
+static int btintel_pcie_dump_info(struct btintel_pcie_data *data,
+				  struct list_head *list,
+				  u64 regions_mask)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_error_dump_data *tlv;
+	struct btintel_pcie_ini_dump_info *dump;
+	u32 size = sizeof(*tlv) + sizeof(*dump);
+	char build_tag[64];
+
+	entry = btintel_pcie_dump_entry_alloc(size);
+	if (!entry)
+		return -ENOMEM;
+
+	tlv = (void *)entry->data;
+	tlv->type = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_INFO_TYPE);
+	tlv->len = cpu_to_le32(sizeof(*dump));
+
+	dump = (void *)tlv->data;
+	memset(dump, 0, sizeof(*dump));
+
+	dump->version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	dump->trigger_reason = cpu_to_le32(data->dmp_hdr.trigger_reason);
+
+	if (data->dmp_hdr.trigger_reason ==
+	    BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT)
+		dump->time_point =
+			cpu_to_le32(BTINTEL_PCIE_TIME_POINT_FW_ASSERT);
+	else if (data->dmp_hdr.trigger_reason ==
+		 BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER)
+		dump->time_point =
+			cpu_to_le32(BTINTEL_PCIE_TIME_POINT_USER_TRIGGER);
+
+	dump->hw_type = cpu_to_le32(INTEL_CNVX_TOP_TYPE(data->dmp_hdr.cnvi_top));
+	dump->hw_step = cpu_to_le32(INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvi_top));
+	bt_dev_dbg(data->hdev, "hw_type=0x%x hw_step=0x%x (cnvi_top=0x%x)",
+		   le32_to_cpu(dump->hw_type), le32_to_cpu(dump->hw_step),
+		   data->dmp_hdr.cnvi_top);
+
+	dump->ver_type = cpu_to_le32(data->dmp_hdr.cnvi_bt);
+	dump->ver_subtype = cpu_to_le32(data->dmp_hdr.fw_sha);
+	dump->rf_id_type = cpu_to_le32(INTEL_CNVX_TOP_TYPE(data->dmp_hdr.cnvr_top));
+	dump->rf_id_dash = cpu_to_le32(INTEL_CNVX_TOP_DASH(data->dmp_hdr.cnvr_top));
+	dump->rf_id_step = cpu_to_le32(INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvr_top));
+	dump->rf_id_flavor = cpu_to_le32(INTEL_CNVX_TOP_FLAVOR(data->dmp_hdr.cnvr_top));
+	bt_dev_dbg(data->hdev,
+		   "rf_id_type=0x%x rf_id_dash=0x%x rf_id_step=0x%x rf_id_flavor=0x%x (cnvr_top=0x%x)",
+		   le32_to_cpu(dump->rf_id_type),
+		   le32_to_cpu(dump->rf_id_dash),
+		   le32_to_cpu(dump->rf_id_step),
+		   le32_to_cpu(dump->rf_id_flavor),
+		   data->dmp_hdr.cnvr_top);
+	dump->lmac_major = cpu_to_le32(0);
+	dump->lmac_minor = cpu_to_le32(0);
+	dump->umac_major = cpu_to_le32(0);
+	dump->umac_minor = cpu_to_le32(0);
+	dump->fw_mon_mode = cpu_to_le32(BTINTEL_PCIE_FW_MON_MODE_DRAM);
+
+	dump->regions_mask = cpu_to_le64(regions_mask);
+
+	bt_dev_dbg(data->hdev, "ExpectedRegionIDs regions_mask=0x%016llx",
+		   le64_to_cpu(dump->regions_mask));
+
+	snprintf(build_tag, sizeof(build_tag), "%08X", data->dmp_hdr.fw_sha);
+
+	dump->build_tag_len = cpu_to_le32(strlen(build_tag));
+	memcpy(dump->build_tag, build_tag, min(strlen(build_tag),
+					       sizeof(dump->build_tag)));
+
+	dump->num_of_cfg_names = cpu_to_le32(0);
+
+	bt_dev_dbg(data->hdev,
+		   "HwType=0x%08x HwStep=0x%08x RfIdType=0x%08x RfIdDash=0x%08x RfIdStep=0x%08x RfIdFlavor=0x%08x",
+		   le32_to_cpu(dump->hw_type), le32_to_cpu(dump->hw_step),
+		   le32_to_cpu(dump->rf_id_type),
+		   le32_to_cpu(dump->rf_id_dash),
+		   le32_to_cpu(dump->rf_id_step),
+		   le32_to_cpu(dump->rf_id_flavor));
+	bt_dev_dbg(data->hdev, "VerType=0x%08x VerSubType=0x%08x",
+		   le32_to_cpu(dump->ver_type), le32_to_cpu(dump->ver_subtype));
+	bt_dev_dbg(data->hdev,
+		   "LmacMajor=0x%08x LmacMinor=0x%08x UmacMajor=0x%08x UmacMinor=0x%08x",
+		   le32_to_cpu(dump->lmac_major), le32_to_cpu(dump->lmac_minor),
+		   le32_to_cpu(dump->umac_major),
+		   le32_to_cpu(dump->umac_minor));
+	bt_dev_dbg(data->hdev, "TriggerReason=0x%04x MonMode=%u BuildTag=%.64s",
+		   le32_to_cpu(dump->trigger_reason),
+		   le32_to_cpu(dump->fw_mon_mode),
+		   dump->build_tag);
+
+	list_add(&entry->list, list);
+
+	return 0;
+}
+
+static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
+{
+	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
+}
+
+static void btintel_pcie_dump_mem_range(struct btintel_pcie_data *data,
+					struct list_head *list, u32 region_id,
+					const char *name, u32 addr_start,
+					u32 addr_end, u64 *regions_mask)
+{
+	u32 region_size;
+
+	if (!addr_start || !addr_end || addr_end < addr_start)
+		return;
+
+	if (addr_end > U32_MAX - 0x04) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: addr_end 0x%08x would overflow",
+			    name, addr_end);
+		return;
+	}
+
+	region_size = (addr_end + 0x04) - addr_start;
+	if (region_size > BTINTEL_PCIE_REGION_MAX_SIZE) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: size %u exceeds max %u",
+			    name, region_size,
+			    BTINTEL_PCIE_REGION_MAX_SIZE);
+		return;
+	}
+
+	if (!btintel_pcie_dump_target_region(data, list, region_id, name,
+					     addr_start, region_size))
+		*regions_mask |= BIT_ULL(region_id);
+}
+
+static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
+{
+	struct btintel_pcie_dbgc *dbgc = NULL;
+	struct btintel_pcie_mdbgc *mdbgc = NULL;
 	struct hci_dev *hdev = data->hdev;
-	u8 *pdata, *p, buf_idx, hw_variant;
-	struct intel_tlv *tlv;
-	struct timespec64 now;
-	struct tm tm_now;
-	char fw_build[128];
-	char ts[128];
-	char vendor[64];
-	char driver[64];
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_dump_file_hdr *file_hdr;
+	struct scatterlist *sg_dump_data;
+	u32 offset, prev_size, wr_ptr_status;
+	u32 status_reg, wrap_reg;
+	u32 exception_dump_len;
+	u32 exc_addr;
+	u64 regions_mask = 0;
+	u8 buf_idx, hw_variant;
+	u32 smem_rd_addr = 0, smem_rd_size = 0;
+	u32 file_len;
+	u8 count;
+	int ret;
+	LIST_HEAD(dump_list);
 
 	if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
 		return -EOPNOTSUPP;
 
+	if (btintel_pcie_is_mdbgc_supported(data)) {
+		mdbgc = &data->mdbgc;
+		count = mdbgc->count;
+	} else {
+		dbgc = &data->dbgc;
+		count = dbgc->count;
+	}
 
 	hw_variant = INTEL_HW_VARIANT(data->cnvi);
-	switch (hw_variant) {
-	case BTINTEL_HWID_BZRI:
-	case BTINTEL_HWID_BZRIW:
+
+	if (hw_variant == BTINTEL_HWID_BZRI ||
+	    hw_variant == BTINTEL_HWID_BZRIW) {
 		status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS;
 		wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND;
-		break;
-	case BTINTEL_HWID_SCP:
-	case BTINTEL_HWID_SCP2:
-	case BTINTEL_HWID_SCP2F:
+	} else if (hw_variant >= BTINTEL_HWID_SCP) {
 		status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP;
 		wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP;
-		break;
-	default:
-		bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%2.2x)",
+	} else {
+		bt_dev_err(hdev,
+			   "Unsupported Intel hardware variant (0x%2.2x)",
 			   hw_variant);
 		return -EINVAL;
 	}
@@ -809,7 +1236,7 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
 	offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
 
 	buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
-	if (buf_idx > dbgc->count) {
+	if (buf_idx > count) {
 		bt_dev_warn(hdev, "Buffer index is invalid");
 		return -EINVAL;
 	}
@@ -820,102 +1247,175 @@ static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
 	else
 		return -EINVAL;
 
-	strscpy(vendor, "Vendor: Intel\n");
-	snprintf(driver, sizeof(driver), "Driver: %s\n",
-		 data->dmp_hdr.driver_name);
-
-	ktime_get_real_ts64(&now);
-	time64_to_tm(now.tv_sec, 0, &tm_now);
-	snprintf(ts, sizeof(ts), "Dump Time: %02d-%02d-%04ld %02d:%02d:%02d",
-				 tm_now.tm_mday, tm_now.tm_mon + 1, tm_now.tm_year + 1900,
-				 tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
-
-	snprintf(fw_build, sizeof(fw_build),
-			    "Firmware Timestamp: Year %u WW %02u buildtype %u build %u",
-			    2000 + (data->dmp_hdr.fw_timestamp >> 8),
-			    data->dmp_hdr.fw_timestamp & 0xff, data->dmp_hdr.fw_build_type,
-			    data->dmp_hdr.fw_build_num);
-
-	data_len = sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_bt) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.write_ptr) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.wrap_ctr) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.trigger_reason) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.fw_sha) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.cnvr_top) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_top) +
-		sizeof(*tlv) + strlen(ts) +
-		sizeof(*tlv) + strlen(fw_build) +
-		sizeof(*tlv) + strlen(vendor) +
-		sizeof(*tlv) + strlen(driver);
-
-	if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
-		data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_type);
-		data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_id);
-	}
+	bt_dev_dbg(hdev, "wr_ptr_status=0x%08x buf_idx=%u offset=0x%06x",
+		   wr_ptr_status, buf_idx, offset);
+	bt_dev_dbg(hdev, "write_ptr=0x%08x wrap_ctr=0x%08x",
+		   data->dmp_hdr.write_ptr, data->dmp_hdr.wrap_ctr);
 
-	/*
-	 * sizeof(u32) - signature
-	 * sizeof(data_len) - to store tlv data size
-	 * data_len - TLV data
-	 */
-	dump_size = sizeof(u32) + sizeof(data_len) + data_len;
+	smem_rd_addr = data->dump_info.smem_addr_start;
+	smem_rd_size = 0;
 
+	if (data->dump_info.smem_addr_end < smem_rd_addr ||
+	    data->dump_info.smem_addr_end > U32_MAX - 0x04) {
+		bt_dev_err(hdev,
+			   "Invalid smem region: start=0x%08x end=0x%08x",
+			   smem_rd_addr, data->dump_info.smem_addr_end);
+	} else {
+		smem_rd_size = (data->dump_info.smem_addr_end + 0x04) -
+			       smem_rd_addr;
 
-	/* Add debug buffers data length to dump size */
-	dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
+		bt_dev_dbg(hdev,
+			   "smem_region: smem_start_addr=0x%08x smem_end_addr=0x%08x smem_rd_size=%u",
+			   smem_rd_addr, data->dump_info.smem_addr_end,
+			   smem_rd_size);
+
+		if (smem_rd_size == 0 ||
+		    smem_rd_size > BTINTEL_PCIE_SMEM_MAX_SIZE) {
+			bt_dev_err(hdev,
+				   "Invalid smem region: smem_rd_addr 0x%08x size %u (max %u)",
+				   smem_rd_addr, smem_rd_size,
+				   BTINTEL_PCIE_SMEM_MAX_SIZE);
+			smem_rd_size = 0;
+		}
+	}
 
-	pdata = vmalloc(dump_size);
-	if (!pdata)
+	if (btintel_pcie_is_mdbgc_supported(data)) {
+		ret = btintel_pcie_dump_dram(&dump_list, count,
+					     mdbgc->buf1, 0,
+					     data->dmp_hdr.write_ptr,
+					     data->dmp_hdr.wrap_ctr,
+					     BTINTEL_PCIE_INI_ID_DRAM_MONITOR1,
+					     "monitor");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR1);
+		else
+			bt_dev_warn(hdev, "Failed to dump DRAM buf1: %d", ret);
+
+		ret = btintel_pcie_dump_dram(&dump_list, count,
+					     mdbgc->buf2, 0,
+					     data->dmp_hdr.write_ptr,
+					     data->dmp_hdr.wrap_ctr,
+					     BTINTEL_PCIE_INI_ID_DRAM_MONITOR2,
+					     "monitor2");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR2);
+		else
+			bt_dev_warn(hdev, "Failed to dump DRAM buf2: %d", ret);
+
+		ret = btintel_pcie_dump_dram(&dump_list, count,
+					     mdbgc->buf3, 0,
+					     data->dmp_hdr.write_ptr,
+					     data->dmp_hdr.wrap_ctr,
+					     BTINTEL_PCIE_INI_ID_DRAM_MONITOR3,
+					     "monitor3");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR3);
+		else
+			bt_dev_warn(hdev, "Failed to dump DRAM buf3: %d", ret);
+	} else {
+		ret = btintel_pcie_dump_dram(&dump_list, count,
+					     dbgc->bufs, 0,
+					     data->dmp_hdr.write_ptr,
+					     data->dmp_hdr.wrap_ctr,
+					     BTINTEL_PCIE_INI_ID_DRAM_MONITOR1,
+					     "monitor");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR1);
+		else
+			bt_dev_warn(hdev,
+				    "Failed to dump DRAM region: %d", ret);
+	}
+
+	if (smem_rd_size &&
+	    !btintel_pcie_dump_smem_monitor_region(data, &dump_list,
+						   BTINTEL_PCIE_INI_ID_SMEM,
+						   "monitor_smem",
+						   smem_rd_addr,
+						   smem_rd_size))
+		regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SMEM);
+
+	exc_addr = data->dump_info.exception_dump_addr;
+	exception_dump_len = data->dump_info.exception_dump_len;
+	ret = btintel_pcie_dump_target_region(data, &dump_list,
+					      BTINTEL_PCIE_INI_ID_EXCEPTION_EVT,
+					      "EXCEPTION_EVT_BUFFER", exc_addr,
+					      exception_dump_len);
+	if (!ret && exception_dump_len)
+		regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_EXCEPTION_EVT);
+
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_DCCM, "DCCM",
+				    data->dump_info.dccm_addr_start,
+				    data->dump_info.dccm_addr_end,
+				    &regions_mask);
+
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_SDS, "SDS",
+				    data->dump_info.sds_start_addr_start,
+				    data->dump_info.sds_start_addr_end,
+				    &regions_mask);
+
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_SDS_IOSF, "SDS_IOSF",
+				    data->dump_info.sds_iosf_data_addr_start,
+				    data->dump_info.sds_iosf_data_addr_end,
+				    &regions_mask);
+
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_ECL, "ECL_REGION",
+				    data->dump_info.ecl_addr_start,
+				    data->dump_info.ecl_addr_end,
+				    &regions_mask);
+
+	ret = btintel_pcie_dump_info(data, &dump_list, regions_mask);
+	if (ret) {
+		btintel_pcie_dump_list_free(&dump_list);
+		return ret;
+	}
+
+	file_len = sizeof(*file_hdr) +
+		   btintel_pcie_dump_list_total_size(&dump_list);
+	entry = btintel_pcie_dump_entry_alloc(sizeof(*file_hdr));
+	if (!entry) {
+		btintel_pcie_dump_list_free(&dump_list);
 		return -ENOMEM;
-	p = pdata;
-
-	*(u32 *)p = BTINTEL_PCIE_MAGIC_NUM;
-	p += sizeof(u32);
-
-	*(u32 *)p = data_len;
-	p += sizeof(u32);
-
-
-	p = btintel_pcie_copy_tlv(p, BTINTEL_VENDOR, vendor, strlen(vendor));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_DRIVER, driver, strlen(driver));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_DUMP_TIME, ts, strlen(ts));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_FW_BUILD, fw_build,
-				  strlen(fw_build));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_BT, &data->dmp_hdr.cnvi_bt,
-				  sizeof(data->dmp_hdr.cnvi_bt));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_WRITE_PTR, &data->dmp_hdr.write_ptr,
-				  sizeof(data->dmp_hdr.write_ptr));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
-				  sizeof(data->dmp_hdr.wrap_ctr));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
-				  sizeof(data->dmp_hdr.trigger_reason));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_sha,
-				  sizeof(data->dmp_hdr.fw_sha));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVR_TOP, &data->dmp_hdr.cnvr_top,
-				  sizeof(data->dmp_hdr.cnvr_top));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
-				  sizeof(data->dmp_hdr.cnvi_top));
-
-	if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
-		p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_TYPE,
-					  &data->dmp_hdr.event_type,
-					  sizeof(data->dmp_hdr.event_type));
-		p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_ID,
-					  &data->dmp_hdr.event_id,
-					  sizeof(data->dmp_hdr.event_id));
-		data->dmp_hdr.event_type = 0;
-		data->dmp_hdr.event_id = 0;
-	}
-
-	memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
-	dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
-	return 0;
+	}
+
+	file_hdr = (void *)entry->data;
+	file_hdr->barker = cpu_to_le32(BTINTEL_PCIE_INI_ERROR_DUMP_BARKER);
+	file_hdr->file_len = cpu_to_le32(file_len);
+	list_add(&entry->list, &dump_list);
+
+	sg_dump_data = btintel_pcie_alloc_sgtable(file_len);
+	if (sg_dump_data) {
+		int sg_entries = sg_nents(sg_dump_data);
+		u32 offs = 0;
+
+		list_for_each_entry(entry, &dump_list, list) {
+			sg_pcopy_from_buffer(sg_dump_data, sg_entries,
+					     entry->data, entry->size, offs);
+			offs += entry->size;
+		}
+
+		bt_dev_dbg(hdev, "triggering dev_coredumpsg()");
+		dev_coredumpsg(&hdev->dev, sg_dump_data, file_len, GFP_KERNEL);
+	} else {
+		bt_dev_err(hdev, "Failed to allocate scatter-gather table for coredump");
+		ret = -ENOMEM;
+	}
+
+	btintel_pcie_dump_list_free(&dump_list);
+	return ret;
 }
 
 static void btintel_pcie_dump_traces(struct hci_dev *hdev)
 {
 	struct btintel_pcie_data *data = hci_get_drvdata(hdev);
-	int ret = 0;
+	int ret;
 
 	ret = btintel_pcie_get_mac_access(data);
 	if (ret) {
@@ -923,7 +1423,7 @@ static void btintel_pcie_dump_traces(struct hci_dev *hdev)
 		return;
 	}
 
-	ret = btintel_pcie_read_dram_buffers(data);
+	ret = btintel_pcie_read_debug_regions(data);
 
 	btintel_pcie_release_mac_access(data);
 
@@ -1168,19 +1668,19 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 	ptr = buffer;
 	remaining = buffer_len;
 
-	/* Parse TLV structures: type(1) + length(2) + value */
+	/* Parse TLV structures: 1 byte type + 2 bytes length +
+	 * variable value
+	 */
 	while (remaining >= sizeof(struct mbox_tlv)) {
 		u16 tlv_len;
 		u32 tlv_total;
 
 		tlv = (struct mbox_tlv *)ptr;
 		tlv_len = le16_to_cpu(tlv->len);
-		tlv_total = sizeof(tlv->type) +
-			    sizeof(tlv->len) + tlv_len;
+		tlv_total = sizeof(tlv->type) + sizeof(tlv->len) + tlv_len;
 
 		if (tlv_total > remaining) {
-			bt_dev_err(data->hdev,
-				   "TLV parse error: type=%u, len=%u",
+			bt_dev_err(data->hdev, "TLV parse error: not enough data for TLV value (type=%u, len=%u)",
 				   tlv->type, tlv_len);
 			break;
 		}
@@ -1189,7 +1689,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_EXCEPTION_DUMP_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1202,7 +1702,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1221,8 +1721,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 				bt_dev_dbg(data->hdev, "SDS TLV: skipped, hw_variant not yet known");
 				break;
 			}
-			if (tlv_len == 16 &&
-			    hw_variant > BTINTEL_HWID_BZRI) {
+			if (tlv_len == 16 && hw_variant > BTINTEL_HWID_BZRI) {
 				data->dump_info.sds_start_addr_start =
 					get_unaligned_le32(&tlv->val[0]);
 				data->dump_info.sds_start_addr_end =
@@ -1255,7 +1754,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1268,7 +1767,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1279,8 +1778,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 				get_unaligned_le32(&tlv->val[4]);
 			break;
 		default:
-			bt_dev_dbg(data->hdev,
-				   "Unknown TLV type: %u length: %u",
+			bt_dev_dbg(data->hdev, "Unknown TLV type: %u length: %u",
 				   tlv->type, tlv_len);
 			break;
 		}
@@ -1871,11 +2369,8 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
 		goto exit_on_error;
 
 	evt = (void *)buf;
-	data->dmp_hdr.event_type = evt->event_type;
-	data->dmp_hdr.event_id = le16_to_cpu(evt->event_id);
-
 	bt_dev_dbg(data->hdev, "event type: 0x%2.2x event id: 0x%4.4x len: %u",
-		   data->dmp_hdr.event_type, data->dmp_hdr.event_id, len);
+		   evt->event_type, le16_to_cpu(evt->event_id), len);
 
 	skb = bt_skb_alloc(len, GFP_KERNEL);
 	if (!skb) {
@@ -1901,8 +2396,8 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
  *
  * Always queue this AFTER any companion event-reader work (hwexp /
  * fwtrigger) so that, on the ordered @dump_workqueue, the event reader
- * runs first and populates dmp_hdr.event_type / event_id before
- * dump_traces consumes them.
+ * runs first and the trigger metadata is populated before dump_traces
+ * consumes it.
  */
 static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data,
 					u16 trigger_reason)
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 5c35e65d3e81..7feb13f7e7d0 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -75,10 +75,14 @@
 #define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP	(BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x1C)
 #define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP		(BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x2C)
 
+#define BTINTEL_PCIE_SMEM_MAX_SIZE		(16 * 1024)
+#define BTINTEL_PCIE_REGION_MAX_SIZE		(16 * 1024 * 1024)
 #define BTINTEL_PCIE_DBG_IDX_BIT_MASK		0x0F
 #define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data)	(((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK)
 #define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK	0xFFFFFF
 
+#define BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET	4
+
 /* The DRAM buffer count, each buffer size, and
  * fragment buffer size
  */
@@ -242,7 +246,7 @@ enum {
 #define BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS        0x05
 #define BTINTEL_PCIE_TLV_TYPE_SDS_MEM_ADDRESS         0x06
 #define BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS         0x07
-#define BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS           0x08
+#define BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS	      0x08
 
 /*
  * Struct for Context Information (v2)
@@ -527,10 +531,147 @@ struct btintel_pcie_dump_header {
 	u32		wrap_ctr;
 	u16		trigger_reason;
 	int		state;
-	u8		event_type;
-	u16		event_id;
 };
 
+/* Per-fragment range descriptor for dump regions.
+ * Binary-compatible with iwl_fw_ini_error_dump_range.
+ */
+struct btintel_pcie_dump_range {
+	__le32	range_data_size;
+	union {
+		__le32	internal_base_addr;
+		__le64	dram_base_addr;
+		__le32	page_num;
+	};
+	__le32	data[];
+} __packed;
+
+/*
+ * INI region types for ini_dump_data.type field.
+ * The unified decoder dispatches parsing logic based on these values.
+ */
+#define BTINTEL_PCIE_INI_REGION_INTERNAL_BUFFER	2
+#define BTINTEL_PCIE_INI_REGION_DRAM_BUFFER	3
+#define BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY	9
+
+/* INI region IDs - used in dump header region_id field and regions_mask */
+#define BTINTEL_PCIE_INI_ID_EXCEPTION_EVT	7
+#define BTINTEL_PCIE_INI_ID_SMEM		15
+#define BTINTEL_PCIE_INI_ID_DCCM		39
+#define BTINTEL_PCIE_INI_ID_SDS			40
+#define BTINTEL_PCIE_INI_ID_SDS_IOSF		41
+#define BTINTEL_PCIE_INI_ID_ECL			42
+#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR3	61
+#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR2	62
+#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR1	63
+
+/*
+ * INI (Intel INI debug infrastructure) style dump data TLV - wraps each dump
+ * region. INI is the iwlwifi firmware debug format used by the unified decoder.
+ * Compatible with iwl_fw_ini_error_dump_data.
+ */
+struct btintel_pcie_ini_dump_data {
+	u8	type;
+	u8	sub_type;
+	u8	sub_type_ver;
+	u8	reserved;
+	__le32	len;
+	u8	data[];
+} __packed;
+
+/*
+ * INI-style region dump header.
+ * Compatible with iwl_fw_ini_error_dump_header.
+ */
+#define BTINTEL_PCIE_INI_MAX_NAME	32
+#define BTINTEL_PCIE_INI_DUMP_VER	1
+
+struct btintel_pcie_ini_dump_header {
+	__le32	version;
+	__le32	region_id;
+	__le32	num_of_ranges;
+	__le32	name_len;
+	u8	name[BTINTEL_PCIE_INI_MAX_NAME];
+};
+
+/*
+ * INI-style monitor dump - region header + monitor state.
+ * Compatible with iwl_fw_ini_monitor_dump.
+ */
+struct btintel_pcie_ini_monitor_dump {
+	struct btintel_pcie_ini_dump_header header;
+	__le32	write_ptr;
+	__le32	cycle_cnt;
+	__le32	cur_frag;
+	u8	data[];
+} __packed;
+
+/* Linked list entry for modular dump collection */
+struct btintel_pcie_dump_entry {
+	struct list_head	list;
+	u32			size;
+	u8			data[];
+};
+
+/* File-level header for coredump output.
+ * Compatible with iwl_fw_ini_dump_file_hdr.
+ * Uses IWL_FW_INI_ERROR_DUMP_BARKER (0x14789633) for decoder compatibility.
+ */
+#define BTINTEL_PCIE_INI_ERROR_DUMP_BARKER	0x14789633
+
+struct btintel_pcie_dump_file_hdr {
+	__le32	barker;
+	__le32	file_len;
+} __packed;
+
+/*
+ * Legacy-style dump data wrapper for dump info TLV.
+ * Compatible with iwl_fw_error_dump_data.
+ * Used only for the dump info entry (type=BTINTEL_PCIE_INI_DUMP_INFO_TYPE).
+ */
+struct btintel_pcie_error_dump_data {
+	__le32	type;
+	__le32	len;
+	u8	data[];
+} __packed;
+
+/* Use bit 31 as dump info type, matching IWL_INI_DUMP_INFO_TYPE */
+#define BTINTEL_PCIE_INI_DUMP_INFO_TYPE	BIT(31)
+
+/* Time point values matching iwl_fw_ini_time_point for fwdump parser */
+#define BTINTEL_PCIE_TIME_POINT_FW_ASSERT	4
+#define BTINTEL_PCIE_TIME_POINT_USER_TRIGGER	9
+
+/*
+ * Dump info struct - single TLV containing all metadata.
+ * Compatible with iwl_fw_ini_dump_info.
+ * Packs all device/firmware info that the decoder needs.
+ */
+struct btintel_pcie_ini_dump_info {
+	__le32	version;
+	__le32	time_point;
+	__le32	trigger_reason;
+	__le32	external_cfg_state;
+	__le32	ver_type;
+	__le32	ver_subtype;
+	__le32	hw_step;
+	__le32	hw_type;
+	__le32	rf_id_flavor;
+	__le32	rf_id_dash;
+	__le32	rf_id_step;
+	__le32	rf_id_type;
+	__le32	lmac_major;
+	__le32	lmac_minor;
+	__le32	umac_major;
+	__le32	umac_minor;
+	__le32	fw_mon_mode;
+	__le64	regions_mask;
+	__le32	build_tag_len;
+	u8	build_tag[64];
+	__le32	num_of_cfg_names;
+	/* no cfg_names for BT - keep zero-length */
+} __packed;
+
 /* struct btintel_pcie_data
  * @pdev: pci device
  * @hdev: hdev device
-- 
2.43.0


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

* RE: [v1,1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
                   ` (2 preceding siblings ...)
  2026-08-28 14:27 ` [PATCH v1 4/4] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
@ 2026-08-28 22:15 ` bluez.test.bot
  2026-09-04  0:29 ` [PATCH v2 1/3] " Chandrashekar Devegowda
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: bluez.test.bot @ 2026-08-28 22:15 UTC (permalink / raw)
  To: linux-bluetooth, chandrashekar.devegowda

[-- Attachment #1: Type: text/plain, Size: 1679 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1153294

---Test result---

Test Summary:
CheckPatch                    PASS      3.53 seconds
VerifyFixes                   PASS      0.08 seconds
VerifySignedoff               PASS      0.09 seconds
GitLint                       FAIL      0.86 seconds
SubjectPrefix                 PASS      0.29 seconds
BuildKernel                   PASS      27.83 seconds
CheckAllWarning               PASS      30.31 seconds
CheckSparse                   PASS      29.13 seconds
BuildKernel32                 PASS      26.64 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      504.06 seconds
IncrementalBuild              PASS      32.39 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[v1,1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition

1: T1 Title exceeds max length (84>80): "[v1,1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition"
[v1,3/4] Bluetooth: btintel_pcie: log mailbox handler latency in msix_gp1_handler

1: T1 Title exceeds max length (81>80): "[v1,3/4] Bluetooth: btintel_pcie: log mailbox handler latency in msix_gp1_handler"
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


https://github.com/bluez/bluetooth-next/pull/661

---
Regards,
Linux Bluetooth


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

* [PATCH v2 1/3] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
                   ` (3 preceding siblings ...)
  2026-08-28 22:15 ` [v1,1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition bluez.test.bot
@ 2026-09-04  0:29 ` Chandrashekar Devegowda
  2026-09-04  1:10   ` [v2,1/3] " bluez.test.bot
  2026-09-04 16:56   ` [PATCH v2 1/3] " patchwork-bot+bluetooth
  2026-09-04  0:29 ` [PATCH v2 2/3] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 15+ messages in thread
From: Chandrashekar Devegowda @ 2026-09-04  0:29 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan, kiran.k,
	Chandrashekar Devegowda

Two identical definitions of BTINTEL_PCIE_MAGIC_NUM were present in
btintel_pcie.c, one indented with spaces and one with a tab. Remove the
space-indented duplicate and the surrounding blank lines. Kernel coding
style requires tabs for indentation, so keep the tab-indented copy.

No functional change.

Assisted-by: Copilot:claude-opus-4.7 sparse
Fixes: 6ed83047389c ("Bluetooth: btintel_pcie: Setup buffers for firmware traces")
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
Changes in v2:
 - No changes; re-sent as part of the v2 series.

 drivers/bluetooth/btintel_pcie.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 30923eaabed7..b90f87e8368d 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -73,11 +73,6 @@ struct btintel_pcie_dev_recovery {
 #define BTINTEL_PCIE_HCI_EVT_PKT	0x00000004
 #define BTINTEL_PCIE_HCI_ISO_PKT	0x00000005
 
-#define BTINTEL_PCIE_MAGIC_NUM    0xA5A5A5A5
-
-
-
-
 #define BTINTEL_PCIE_MAGIC_NUM	0xA5A5A5A5
 
 #define BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER	0x17A2
-- 
2.43.0


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

* [PATCH v2 2/3] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
                   ` (4 preceding siblings ...)
  2026-09-04  0:29 ` [PATCH v2 1/3] " Chandrashekar Devegowda
@ 2026-09-04  0:29 ` Chandrashekar Devegowda
  2026-09-04 17:50   ` patchwork-bot+bluetooth
  2026-09-04  0:29 ` [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
  2026-09-04 16:56 ` [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition patchwork-bot+bluetooth
  7 siblings, 1 reply; 15+ messages in thread
From: Chandrashekar Devegowda @ 2026-09-04  0:29 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan, kiran.k,
	Chandrashekar Devegowda

Newer Intel BT PCIe variants (Nova Lake SCP2 and PTL FMP2) require
three independent DRAM debug (DBGC) buffer pools instead of the
single pool used by existing controllers.

Factor the per-pool buffer allocation into a common helper and add
a multi-DBGC (MDBGC) setup path that allocates three pools using
the fragment context format. MDBGC is used when the device reports
an MDBGC-capable variant; otherwise the existing single-pool DBGC
path is retained.

Assisted-by: Copilot:claude-opus-4.7 sparse
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
Changes in v2:
 - Reworded the commit message for brevity.
 - Skip coredump collection in btintel_pcie_coredump_worker() when
   the controller reports MDBGC support, since the MDBGC dump format
   is not handled by this patch. The unified decoder path added in
   patch 3/3 replaces this skip with proper multi-DBGC collection.

 drivers/bluetooth/btintel_pcie.c | 184 ++++++++++++++++++++++++++-----
 drivers/bluetooth/btintel_pcie.h |  30 +++++
 2 files changed, 186 insertions(+), 28 deletions(-)

diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index b90f87e8368d..233cb8a3cbd4 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -134,6 +134,20 @@ struct btintel_pcie_dbgc_ctxt {
 	struct btintel_pcie_dbgc_ctxt_buf bufs[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
 };
 
+struct btintel_pcie_mdbgc_ctxt {
+	u32     magic_num;
+	u32     ver;
+	u32     buf1_index;
+	u32     buf1_count;
+	struct btintel_pcie_dbgc_ctxt_buf buf1[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
+	u32     buf2_index;
+	u32     buf2_count;
+	struct btintel_pcie_dbgc_ctxt_buf buf2[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
+	u32     buf3_index;
+	u32     buf3_count;
+	struct btintel_pcie_dbgc_ctxt_buf buf3[BTINTEL_PCIE_DBGC_BUFFER_COUNT];
+};
+
 struct btintel_pcie_trigger_evt {
 	u8 type;
 	u8 len;
@@ -184,57 +198,159 @@ static inline bool btintel_pcie_dbg_to_wifi(struct btintel_pcie_data *data)
 	return data->dbg_path_cache != BTINTEL_PCIE_DRAM;
 }
 
-/* This function initializes the memory for DBGC buffers and formats the
- * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
- * size as the payload
- */
-static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
+/* Helper function to allocate and setup a debug buffer group */
+static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
+				       struct data_buf **buf,
+				       dma_addr_t *p_addr,
+				       void **v_addr,
+				       struct btintel_pcie_dbgc_ctxt_buf *frag,
+				       u32 buf_index,
+				       u32 buf_count)
 {
-	struct btintel_pcie_dbgc_ctxt db_frag;
-	struct data_buf *buf;
+	struct data_buf *b;
 	int i;
 
-	data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
-	data->dbgc.bufs = devm_kcalloc(&data->pdev->dev, data->dbgc.count,
-				       sizeof(*buf), GFP_KERNEL);
-	if (!data->dbgc.bufs)
+	*buf = devm_kcalloc(&data->pdev->dev, buf_count,
+			    sizeof(**buf), GFP_KERNEL);
+	if (!*buf) {
+		BT_ERR("Failed to allocate dbgc buf: %u",
+		       buf_index + 1);
 		return -ENOMEM;
+	}
 
-	data->dbgc.buf_v_addr = dmam_alloc_coherent(&data->pdev->dev,
-						    data->dbgc.count *
-						    BTINTEL_PCIE_DBGC_BUFFER_SIZE,
-						    &data->dbgc.buf_p_addr,
-						    GFP_KERNEL | __GFP_NOWARN);
-	if (!data->dbgc.buf_v_addr)
+	*v_addr = dmam_alloc_coherent(&data->pdev->dev,
+				      buf_count *
+				      BTINTEL_PCIE_DBGC_BUFFER_SIZE,
+				      p_addr,
+				      GFP_KERNEL | __GFP_NOWARN);
+	if (!*v_addr) {
+		BT_ERR("Failed to allocate dbgc buf: %u DMA",
+		       buf_index + 1);
 		return -ENOMEM;
+	}
+
+	for (i = 0; i < buf_count; i++) {
+		b = &(*buf)[i];
+		b->data_p_addr = *p_addr +
+				 i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		b->data = *v_addr +
+			  i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		frag[i].buf_addr_lsb =
+			lower_32_bits(b->data_p_addr);
+		frag[i].buf_addr_msb =
+			upper_32_bits(b->data_p_addr);
+		frag[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+	}
+
+	return 0;
+}
+
+/* This function initializes the memory for MDBGC buffers */
+static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
+{
+	struct btintel_pcie_mdbgc_ctxt db_frag;
+	u32 frag_size = sizeof(db_frag);
+	void *frag_v_addr;
+	int err;
+
+	data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
+
+	frag_v_addr = dmam_alloc_coherent(&data->pdev->dev, frag_size,
+					  &data->mdbgc.frag_p_addr,
+					  GFP_KERNEL | __GFP_NOWARN);
+	if (!frag_v_addr)
+		return -ENOMEM;
+
+	data->mdbgc.frag_v_addr = frag_v_addr;
+	data->mdbgc.frag_size = frag_size;
+
+	memset(&db_frag, 0, sizeof(db_frag));
+	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
+	db_frag.ver = BTINTEL_PCIE_MDBGC_FRAG_VERSION;
+
+	/* Allocate DBGC buffer 1 */
+	db_frag.buf1_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_1;
+	db_frag.buf1_count = data->mdbgc.count;
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->mdbgc.buf1,
+					  &data->mdbgc.buf1_p_addr,
+					  &data->mdbgc.buf1_v_addr,
+					  db_frag.buf1, 0,
+					  data->mdbgc.count);
+	if (err)
+		return err;
+
+	/* Allocate DBGC buffer 2 */
+	db_frag.buf2_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_2;
+	db_frag.buf2_count = data->mdbgc.count;
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->mdbgc.buf2,
+					  &data->mdbgc.buf2_p_addr,
+					  &data->mdbgc.buf2_v_addr,
+					  db_frag.buf2, 1,
+					  data->mdbgc.count);
+	if (err)
+		return err;
+
+	/* Allocate DBGC buffer 3 */
+	db_frag.buf3_index = BTINTEL_PCIE_MDBGC_ALLOCATIONID_3;
+	db_frag.buf3_count = data->mdbgc.count;
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->mdbgc.buf3,
+					  &data->mdbgc.buf3_p_addr,
+					  &data->mdbgc.buf3_v_addr,
+					  db_frag.buf3, 2,
+					  data->mdbgc.count);
+	if (err)
+		return err;
+
+	memcpy(data->mdbgc.frag_v_addr, &db_frag, sizeof(db_frag));
+	return 0;
+}
+
+/* This function initializes the memory for DBGC buffers */
+static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
+{
+	struct btintel_pcie_dbgc_ctxt db_frag;
+	int err;
+
+	data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
 
 	data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
-						     sizeof(struct btintel_pcie_dbgc_ctxt),
-						     &data->dbgc.frag_p_addr,
-						     GFP_KERNEL | __GFP_NOWARN);
+				sizeof(struct btintel_pcie_dbgc_ctxt),
+				&data->dbgc.frag_p_addr,
+				GFP_KERNEL | __GFP_NOWARN);
 	if (!data->dbgc.frag_v_addr)
 		return -ENOMEM;
 
 	data->dbgc.frag_size = sizeof(struct btintel_pcie_dbgc_ctxt);
 
+	memset(&db_frag, 0, sizeof(db_frag));
 	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
 	db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
 	db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
 	db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
 
-	for (i = 0; i < data->dbgc.count; i++) {
-		buf = &data->dbgc.bufs[i];
-		buf->data_p_addr = data->dbgc.buf_p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		buf->data = data->dbgc.buf_v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		db_frag.bufs[i].buf_addr_lsb = lower_32_bits(buf->data_p_addr);
-		db_frag.bufs[i].buf_addr_msb = upper_32_bits(buf->data_p_addr);
-		db_frag.bufs[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-	}
+	err = btintel_pcie_alloc_dbgc_buf(data,
+					  &data->dbgc.bufs,
+					  &data->dbgc.buf_p_addr,
+					  &data->dbgc.buf_v_addr,
+					  db_frag.bufs, 0,
+					  data->dbgc.count);
+	if (err)
+		return err;
 
 	memcpy(data->dbgc.frag_v_addr, &db_frag, sizeof(db_frag));
 	return 0;
 }
 
+static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
+{
+	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
+}
+
 static inline void ipc_print_ia_ring(struct hci_dev *hdev, struct ia *ia,
 				     u16 queue_num)
 {
@@ -1866,6 +1982,12 @@ static void btintel_pcie_coredump_worker(struct work_struct *work)
 		goto out;
 	}
 
+	if (btintel_pcie_is_mdbgc_supported(data)) {
+		bt_dev_info(data->hdev,
+			    "Skipping coredump: MDBGC dump format not supported yet");
+		goto out;
+	}
+
 	btintel_pcie_dump_traces(data->hdev);
 out:
 	/* Release guard last so a new trigger can run only after this
@@ -2247,10 +2369,14 @@ static void btintel_pcie_init_ci(struct btintel_pcie_data *data,
 		 */
 		ci->dbgc_addr = 0;
 		ci->dbgc_size = 0;
+	} else if (btintel_pcie_is_mdbgc_supported(data)) {
+		ci->dbgc_addr = data->mdbgc.frag_p_addr;
+		ci->dbgc_size = data->mdbgc.frag_size;
 	} else {
 		ci->dbgc_addr = data->dbgc.frag_p_addr;
 		ci->dbgc_size = data->dbgc.frag_size;
 	}
+
 	ci->dbg_preset = 0x00;
 }
 
@@ -2483,6 +2609,8 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
 		 * buffer allocation entirely.
 		 */
 		err = 0;
+	} else if (btintel_pcie_is_mdbgc_supported(data)) {
+		err = btintel_pcie_setup_mdbgc(data);
 	} else {
 		err = btintel_pcie_setup_dbgc(data);
 	}
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 9baa214d9bbe..5c35e65d3e81 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -106,8 +106,19 @@
  *   Bits[2:3] DBGI O/P : 01 = WiFi DBGC
  */
 #define BTINTEL_PCIE_DRAM	0x01
+#define BTINTEL_PCIE_FW_MON_MODE_DRAM	0x02
 #define BTINTEL_PCIE_WIFI_DBGC	0x06
 
+#define BTINTEL_PCIE_MDBGC_FRAG_VERSION		2
+
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_1		0
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_2		1
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_3		2
+
+#define BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2	0x6E74
+#define BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2	0xD346
+#define BTINTEL_PCIE_DEVICE_ID_PTL_FMP2		0xE476
+
 /* Causes for the FH register interrupts */
 enum msix_fh_int_causes {
 	BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0	= BIT(0),	/* cause 0 */
@@ -459,6 +470,24 @@ struct btintel_pcie_dbgc {
 	struct data_buf *bufs;
 };
 
+struct btintel_pcie_mdbgc {
+	u32		count;
+
+	void		*frag_v_addr;
+	dma_addr_t	frag_p_addr;
+	u32		frag_size;
+
+	dma_addr_t	buf1_p_addr;
+	void		*buf1_v_addr;
+	dma_addr_t	buf2_p_addr;
+	void		*buf2_v_addr;
+	dma_addr_t	buf3_p_addr;
+	void		*buf3_v_addr;
+	struct data_buf *buf1;
+	struct data_buf *buf2;
+	struct data_buf *buf3;
+};
+
 struct btintel_pcie_dump_mem_info {
 	u32	exception_dump_addr;
 	u32	exception_dump_len;
@@ -599,6 +628,7 @@ struct btintel_pcie_data {
 	u32	alive_intr_ctxt;
 	enum btintel_pcie_reset_type	reset_type;
 	struct btintel_pcie_dbgc	dbgc;
+	struct btintel_pcie_mdbgc	mdbgc;
 	struct btintel_pcie_dump_header dmp_hdr;
 	u8	pm_sx_event;
 	u32	debug_evt_addr;
-- 
2.43.0


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

* [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
                   ` (5 preceding siblings ...)
  2026-09-04  0:29 ` [PATCH v2 2/3] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
@ 2026-09-04  0:29 ` Chandrashekar Devegowda
  2026-09-04  0:46   ` [v2,3/3] " bluez.test.bot
                     ` (2 more replies)
  2026-09-04 16:56 ` [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition patchwork-bot+bluetooth
  7 siblings, 3 replies; 15+ messages in thread
From: Chandrashekar Devegowda @ 2026-09-04  0:29 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan, kiran.k,
	Chandrashekar Devegowda

Replace the legacy TLV-based coredump with a unified INI-format dump
compatible with the iwlwifi decoder so a single decoder can parse
coredumps across all Intel BT PCIe controller variants.

The dump is emitted via dev_coredumpsg() as structured regions built
from firmware-advertised addresses: DRAM monitor buffers (single or
MDBGC multi-buffer), SMEM monitor, exception event buffer, DCCM, SDS,
SDS IOSF, ECL and dump info metadata. Regions are collected only when
firmware advertises a valid address and size, and only collected
regions are advertised in regions_mask.

Add INTEL_CNVX_TOP_DASH() and INTEL_CNVX_TOP_FLAVOR() helpers to
btintel.h for RfIdDash/RfIdFlavor extraction.

Assisted-by: Copilot:gemini-3.6-flash
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
Changes in v2:
 - Reworded the commit message for brevity.
 - Add btintel_pcie_dump_dram_monitor() helper that reads the
   per-allocation status/wrap registers (0x100 * allocation_id
   offset) for each MDBGC pool, stores the per-fragment DWORD write
   offset in mon_dump->write_ptr, and stores the active fragment
   index in mon_dump->cur_frag, replacing the previously flattened
   write_ptr / hard-coded cur_frag = 0 encoding.
 - btintel_pcie_dump_target_region() now returns a skip indicator
   (leaving regions_mask untouched) when the firmware does not
   advertise a valid region, avoiding a spurious
   "Skipping empty dump region: EXCEPTION_EVT_BUFFER" warning when
   the exception dump TLV is absent. It also skips when the size
   rounded down to 4-byte alignment collapses to zero.
 - Log via bt_dev_dbg (instead of bt_dev_err) when firmware does
   not advertise an SMEM monitor region, so the absence of an
   optional TLV is not reported as an error.

 drivers/bluetooth/btintel.h      |   2 +
 drivers/bluetooth/btintel_pcie.c | 887 ++++++++++++++++++++++++-------
 drivers/bluetooth/btintel_pcie.h | 149 +++++-
 3 files changed, 846 insertions(+), 192 deletions(-)

diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index ef232820c31b..c00f793d9dc8 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -225,7 +225,9 @@ struct btintel_sar_rev2 {
 #define INTEL_HW_PLATFORM(cnvx_bt)	((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
 #define INTEL_HW_VARIANT(cnvx_bt)	((u8)(((cnvx_bt) & 0x003f0000) >> 16))
 #define INTEL_CNVX_TOP_TYPE(cnvx_top)	((cnvx_top) & 0x00000fff)
+#define INTEL_CNVX_TOP_DASH(cnvx_top)	(((cnvx_top) & 0x00f00000) >> 20)
 #define INTEL_CNVX_TOP_STEP(cnvx_top)	(((cnvx_top) & 0x0f000000) >> 24)
+#define INTEL_CNVX_TOP_FLAVOR(cnvx_top)	(((cnvx_top) & 0xf0000000) >> 28)
 #define INTEL_CNVX_TOP_PACK_SWAB(t, s)	__swab16(((__u16)(((t) << 4) | (s))))
 
 enum {
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 233cb8a3cbd4..38a6dbd04dcf 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c
@@ -19,6 +19,7 @@
 
 #include <linux/unaligned.h>
 #include <linux/devcoredump.h>
+#include <linux/scatterlist.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -198,7 +199,15 @@ static inline bool btintel_pcie_dbg_to_wifi(struct btintel_pcie_data *data)
 	return data->dbg_path_cache != BTINTEL_PCIE_DRAM;
 }
 
-/* Helper function to allocate and setup a debug buffer group */
+/* Helper function to allocate and setup a debug buffer group
+ * @data: driver data structure
+ * @buf: pointer to data_buf array pointer
+ * @p_addr: pointer to physical DMA address
+ * @v_addr: pointer to virtual address
+ * @frag: pointer to fragment buffer array
+ * @buf_index: buffer index (for error messages)
+ * @buf_count: number of buffers to allocate
+ */
 static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
 				       struct data_buf **buf,
 				       dma_addr_t *p_addr,
@@ -213,39 +222,36 @@ static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
 	*buf = devm_kcalloc(&data->pdev->dev, buf_count,
 			    sizeof(**buf), GFP_KERNEL);
 	if (!*buf) {
-		BT_ERR("Failed to allocate dbgc buf: %u",
-		       buf_index + 1);
+		BT_ERR("Failed to allocate dbgc buf: %u", buf_index + 1);
 		return -ENOMEM;
 	}
 
 	*v_addr = dmam_alloc_coherent(&data->pdev->dev,
 				      buf_count *
-				      BTINTEL_PCIE_DBGC_BUFFER_SIZE,
-				      p_addr,
-				      GFP_KERNEL | __GFP_NOWARN);
+					BTINTEL_PCIE_DBGC_BUFFER_SIZE,
+					p_addr,
+					GFP_KERNEL | __GFP_NOWARN);
 	if (!*v_addr) {
-		BT_ERR("Failed to allocate dbgc buf: %u DMA",
-		       buf_index + 1);
+		BT_ERR("Failed to allocate dbgc buf: %u DMA", buf_index + 1);
 		return -ENOMEM;
 	}
 
 	for (i = 0; i < buf_count; i++) {
 		b = &(*buf)[i];
-		b->data_p_addr = *p_addr +
-				 i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		b->data = *v_addr +
-			  i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-		frag[i].buf_addr_lsb =
-			lower_32_bits(b->data_p_addr);
-		frag[i].buf_addr_msb =
-			upper_32_bits(b->data_p_addr);
+		b->data_p_addr = *p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		b->data = *v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+		frag[i].buf_addr_lsb = lower_32_bits(b->data_p_addr);
+		frag[i].buf_addr_msb = upper_32_bits(b->data_p_addr);
 		frag[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
 	}
 
 	return 0;
 }
 
-/* This function initializes the memory for MDBGC buffers */
+/* This function initializes the memory for MDBGC buffers and formats the
+ * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
+ * size as the payload
+ */
 static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 {
 	struct btintel_pcie_mdbgc_ctxt db_frag;
@@ -255,15 +261,19 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 
 	data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
 
+	/* Allocate fragment context structure */
 	frag_v_addr = dmam_alloc_coherent(&data->pdev->dev, frag_size,
 					  &data->mdbgc.frag_p_addr,
 					  GFP_KERNEL | __GFP_NOWARN);
-	if (!frag_v_addr)
+	if (!frag_v_addr) {
+		BT_ERR("Failed to allocate mdbgc context");
 		return -ENOMEM;
+	}
 
 	data->mdbgc.frag_v_addr = frag_v_addr;
 	data->mdbgc.frag_size = frag_size;
 
+	/* Initialize fragment header */
 	memset(&db_frag, 0, sizeof(db_frag));
 	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
 	db_frag.ver = BTINTEL_PCIE_MDBGC_FRAG_VERSION;
@@ -275,7 +285,8 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 					  &data->mdbgc.buf1,
 					  &data->mdbgc.buf1_p_addr,
 					  &data->mdbgc.buf1_v_addr,
-					  db_frag.buf1, 0,
+					  db_frag.buf1,
+					  0,
 					  data->mdbgc.count);
 	if (err)
 		return err;
@@ -287,7 +298,8 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 					  &data->mdbgc.buf2,
 					  &data->mdbgc.buf2_p_addr,
 					  &data->mdbgc.buf2_v_addr,
-					  db_frag.buf2, 1,
+					  db_frag.buf2,
+					  1,
 					  data->mdbgc.count);
 	if (err)
 		return err;
@@ -299,58 +311,64 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
 					  &data->mdbgc.buf3,
 					  &data->mdbgc.buf3_p_addr,
 					  &data->mdbgc.buf3_v_addr,
-					  db_frag.buf3, 2,
+					  db_frag.buf3,
+					  2,
 					  data->mdbgc.count);
 	if (err)
 		return err;
 
+	/* Copy fragment to DMA coherent memory */
 	memcpy(data->mdbgc.frag_v_addr, &db_frag, sizeof(db_frag));
 	return 0;
 }
 
-/* This function initializes the memory for DBGC buffers */
+/* This function initializes the memory for DBGC buffers and formats the
+ * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
+ * size as the payload
+ */
 static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
 {
 	struct btintel_pcie_dbgc_ctxt db_frag;
+	u32 frag_size = sizeof(db_frag);
 	int err;
 
 	data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
 
+	/* Allocate fragment context structure */
 	data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
-				sizeof(struct btintel_pcie_dbgc_ctxt),
-				&data->dbgc.frag_p_addr,
-				GFP_KERNEL | __GFP_NOWARN);
-	if (!data->dbgc.frag_v_addr)
+						     frag_size,
+						     &data->dbgc.frag_p_addr,
+						     GFP_KERNEL | __GFP_NOWARN);
+	if (!data->dbgc.frag_v_addr) {
+		BT_ERR("Failed to allocate dbgc context");
 		return -ENOMEM;
+	}
 
 	data->dbgc.frag_size = sizeof(struct btintel_pcie_dbgc_ctxt);
 
+	/* Initialize fragment header */
 	memset(&db_frag, 0, sizeof(db_frag));
 	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
 	db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
 	db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
 	db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
 
+	/* Allocate DBGC buffers */
 	err = btintel_pcie_alloc_dbgc_buf(data,
 					  &data->dbgc.bufs,
 					  &data->dbgc.buf_p_addr,
 					  &data->dbgc.buf_v_addr,
-					  db_frag.bufs, 0,
+					  db_frag.bufs,
+					  0,
 					  data->dbgc.count);
 	if (err)
 		return err;
 
+	/* Copy fragment to DMA coherent memory */
 	memcpy(data->dbgc.frag_v_addr, &db_frag, sizeof(db_frag));
 	return 0;
 }
 
-static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
-{
-	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
-		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
-		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
-}
-
 static inline void ipc_print_ia_ring(struct hci_dev *hdev, struct ia *ia,
 				     u16 queue_num)
 {
@@ -753,169 +771,673 @@ static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data)
 	}
 }
 
-static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type,
-				   void *data, size_t size)
+static struct scatterlist *btintel_pcie_alloc_sgtable(ssize_t size)
 {
-	struct intel_tlv *tlv;
+	int nents, i;
+	struct page *page;
+	struct scatterlist *sg, *result;
+
+	if (size <= 0)
+		return NULL;
+
+	nents = DIV_ROUND_UP(size, PAGE_SIZE);
+	result = kcalloc(nents, sizeof(*result), GFP_KERNEL);
+	if (!result)
+		return NULL;
+
+	sg_init_table(result, nents);
+	sg = result;
 
-	tlv = dest;
-	tlv->type = type;
-	tlv->len = size;
-	memcpy(tlv->val, data, tlv->len);
-	return dest + sizeof(*tlv) + size;
+	for (i = 0; size > 0; i++) {
+		ssize_t bytes = min_t(ssize_t, size, PAGE_SIZE);
+
+		page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+		if (!page)
+			goto err_free;
+
+		sg_set_page(sg, page, bytes, 0);
+		sg = sg_next(sg);
+		size -= bytes;
+	}
+	return result;
+
+err_free:
+	for (sg = result; sg; sg = sg_next(sg)) {
+		page = sg_page(sg);
+		if (page)
+			__free_page(page);
+	}
+	kfree(result);
+	return NULL;
 }
 
-static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
+static struct btintel_pcie_dump_entry *
+btintel_pcie_dump_entry_alloc(u32 data_size)
 {
-	u32 offset, prev_size, wr_ptr_status, dump_size, data_len;
-	u32 status_reg, wrap_reg;
-	struct btintel_pcie_dbgc *dbgc = &data->dbgc;
+	struct btintel_pcie_dump_entry *entry;
+
+	entry = vzalloc(sizeof(*entry) + data_size);
+	if (!entry)
+		return NULL;
+
+	entry->size = data_size;
+	INIT_LIST_HEAD(&entry->list);
+	return entry;
+}
+
+static void btintel_pcie_dump_list_free(struct list_head *list)
+{
+	struct btintel_pcie_dump_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, list, list) {
+		list_del(&entry->list);
+		vfree(entry);
+	}
+}
+
+static u32 btintel_pcie_dump_list_total_size(struct list_head *list)
+{
+	struct btintel_pcie_dump_entry *entry;
+	u32 total = 0;
+
+	list_for_each_entry(entry, list, list)
+		total += entry->size;
+
+	return total;
+}
+
+static int btintel_pcie_dump_dram(struct list_head *list,
+				  u8 count, struct data_buf *bufs,
+				  u32 write_ptr, u32 wrap_ctr, u32 cur_frag,
+				  u32 region_id, const char *name)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_ini_dump_data *dump_data;
+	struct btintel_pcie_ini_monitor_dump *mon_dump;
+	struct btintel_pcie_dump_range *range;
+	u32 mon_hdr_size, ranges_size, payload_size, total_size;
+	int i;
+
+	mon_hdr_size = sizeof(*mon_dump);
+	ranges_size = count * (sizeof(*range) + BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+	payload_size = mon_hdr_size + ranges_size;
+	total_size = sizeof(*dump_data) + payload_size;
+
+	entry = btintel_pcie_dump_entry_alloc(total_size);
+	if (!entry)
+		return -ENOMEM;
+
+	dump_data = (void *)entry->data;
+	dump_data->type = BTINTEL_PCIE_INI_REGION_DRAM_BUFFER;
+	dump_data->sub_type = 0;
+	dump_data->sub_type_ver = 0;
+	dump_data->reserved = 0;
+	dump_data->len = cpu_to_le32(payload_size);
+
+	mon_dump = (void *)dump_data->data;
+	mon_dump->header.version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	mon_dump->header.region_id = cpu_to_le32(region_id);
+	mon_dump->header.num_of_ranges = cpu_to_le32(count);
+	mon_dump->header.name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
+	memset(mon_dump->header.name, 0, BTINTEL_PCIE_INI_MAX_NAME);
+	strscpy(mon_dump->header.name, name, BTINTEL_PCIE_INI_MAX_NAME);
+
+	mon_dump->write_ptr = cpu_to_le32(write_ptr);
+	mon_dump->cycle_cnt = cpu_to_le32(wrap_ctr);
+	mon_dump->cur_frag = cpu_to_le32(cur_frag);
+
+	range = (void *)mon_dump->data;
+	for (i = 0; i < count; i++) {
+		range->range_data_size =
+			cpu_to_le32(BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+		range->dram_base_addr = cpu_to_le64(bufs[i].data_p_addr);
+		memcpy(range->data, bufs[i].data,
+		       BTINTEL_PCIE_DBGC_BUFFER_SIZE);
+		range = (void *)range->data + BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+	}
+
+	list_add_tail(&entry->list, list);
+	return 0;
+}
+
+static int
+btintel_pcie_dump_dram_monitor(struct btintel_pcie_data *data,
+			       struct list_head *list, u8 count,
+			       struct data_buf *bufs, u32 status_reg,
+			       u32 wrap_reg, u8 allocation_id, u32 region_id,
+			       const char *name)
+{
+	u32 allocation_offset, write_ptr, wrap_ctr, wr_ptr_status;
+	u8 cur_frag;
+
+	allocation_offset = allocation_id *
+			    BTINTEL_PCIE_DBGC_ALLOCATION_OFFSET;
+	wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg +
+						allocation_offset);
+	wrap_ctr = btintel_pcie_rd_dev_mem(data, wrap_reg +
+					  allocation_offset);
+
+	write_ptr = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
+	write_ptr >>= 2;
+	cur_frag = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
+	if (cur_frag >= count) {
+		bt_dev_warn(data->hdev,
+			    "Invalid DRAM monitor fragment %u for allocation %u",
+			    cur_frag, allocation_id);
+		return -EINVAL;
+	}
+
+	bt_dev_dbg(data->hdev,
+		   "allocation=%u wr_ptr_status=0x%08x write_ptr=0x%06x cur_frag=%u wrap_ctr=0x%08x",
+		   allocation_id, wr_ptr_status, write_ptr, cur_frag, wrap_ctr);
+
+	return btintel_pcie_dump_dram(list, count, bufs, write_ptr, wrap_ctr,
+				      cur_frag, region_id, name);
+}
+
+static int btintel_pcie_dump_target_region(struct btintel_pcie_data *data,
+					   struct list_head *list,
+					   u32 region_id, const char *name,
+					   u32 addr_start, u32 size)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_ini_dump_data *dump_data;
+	struct btintel_pcie_ini_dump_header *hdr;
+	struct btintel_pcie_dump_range *range;
+	u32 payload_size, total_size, target_mem_offset, tempdata;
+	u8 *dest;
+	int i;
+
+	if (!size) {
+		bt_dev_warn(data->hdev, "Skipping empty dump region: %s", name);
+		return 1;
+	}
+
+	if (!addr_start) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region with zero address: %s",
+			    name);
+		return 1;
+	}
+
+	if (size > BTINTEL_PCIE_REGION_MAX_SIZE) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: size %u exceeds max %u",
+			    name, size, BTINTEL_PCIE_REGION_MAX_SIZE);
+		return 1;
+	}
+
+	if (addr_start > U32_MAX - size) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: addr_start 0x%08x + size %u would overflow",
+			    name, addr_start, size);
+		return 1;
+	}
+
+	/* Align to 4 bytes - target access reads 32-bit words */
+	size = round_down(size, sizeof(u32));
+	if (!size) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: size is smaller than 4-byte access",
+			    name);
+		return 1;
+	}
+
+	bt_dev_dbg(data->hdev,
+		   "Target access: region=%s start=0x%08x size=%u",
+		   name, addr_start, size);
+
+	payload_size = sizeof(*hdr) + sizeof(*range) + size;
+	total_size = sizeof(*dump_data) + payload_size;
+	entry = btintel_pcie_dump_entry_alloc(total_size);
+	if (!entry)
+		return -ENOMEM;
+
+	dump_data = (void *)entry->data;
+	dump_data->type = BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY;
+	dump_data->sub_type = 0;
+	dump_data->sub_type_ver = 0;
+	dump_data->reserved = 0;
+	dump_data->len = cpu_to_le32(payload_size);
+
+	hdr = (void *)dump_data->data;
+	hdr->version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	hdr->region_id = cpu_to_le32(region_id);
+	hdr->num_of_ranges = cpu_to_le32(1);
+	hdr->name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
+	memset(hdr->name, 0, BTINTEL_PCIE_INI_MAX_NAME);
+	strscpy(hdr->name, name, BTINTEL_PCIE_INI_MAX_NAME);
+
+	range = (void *)(hdr + 1);
+	range->range_data_size = cpu_to_le32(size);
+	range->internal_base_addr = cpu_to_le32(addr_start);
+
+	dest = (u8 *)range->data;
+	target_mem_offset = size / sizeof(u32);
+	for (i = 0; i < target_mem_offset; i++) {
+		u32 offset = BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET * i;
+
+		tempdata = btintel_pcie_rd_dev_mem(data,
+						   addr_start + offset);
+		memcpy(dest, &tempdata, sizeof(tempdata));
+		dest += sizeof(tempdata);
+	}
+
+	list_add_tail(&entry->list, list);
+	return 0;
+}
+
+static int btintel_pcie_dump_smem_monitor_region(struct btintel_pcie_data *data,
+						 struct list_head *list,
+						 u32 region_id,
+						 const char *name,
+						 u32 addr_start, u32 size)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_ini_dump_data *dump_data;
+	struct btintel_pcie_ini_monitor_dump *mon;
+	struct btintel_pcie_dump_range *range;
+	u32 payload_size, total_size, target_mem_offset, tempdata;
+	u8 *dest;
+	int i;
+
+	if (!size || !addr_start) {
+		bt_dev_err(data->hdev, "Skipping smem dump: size = %u addr = %8.8x",
+			   size, addr_start);
+		return -EINVAL;
+	}
+
+	payload_size = sizeof(*mon) + sizeof(*range) + size;
+	total_size = sizeof(*dump_data) + payload_size;
+	entry = btintel_pcie_dump_entry_alloc(total_size);
+	if (!entry)
+		return -ENOMEM;
+
+	dump_data = (void *)entry->data;
+	dump_data->type = BTINTEL_PCIE_INI_REGION_INTERNAL_BUFFER;
+	dump_data->sub_type = 0;
+	dump_data->sub_type_ver = 0;
+	dump_data->reserved = 0;
+	dump_data->len = cpu_to_le32(payload_size);
+
+	mon = (void *)dump_data->data;
+	mon->header.version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	mon->header.region_id = cpu_to_le32(region_id);
+	mon->header.num_of_ranges = cpu_to_le32(1);
+	mon->header.name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
+	memset(mon->header.name, 0, BTINTEL_PCIE_INI_MAX_NAME);
+	strscpy(mon->header.name, name, BTINTEL_PCIE_INI_MAX_NAME);
+
+	mon->write_ptr = cpu_to_le32(0);
+	mon->cycle_cnt = cpu_to_le32(0);
+	mon->cur_frag = cpu_to_le32(0);
+
+	range = (void *)mon->data;
+	range->range_data_size = cpu_to_le32(size);
+	range->internal_base_addr = cpu_to_le32(addr_start);
+
+	dest = (u8 *)range->data;
+	target_mem_offset = size / sizeof(u32);
+	for (i = 0; i < target_mem_offset; i++) {
+		u32 offset = BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET * i;
+
+		tempdata = btintel_pcie_rd_dev_mem(data,
+						   addr_start + offset);
+		memcpy(dest, &tempdata, sizeof(tempdata));
+		dest += sizeof(tempdata);
+	}
+
+	list_add_tail(&entry->list, list);
+	return 0;
+}
+
+static int btintel_pcie_dump_info(struct btintel_pcie_data *data,
+				  struct list_head *list,
+				  u64 regions_mask)
+{
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_error_dump_data *tlv;
+	struct btintel_pcie_ini_dump_info *dump;
+	u32 size = sizeof(*tlv) + sizeof(*dump);
+	char build_tag[64];
+
+	entry = btintel_pcie_dump_entry_alloc(size);
+	if (!entry)
+		return -ENOMEM;
+
+	tlv = (void *)entry->data;
+	tlv->type = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_INFO_TYPE);
+	tlv->len = cpu_to_le32(sizeof(*dump));
+
+	dump = (void *)tlv->data;
+	memset(dump, 0, sizeof(*dump));
+
+	dump->version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
+	dump->trigger_reason = cpu_to_le32(data->dmp_hdr.trigger_reason);
+
+	if (data->dmp_hdr.trigger_reason ==
+	    BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT)
+		dump->time_point =
+			cpu_to_le32(BTINTEL_PCIE_TIME_POINT_FW_ASSERT);
+	else if (data->dmp_hdr.trigger_reason ==
+		 BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER)
+		dump->time_point =
+			cpu_to_le32(BTINTEL_PCIE_TIME_POINT_USER_TRIGGER);
+
+	dump->hw_type = cpu_to_le32(INTEL_CNVX_TOP_TYPE(data->dmp_hdr.cnvi_top));
+	dump->hw_step = cpu_to_le32(INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvi_top));
+	bt_dev_dbg(data->hdev, "hw_type=0x%x hw_step=0x%x (cnvi_top=0x%x)",
+		   le32_to_cpu(dump->hw_type), le32_to_cpu(dump->hw_step),
+		   data->dmp_hdr.cnvi_top);
+
+	dump->ver_type = cpu_to_le32(data->dmp_hdr.cnvi_bt);
+	dump->ver_subtype = cpu_to_le32(data->dmp_hdr.fw_sha);
+	dump->rf_id_type = cpu_to_le32(INTEL_CNVX_TOP_TYPE(data->dmp_hdr.cnvr_top));
+	dump->rf_id_dash = cpu_to_le32(INTEL_CNVX_TOP_DASH(data->dmp_hdr.cnvr_top));
+	dump->rf_id_step = cpu_to_le32(INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvr_top));
+	dump->rf_id_flavor = cpu_to_le32(INTEL_CNVX_TOP_FLAVOR(data->dmp_hdr.cnvr_top));
+	bt_dev_dbg(data->hdev,
+		   "rf_id_type=0x%x rf_id_dash=0x%x rf_id_step=0x%x rf_id_flavor=0x%x (cnvr_top=0x%x)",
+		   le32_to_cpu(dump->rf_id_type),
+		   le32_to_cpu(dump->rf_id_dash),
+		   le32_to_cpu(dump->rf_id_step),
+		   le32_to_cpu(dump->rf_id_flavor),
+		   data->dmp_hdr.cnvr_top);
+	dump->lmac_major = cpu_to_le32(0);
+	dump->lmac_minor = cpu_to_le32(0);
+	dump->umac_major = cpu_to_le32(0);
+	dump->umac_minor = cpu_to_le32(0);
+	dump->fw_mon_mode = cpu_to_le32(BTINTEL_PCIE_FW_MON_MODE_DRAM);
+
+	dump->regions_mask = cpu_to_le64(regions_mask);
+
+	bt_dev_dbg(data->hdev, "ExpectedRegionIDs regions_mask=0x%016llx",
+		   le64_to_cpu(dump->regions_mask));
+
+	snprintf(build_tag, sizeof(build_tag), "%08X", data->dmp_hdr.fw_sha);
+
+	dump->build_tag_len = cpu_to_le32(strlen(build_tag));
+	memcpy(dump->build_tag, build_tag, min(strlen(build_tag),
+					       sizeof(dump->build_tag)));
+
+	dump->num_of_cfg_names = cpu_to_le32(0);
+
+	bt_dev_dbg(data->hdev,
+		   "HwType=0x%08x HwStep=0x%08x RfIdType=0x%08x RfIdDash=0x%08x RfIdStep=0x%08x RfIdFlavor=0x%08x",
+		   le32_to_cpu(dump->hw_type), le32_to_cpu(dump->hw_step),
+		   le32_to_cpu(dump->rf_id_type),
+		   le32_to_cpu(dump->rf_id_dash),
+		   le32_to_cpu(dump->rf_id_step),
+		   le32_to_cpu(dump->rf_id_flavor));
+	bt_dev_dbg(data->hdev, "VerType=0x%08x VerSubType=0x%08x",
+		   le32_to_cpu(dump->ver_type), le32_to_cpu(dump->ver_subtype));
+	bt_dev_dbg(data->hdev,
+		   "LmacMajor=0x%08x LmacMinor=0x%08x UmacMajor=0x%08x UmacMinor=0x%08x",
+		   le32_to_cpu(dump->lmac_major), le32_to_cpu(dump->lmac_minor),
+		   le32_to_cpu(dump->umac_major),
+		   le32_to_cpu(dump->umac_minor));
+	bt_dev_dbg(data->hdev, "TriggerReason=0x%04x MonMode=%u BuildTag=%.64s",
+		   le32_to_cpu(dump->trigger_reason),
+		   le32_to_cpu(dump->fw_mon_mode),
+		   dump->build_tag);
+
+	list_add(&entry->list, list);
+
+	return 0;
+}
+
+static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
+{
+	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
+		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
+}
+
+static void btintel_pcie_dump_mem_range(struct btintel_pcie_data *data,
+					struct list_head *list, u32 region_id,
+					const char *name, u32 addr_start,
+					u32 addr_end, u64 *regions_mask)
+{
+	u32 region_size;
+
+	if (!addr_start || !addr_end || addr_end < addr_start)
+		return;
+
+	if (addr_end > U32_MAX - 0x04) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: addr_end 0x%08x would overflow",
+			    name, addr_end);
+		return;
+	}
+
+	region_size = (addr_end + 0x04) - addr_start;
+	if (region_size > BTINTEL_PCIE_REGION_MAX_SIZE) {
+		bt_dev_warn(data->hdev,
+			    "Skipping dump region %s: size %u exceeds max %u",
+			    name, region_size,
+			    BTINTEL_PCIE_REGION_MAX_SIZE);
+		return;
+	}
+
+	if (!btintel_pcie_dump_target_region(data, list, region_id, name,
+					     addr_start, region_size))
+		*regions_mask |= BIT_ULL(region_id);
+}
+
+static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
+{
+	struct btintel_pcie_dbgc *dbgc = NULL;
+	struct btintel_pcie_mdbgc *mdbgc = NULL;
 	struct hci_dev *hdev = data->hdev;
-	u8 *pdata, *p, buf_idx, hw_variant;
-	struct intel_tlv *tlv;
-	struct timespec64 now;
-	struct tm tm_now;
-	char fw_build[128];
-	char ts[128];
-	char vendor[64];
-	char driver[64];
+	struct btintel_pcie_dump_entry *entry;
+	struct btintel_pcie_dump_file_hdr *file_hdr;
+	struct scatterlist *sg_dump_data;
+	u32 status_reg, wrap_reg;
+	u32 exception_dump_len;
+	u32 exc_addr;
+	u64 regions_mask = 0;
+	u8 hw_variant;
+	u32 smem_rd_addr = 0, smem_rd_size = 0;
+	u32 file_len;
+	u8 count;
+	int ret;
+	LIST_HEAD(dump_list);
 
 	if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
 		return -EOPNOTSUPP;
 
+	if (btintel_pcie_is_mdbgc_supported(data)) {
+		mdbgc = &data->mdbgc;
+		count = mdbgc->count;
+	} else {
+		dbgc = &data->dbgc;
+		count = dbgc->count;
+	}
 
 	hw_variant = INTEL_HW_VARIANT(data->cnvi);
-	switch (hw_variant) {
-	case BTINTEL_HWID_BZRI:
-	case BTINTEL_HWID_BZRIW:
+
+	if (hw_variant == BTINTEL_HWID_BZRI ||
+	    hw_variant == BTINTEL_HWID_BZRIW) {
 		status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS;
 		wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND;
-		break;
-	case BTINTEL_HWID_SCP:
-	case BTINTEL_HWID_SCP2:
-	case BTINTEL_HWID_SCP2F:
+	} else if (hw_variant >= BTINTEL_HWID_SCP) {
 		status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP;
 		wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP;
-		break;
-	default:
-		bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%2.2x)",
+	} else {
+		bt_dev_err(hdev,
+			   "Unsupported Intel hardware variant (0x%2.2x)",
 			   hw_variant);
 		return -EINVAL;
 	}
 
-	wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg);
-	data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data, wrap_reg);
+	smem_rd_addr = data->dump_info.smem_addr_start;
+	smem_rd_size = 0;
 
-	offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
+	if (!smem_rd_addr && !data->dump_info.smem_addr_end) {
+		bt_dev_dbg(hdev, "smem region not advertised by firmware");
+	} else if (data->dump_info.smem_addr_end < smem_rd_addr ||
+		   data->dump_info.smem_addr_end > U32_MAX - 0x04) {
+		bt_dev_err(hdev,
+			   "Invalid smem region: start=0x%08x end=0x%08x",
+			   smem_rd_addr, data->dump_info.smem_addr_end);
+	} else {
+		smem_rd_size = (data->dump_info.smem_addr_end + 0x04) -
+			       smem_rd_addr;
 
-	buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
-	if (buf_idx > dbgc->count) {
-		bt_dev_warn(hdev, "Buffer index is invalid");
-		return -EINVAL;
+		bt_dev_dbg(hdev,
+			   "smem_region: smem_start_addr=0x%08x smem_end_addr=0x%08x smem_rd_size=%u",
+			   smem_rd_addr, data->dump_info.smem_addr_end,
+			   smem_rd_size);
+
+		if (smem_rd_size == 0 ||
+		    smem_rd_size > BTINTEL_PCIE_SMEM_MAX_SIZE) {
+			bt_dev_err(hdev,
+				   "Invalid smem region: smem_rd_addr 0x%08x size %u (max %u)",
+				   smem_rd_addr, smem_rd_size,
+				   BTINTEL_PCIE_SMEM_MAX_SIZE);
+			smem_rd_size = 0;
+		}
 	}
 
-	prev_size = buf_idx * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
-	if (prev_size + offset >= prev_size)
-		data->dmp_hdr.write_ptr = prev_size + offset;
-	else
-		return -EINVAL;
+	if (btintel_pcie_is_mdbgc_supported(data)) {
+		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
+						     mdbgc->buf1, status_reg,
+						     wrap_reg,
+						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_1,
+						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR1,
+						     "monitor");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR1);
+		else
+			bt_dev_warn(hdev, "Failed to dump DRAM buf1: %d", ret);
+
+		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
+						     mdbgc->buf2, status_reg,
+						     wrap_reg,
+						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_2,
+						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR2,
+						     "monitor2");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR2);
+		else
+			bt_dev_warn(hdev, "Failed to dump DRAM buf2: %d", ret);
+
+		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
+						     mdbgc->buf3, status_reg,
+						     wrap_reg,
+						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_3,
+						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR3,
+						     "monitor3");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR3);
+		else
+			bt_dev_warn(hdev, "Failed to dump DRAM buf3: %d", ret);
+	} else {
+		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
+						     dbgc->bufs, status_reg,
+						     wrap_reg,
+						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_1,
+						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR1,
+						     "monitor");
+		if (!ret)
+			regions_mask |=
+				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR1);
+		else
+			bt_dev_warn(hdev,
+				    "Failed to dump DRAM region: %d", ret);
+	}
 
-	strscpy(vendor, "Vendor: Intel\n");
-	snprintf(driver, sizeof(driver), "Driver: %s\n",
-		 data->dmp_hdr.driver_name);
-
-	ktime_get_real_ts64(&now);
-	time64_to_tm(now.tv_sec, 0, &tm_now);
-	snprintf(ts, sizeof(ts), "Dump Time: %02d-%02d-%04ld %02d:%02d:%02d",
-				 tm_now.tm_mday, tm_now.tm_mon + 1, tm_now.tm_year + 1900,
-				 tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
-
-	snprintf(fw_build, sizeof(fw_build),
-			    "Firmware Timestamp: Year %u WW %02u buildtype %u build %u",
-			    2000 + (data->dmp_hdr.fw_timestamp >> 8),
-			    data->dmp_hdr.fw_timestamp & 0xff, data->dmp_hdr.fw_build_type,
-			    data->dmp_hdr.fw_build_num);
-
-	data_len = sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_bt) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.write_ptr) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.wrap_ctr) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.trigger_reason) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.fw_sha) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.cnvr_top) +
-		sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_top) +
-		sizeof(*tlv) + strlen(ts) +
-		sizeof(*tlv) + strlen(fw_build) +
-		sizeof(*tlv) + strlen(vendor) +
-		sizeof(*tlv) + strlen(driver);
-
-	if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
-		data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_type);
-		data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_id);
+	if (smem_rd_size &&
+	    !btintel_pcie_dump_smem_monitor_region(data, &dump_list,
+						   BTINTEL_PCIE_INI_ID_SMEM,
+						   "monitor_smem",
+						   smem_rd_addr,
+						   smem_rd_size))
+		regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SMEM);
+
+	exc_addr = data->dump_info.exception_dump_addr;
+	exception_dump_len = data->dump_info.exception_dump_len;
+	if (exc_addr && exception_dump_len) {
+		ret = btintel_pcie_dump_target_region(data, &dump_list,
+						      BTINTEL_PCIE_INI_ID_EXCEPTION_EVT,
+						      "EXCEPTION_EVT_BUFFER", exc_addr,
+						      exception_dump_len);
+		if (!ret)
+			regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_EXCEPTION_EVT);
 	}
 
-	/*
-	 * sizeof(u32) - signature
-	 * sizeof(data_len) - to store tlv data size
-	 * data_len - TLV data
-	 */
-	dump_size = sizeof(u32) + sizeof(data_len) + data_len;
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_DCCM, "DCCM",
+				    data->dump_info.dccm_addr_start,
+				    data->dump_info.dccm_addr_end,
+				    &regions_mask);
+
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_SDS, "SDS",
+				    data->dump_info.sds_start_addr_start,
+				    data->dump_info.sds_start_addr_end,
+				    &regions_mask);
+
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_SDS_IOSF, "SDS_IOSF",
+				    data->dump_info.sds_iosf_data_addr_start,
+				    data->dump_info.sds_iosf_data_addr_end,
+				    &regions_mask);
+
+	btintel_pcie_dump_mem_range(data, &dump_list,
+				    BTINTEL_PCIE_INI_ID_ECL, "ECL_REGION",
+				    data->dump_info.ecl_addr_start,
+				    data->dump_info.ecl_addr_end,
+				    &regions_mask);
+
+	ret = btintel_pcie_dump_info(data, &dump_list, regions_mask);
+	if (ret) {
+		btintel_pcie_dump_list_free(&dump_list);
+		return ret;
+	}
 
+	file_len = sizeof(*file_hdr) +
+		   btintel_pcie_dump_list_total_size(&dump_list);
+	entry = btintel_pcie_dump_entry_alloc(sizeof(*file_hdr));
+	if (!entry) {
+		btintel_pcie_dump_list_free(&dump_list);
+		return -ENOMEM;
+	}
 
-	/* Add debug buffers data length to dump size */
-	dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
+	file_hdr = (void *)entry->data;
+	file_hdr->barker = cpu_to_le32(BTINTEL_PCIE_INI_ERROR_DUMP_BARKER);
+	file_hdr->file_len = cpu_to_le32(file_len);
+	list_add(&entry->list, &dump_list);
 
-	pdata = vmalloc(dump_size);
-	if (!pdata)
-		return -ENOMEM;
-	p = pdata;
-
-	*(u32 *)p = BTINTEL_PCIE_MAGIC_NUM;
-	p += sizeof(u32);
-
-	*(u32 *)p = data_len;
-	p += sizeof(u32);
-
-
-	p = btintel_pcie_copy_tlv(p, BTINTEL_VENDOR, vendor, strlen(vendor));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_DRIVER, driver, strlen(driver));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_DUMP_TIME, ts, strlen(ts));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_FW_BUILD, fw_build,
-				  strlen(fw_build));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_BT, &data->dmp_hdr.cnvi_bt,
-				  sizeof(data->dmp_hdr.cnvi_bt));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_WRITE_PTR, &data->dmp_hdr.write_ptr,
-				  sizeof(data->dmp_hdr.write_ptr));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
-				  sizeof(data->dmp_hdr.wrap_ctr));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
-				  sizeof(data->dmp_hdr.trigger_reason));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_sha,
-				  sizeof(data->dmp_hdr.fw_sha));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVR_TOP, &data->dmp_hdr.cnvr_top,
-				  sizeof(data->dmp_hdr.cnvr_top));
-	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
-				  sizeof(data->dmp_hdr.cnvi_top));
-
-	if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
-		p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_TYPE,
-					  &data->dmp_hdr.event_type,
-					  sizeof(data->dmp_hdr.event_type));
-		p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_ID,
-					  &data->dmp_hdr.event_id,
-					  sizeof(data->dmp_hdr.event_id));
-		data->dmp_hdr.event_type = 0;
-		data->dmp_hdr.event_id = 0;
-	}
-
-	memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
-	dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
-	return 0;
+	sg_dump_data = btintel_pcie_alloc_sgtable(file_len);
+	if (sg_dump_data) {
+		int sg_entries = sg_nents(sg_dump_data);
+		u32 offs = 0;
+
+		list_for_each_entry(entry, &dump_list, list) {
+			sg_pcopy_from_buffer(sg_dump_data, sg_entries,
+					     entry->data, entry->size, offs);
+			offs += entry->size;
+		}
+
+		bt_dev_dbg(hdev, "triggering dev_coredumpsg()");
+		dev_coredumpsg(&hdev->dev, sg_dump_data, file_len, GFP_KERNEL);
+	} else {
+		bt_dev_err(hdev, "Failed to allocate scatter-gather table for coredump");
+		ret = -ENOMEM;
+	}
+
+	btintel_pcie_dump_list_free(&dump_list);
+	return ret;
 }
 
 static void btintel_pcie_dump_traces(struct hci_dev *hdev)
 {
 	struct btintel_pcie_data *data = hci_get_drvdata(hdev);
-	int ret = 0;
+	int ret;
 
 	ret = btintel_pcie_get_mac_access(data);
 	if (ret) {
@@ -923,7 +1445,7 @@ static void btintel_pcie_dump_traces(struct hci_dev *hdev)
 		return;
 	}
 
-	ret = btintel_pcie_read_dram_buffers(data);
+	ret = btintel_pcie_read_debug_regions(data);
 
 	btintel_pcie_release_mac_access(data);
 
@@ -1168,19 +1690,19 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 	ptr = buffer;
 	remaining = buffer_len;
 
-	/* Parse TLV structures: type(1) + length(2) + value */
+	/* Parse TLV structures: 1 byte type + 2 bytes length +
+	 * variable value
+	 */
 	while (remaining >= sizeof(struct mbox_tlv)) {
 		u16 tlv_len;
 		u32 tlv_total;
 
 		tlv = (struct mbox_tlv *)ptr;
 		tlv_len = le16_to_cpu(tlv->len);
-		tlv_total = sizeof(tlv->type) +
-			    sizeof(tlv->len) + tlv_len;
+		tlv_total = sizeof(tlv->type) + sizeof(tlv->len) + tlv_len;
 
 		if (tlv_total > remaining) {
-			bt_dev_err(data->hdev,
-				   "TLV parse error: type=%u, len=%u",
+			bt_dev_err(data->hdev, "TLV parse error: not enough data for TLV value (type=%u, len=%u)",
 				   tlv->type, tlv_len);
 			break;
 		}
@@ -1189,7 +1711,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_EXCEPTION_DUMP_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1202,7 +1724,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1221,8 +1743,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 				bt_dev_dbg(data->hdev, "SDS TLV: skipped, hw_variant not yet known");
 				break;
 			}
-			if (tlv_len == 16 &&
-			    hw_variant > BTINTEL_HWID_BZRI) {
+			if (tlv_len == 16 && hw_variant > BTINTEL_HWID_BZRI) {
 				data->dump_info.sds_start_addr_start =
 					get_unaligned_le32(&tlv->val[0]);
 				data->dump_info.sds_start_addr_end =
@@ -1255,7 +1776,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1268,7 +1789,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 		case BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS:
 			if (tlv_len < 8) {
 				bt_dev_err(data->hdev,
-					   "TLV %s too short: %u",
+					   "TLV %s too short: len=%u (need 8)",
 					   btintel_pcie_tlv_str(tlv->type),
 					   tlv_len);
 				break;
@@ -1279,8 +1800,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
 				get_unaligned_le32(&tlv->val[4]);
 			break;
 		default:
-			bt_dev_dbg(data->hdev,
-				   "Unknown TLV type: %u length: %u",
+			bt_dev_dbg(data->hdev, "Unknown TLV type: %u length: %u",
 				   tlv->type, tlv_len);
 			break;
 		}
@@ -1868,11 +2388,8 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
 		goto exit_on_error;
 
 	evt = (void *)buf;
-	data->dmp_hdr.event_type = evt->event_type;
-	data->dmp_hdr.event_id = le16_to_cpu(evt->event_id);
-
 	bt_dev_dbg(data->hdev, "event type: 0x%2.2x event id: 0x%4.4x len: %u",
-		   data->dmp_hdr.event_type, data->dmp_hdr.event_id, len);
+		   evt->event_type, le16_to_cpu(evt->event_id), len);
 
 	skb = bt_skb_alloc(len, GFP_KERNEL);
 	if (!skb) {
@@ -1898,8 +2415,8 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
  *
  * Always queue this AFTER any companion event-reader work (hwexp /
  * fwtrigger) so that, on the ordered @dump_workqueue, the event reader
- * runs first and populates dmp_hdr.event_type / event_id before
- * dump_traces consumes them.
+ * runs first and the trigger metadata is populated before dump_traces
+ * consumes it.
  */
 static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data,
 					u16 trigger_reason)
@@ -1982,12 +2499,6 @@ static void btintel_pcie_coredump_worker(struct work_struct *work)
 		goto out;
 	}
 
-	if (btintel_pcie_is_mdbgc_supported(data)) {
-		bt_dev_info(data->hdev,
-			    "Skipping coredump: MDBGC dump format not supported yet");
-		goto out;
-	}
-
 	btintel_pcie_dump_traces(data->hdev);
 out:
 	/* Release guard last so a new trigger can run only after this
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 5c35e65d3e81..9d84db48a8e5 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -74,11 +74,16 @@
 #define BTINTEL_PCIE_DBGC_BASE_ADDR_SCP			(0xf0d5d500)
 #define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP	(BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x1C)
 #define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP		(BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x2C)
+#define BTINTEL_PCIE_DBGC_ALLOCATION_OFFSET		0x100
 
+#define BTINTEL_PCIE_SMEM_MAX_SIZE		(16 * 1024)
+#define BTINTEL_PCIE_REGION_MAX_SIZE		(16 * 1024 * 1024)
 #define BTINTEL_PCIE_DBG_IDX_BIT_MASK		0x0F
 #define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data)	(((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK)
 #define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK	0xFFFFFF
 
+#define BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET	4
+
 /* The DRAM buffer count, each buffer size, and
  * fragment buffer size
  */
@@ -242,7 +247,7 @@ enum {
 #define BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS        0x05
 #define BTINTEL_PCIE_TLV_TYPE_SDS_MEM_ADDRESS         0x06
 #define BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS         0x07
-#define BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS           0x08
+#define BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS	      0x08
 
 /*
  * Struct for Context Information (v2)
@@ -527,10 +532,147 @@ struct btintel_pcie_dump_header {
 	u32		wrap_ctr;
 	u16		trigger_reason;
 	int		state;
-	u8		event_type;
-	u16		event_id;
 };
 
+/* Per-fragment range descriptor for dump regions.
+ * Binary-compatible with iwl_fw_ini_error_dump_range.
+ */
+struct btintel_pcie_dump_range {
+	__le32	range_data_size;
+	union {
+		__le32	internal_base_addr;
+		__le64	dram_base_addr;
+		__le32	page_num;
+	};
+	__le32	data[];
+} __packed;
+
+/*
+ * INI region types for ini_dump_data.type field.
+ * The unified decoder dispatches parsing logic based on these values.
+ */
+#define BTINTEL_PCIE_INI_REGION_INTERNAL_BUFFER	2
+#define BTINTEL_PCIE_INI_REGION_DRAM_BUFFER	3
+#define BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY	9
+
+/* INI region IDs - used in dump header region_id field and regions_mask */
+#define BTINTEL_PCIE_INI_ID_EXCEPTION_EVT	7
+#define BTINTEL_PCIE_INI_ID_SMEM		15
+#define BTINTEL_PCIE_INI_ID_DCCM		39
+#define BTINTEL_PCIE_INI_ID_SDS			40
+#define BTINTEL_PCIE_INI_ID_SDS_IOSF		41
+#define BTINTEL_PCIE_INI_ID_ECL			42
+#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR3	61
+#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR2	62
+#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR1	63
+
+/*
+ * INI (Intel INI debug infrastructure) style dump data TLV - wraps each dump
+ * region. INI is the iwlwifi firmware debug format used by the unified decoder.
+ * Compatible with iwl_fw_ini_error_dump_data.
+ */
+struct btintel_pcie_ini_dump_data {
+	u8	type;
+	u8	sub_type;
+	u8	sub_type_ver;
+	u8	reserved;
+	__le32	len;
+	u8	data[];
+} __packed;
+
+/*
+ * INI-style region dump header.
+ * Compatible with iwl_fw_ini_error_dump_header.
+ */
+#define BTINTEL_PCIE_INI_MAX_NAME	32
+#define BTINTEL_PCIE_INI_DUMP_VER	1
+
+struct btintel_pcie_ini_dump_header {
+	__le32	version;
+	__le32	region_id;
+	__le32	num_of_ranges;
+	__le32	name_len;
+	u8	name[BTINTEL_PCIE_INI_MAX_NAME];
+};
+
+/*
+ * INI-style monitor dump - region header + monitor state.
+ * Compatible with iwl_fw_ini_monitor_dump.
+ */
+struct btintel_pcie_ini_monitor_dump {
+	struct btintel_pcie_ini_dump_header header;
+	__le32	write_ptr;
+	__le32	cycle_cnt;
+	__le32	cur_frag;
+	u8	data[];
+} __packed;
+
+/* Linked list entry for modular dump collection */
+struct btintel_pcie_dump_entry {
+	struct list_head	list;
+	u32			size;
+	u8			data[];
+};
+
+/* File-level header for coredump output.
+ * Compatible with iwl_fw_ini_dump_file_hdr.
+ * Uses IWL_FW_INI_ERROR_DUMP_BARKER (0x14789633) for decoder compatibility.
+ */
+#define BTINTEL_PCIE_INI_ERROR_DUMP_BARKER	0x14789633
+
+struct btintel_pcie_dump_file_hdr {
+	__le32	barker;
+	__le32	file_len;
+} __packed;
+
+/*
+ * Legacy-style dump data wrapper for dump info TLV.
+ * Compatible with iwl_fw_error_dump_data.
+ * Used only for the dump info entry (type=BTINTEL_PCIE_INI_DUMP_INFO_TYPE).
+ */
+struct btintel_pcie_error_dump_data {
+	__le32	type;
+	__le32	len;
+	u8	data[];
+} __packed;
+
+/* Use bit 31 as dump info type, matching IWL_INI_DUMP_INFO_TYPE */
+#define BTINTEL_PCIE_INI_DUMP_INFO_TYPE	BIT(31)
+
+/* Time point values matching iwl_fw_ini_time_point for fwdump parser */
+#define BTINTEL_PCIE_TIME_POINT_FW_ASSERT	4
+#define BTINTEL_PCIE_TIME_POINT_USER_TRIGGER	9
+
+/*
+ * Dump info struct - single TLV containing all metadata.
+ * Compatible with iwl_fw_ini_dump_info.
+ * Packs all device/firmware info that the decoder needs.
+ */
+struct btintel_pcie_ini_dump_info {
+	__le32	version;
+	__le32	time_point;
+	__le32	trigger_reason;
+	__le32	external_cfg_state;
+	__le32	ver_type;
+	__le32	ver_subtype;
+	__le32	hw_step;
+	__le32	hw_type;
+	__le32	rf_id_flavor;
+	__le32	rf_id_dash;
+	__le32	rf_id_step;
+	__le32	rf_id_type;
+	__le32	lmac_major;
+	__le32	lmac_minor;
+	__le32	umac_major;
+	__le32	umac_minor;
+	__le32	fw_mon_mode;
+	__le64	regions_mask;
+	__le32	build_tag_len;
+	u8	build_tag[64];
+	__le32	num_of_cfg_names;
+	/* no cfg_names for BT - keep zero-length */
+} __packed;
+
 /* struct btintel_pcie_data
  * @pdev: pci device
  * @hdev: hdev device
@@ -688,4 +830,3 @@ static inline u32 btintel_pcie_rd_dev_mem(struct btintel_pcie_data *data,
 	btintel_pcie_wr_reg32(data, BTINTEL_PCIE_PRPH_DEV_ADDR_REG, addr);
 	return btintel_pcie_rd_reg32(data, BTINTEL_PCIE_PRPH_DEV_RD_REG);
 }
-
-- 
2.43.0


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

* RE: [v2,3/3] Bluetooth: btintel_pcie: unified decoder coredump format
  2026-09-04  0:29 ` [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
@ 2026-09-04  0:46   ` bluez.test.bot
  2026-09-04  9:49   ` [PATCH v2 3/3] " Paul Menzel
  2026-09-04 17:50   ` patchwork-bot+bluetooth
  2 siblings, 0 replies; 15+ messages in thread
From: bluez.test.bot @ 2026-09-04  0:46 UTC (permalink / raw)
  To: linux-bluetooth, chandrashekar.devegowda

[-- Attachment #1: Type: text/plain, Size: 566 bytes --]

This is an automated email and please do not reply to this email.

Dear Submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
While preparing the CI tests, the patches you submitted couldn't be applied to the current HEAD of the repository.

----- Output -----

error: patch failed: drivers/bluetooth/btintel_pcie.c:198
error: drivers/bluetooth/btintel_pcie.c: patch does not apply
hint: Use 'git am --show-current-patch' to see the failed patch

Please resolve the issue and submit the patches again.


---
Regards,
Linux Bluetooth


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

* RE: [v2,1/3] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
  2026-09-04  0:29 ` [PATCH v2 1/3] " Chandrashekar Devegowda
@ 2026-09-04  1:10   ` bluez.test.bot
  2026-09-04 16:56   ` [PATCH v2 1/3] " patchwork-bot+bluetooth
  1 sibling, 0 replies; 15+ messages in thread
From: bluez.test.bot @ 2026-09-04  1:10 UTC (permalink / raw)
  To: linux-bluetooth, chandrashekar.devegowda

[-- Attachment #1: Type: text/plain, Size: 1472 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1157479

---Test result---

Test Summary:
CheckPatch                    PASS      0.75 seconds
VerifyFixes                   PASS      0.13 seconds
VerifySignedoff               PASS      0.10 seconds
GitLint                       FAIL      0.25 seconds
SubjectPrefix                 PASS      0.09 seconds
BuildKernel                   PASS      26.18 seconds
CheckAllWarning               PASS      28.61 seconds
CheckSparse                   PASS      27.17 seconds
BuildKernel32                 PASS      25.03 seconds
CheckKernelLLVM               SKIP      0.00 seconds
TestRunnerSetup               PASS      463.98 seconds
IncrementalBuild              PASS      24.55 seconds

Details
##############################
Test: GitLint - FAIL
Desc: Run gitlint
Output:
[v2,1/3] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition

1: T1 Title exceeds max length (84>80): "[v2,1/3] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition"
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found


https://github.com/bluez/bluetooth-next/pull/700

---
Regards,
Linux Bluetooth


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

* Re: [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format
  2026-09-04  0:29 ` [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
  2026-09-04  0:46   ` [v2,3/3] " bluez.test.bot
@ 2026-09-04  9:49   ` Paul Menzel
  2026-09-04 17:50   ` patchwork-bot+bluetooth
  2 siblings, 0 replies; 15+ messages in thread
From: Paul Menzel @ 2026-09-04  9:49 UTC (permalink / raw)
  To: Chandrashekar Devegowda
  Cc: ravishankar.srivatsa, chethan.tumkur.narayan, kiran.k,
	linux-bluetooth

Dear Chandrashekar,


Thank you for your patch.

Am 04.09.26 um 02:29 schrieb Chandrashekar Devegowda:
> Replace the legacy TLV-based coredump with a unified INI-format dump

Please mention *Intel INI debug infrastructure*, and most importantly, 
where the format is described. What does INI abbreviate?

The commit message summary/title could also be more specific, and be 
made a statement. Maybe:

Bluetooth: btintel_pcie: Convert to unified INI decoder coredump format

> compatible with the iwlwifi decoder so a single decoder can parse
> coredumps across all Intel BT PCIe controller variants.
> 
> The dump is emitted via dev_coredumpsg() as structured regions built
> from firmware-advertised addresses: DRAM monitor buffers (single or
> MDBGC multi-buffer), SMEM monitor, exception event buffer, DCCM, SDS,
> SDS IOSF, ECL and dump info metadata. Regions are collected only when
> firmware advertises a valid address and size, and only collected
> regions are advertised in regions_mask.
> 
> Add INTEL_CNVX_TOP_DASH() and INTEL_CNVX_TOP_FLAVOR() helpers to
> btintel.h for RfIdDash/RfIdFlavor extraction.

Please document how this can be tested.

> Assisted-by: Copilot:gemini-3.6-flash
> Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
> ---
> Changes in v2:
>   - Reworded the commit message for brevity.
>   - Add btintel_pcie_dump_dram_monitor() helper that reads the
>     per-allocation status/wrap registers (0x100 * allocation_id
>     offset) for each MDBGC pool, stores the per-fragment DWORD write
>     offset in mon_dump->write_ptr, and stores the active fragment
>     index in mon_dump->cur_frag, replacing the previously flattened
>     write_ptr / hard-coded cur_frag = 0 encoding.
>   - btintel_pcie_dump_target_region() now returns a skip indicator
>     (leaving regions_mask untouched) when the firmware does not
>     advertise a valid region, avoiding a spurious
>     "Skipping empty dump region: EXCEPTION_EVT_BUFFER" warning when
>     the exception dump TLV is absent. It also skips when the size
>     rounded down to 4-byte alignment collapses to zero.
>   - Log via bt_dev_dbg (instead of bt_dev_err) when firmware does
>     not advertise an SMEM monitor region, so the absence of an
>     optional TLV is not reported as an error.
> 
>   drivers/bluetooth/btintel.h      |   2 +
>   drivers/bluetooth/btintel_pcie.c | 887 ++++++++++++++++++++++++-------
>   drivers/bluetooth/btintel_pcie.h | 149 +++++-
>   3 files changed, 846 insertions(+), 192 deletions(-)
> 
> diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
> index ef232820c31b..c00f793d9dc8 100644
> --- a/drivers/bluetooth/btintel.h
> +++ b/drivers/bluetooth/btintel.h
> @@ -225,7 +225,9 @@ struct btintel_sar_rev2 {
>   #define INTEL_HW_PLATFORM(cnvx_bt)	((u8)(((cnvx_bt) & 0x0000ff00) >> 8))
>   #define INTEL_HW_VARIANT(cnvx_bt)	((u8)(((cnvx_bt) & 0x003f0000) >> 16))
>   #define INTEL_CNVX_TOP_TYPE(cnvx_top)	((cnvx_top) & 0x00000fff)
> +#define INTEL_CNVX_TOP_DASH(cnvx_top)	(((cnvx_top) & 0x00f00000) >> 20)
>   #define INTEL_CNVX_TOP_STEP(cnvx_top)	(((cnvx_top) & 0x0f000000) >> 24)
> +#define INTEL_CNVX_TOP_FLAVOR(cnvx_top)	(((cnvx_top) & 0xf0000000) >> 28)
>   #define INTEL_CNVX_TOP_PACK_SWAB(t, s)	__swab16(((__u16)(((t) << 4) | (s))))
>   
>   enum {
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 233cb8a3cbd4..38a6dbd04dcf 100644
> --- a/drivers/bluetooth/btintel_pcie.c
> +++ b/drivers/bluetooth/btintel_pcie.c
> @@ -19,6 +19,7 @@
>   
>   #include <linux/unaligned.h>
>   #include <linux/devcoredump.h>
> +#include <linux/scatterlist.h>
>   
>   #include <net/bluetooth/bluetooth.h>
>   #include <net/bluetooth/hci_core.h>
> @@ -198,7 +199,15 @@ static inline bool btintel_pcie_dbg_to_wifi(struct btintel_pcie_data *data)
>   	return data->dbg_path_cache != BTINTEL_PCIE_DRAM;
>   }
>   
> -/* Helper function to allocate and setup a debug buffer group */
> +/* Helper function to allocate and setup a debug buffer group
> + * @data: driver data structure
> + * @buf: pointer to data_buf array pointer
> + * @p_addr: pointer to physical DMA address
> + * @v_addr: pointer to virtual address
> + * @frag: pointer to fragment buffer array
> + * @buf_index: buffer index (for error messages)
> + * @buf_count: number of buffers to allocate
> + */
>   static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
>   				       struct data_buf **buf,
>   				       dma_addr_t *p_addr,
> @@ -213,39 +222,36 @@ static int btintel_pcie_alloc_dbgc_buf(struct btintel_pcie_data *data,
>   	*buf = devm_kcalloc(&data->pdev->dev, buf_count,
>   			    sizeof(**buf), GFP_KERNEL);
>   	if (!*buf) {
> -		BT_ERR("Failed to allocate dbgc buf: %u",
> -		       buf_index + 1);
> +		BT_ERR("Failed to allocate dbgc buf: %u", buf_index + 1);
>   		return -ENOMEM;
>   	}
>   
>   	*v_addr = dmam_alloc_coherent(&data->pdev->dev,
>   				      buf_count *
> -				      BTINTEL_PCIE_DBGC_BUFFER_SIZE,
> -				      p_addr,
> -				      GFP_KERNEL | __GFP_NOWARN);
> +					BTINTEL_PCIE_DBGC_BUFFER_SIZE,
> +					p_addr,
> +					GFP_KERNEL | __GFP_NOWARN);
>   	if (!*v_addr) {
> -		BT_ERR("Failed to allocate dbgc buf: %u DMA",
> -		       buf_index + 1);
> +		BT_ERR("Failed to allocate dbgc buf: %u DMA", buf_index + 1);
>   		return -ENOMEM;
>   	}
>   
>   	for (i = 0; i < buf_count; i++) {
>   		b = &(*buf)[i];
> -		b->data_p_addr = *p_addr +
> -				 i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
> -		b->data = *v_addr +
> -			  i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
> -		frag[i].buf_addr_lsb =
> -			lower_32_bits(b->data_p_addr);
> -		frag[i].buf_addr_msb =
> -			upper_32_bits(b->data_p_addr);
> +		b->data_p_addr = *p_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
> +		b->data = *v_addr + i * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
> +		frag[i].buf_addr_lsb = lower_32_bits(b->data_p_addr);
> +		frag[i].buf_addr_msb = upper_32_bits(b->data_p_addr);
>   		frag[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
>   	}
>   
>   	return 0;
>   }
>   
> -/* This function initializes the memory for MDBGC buffers */
> +/* This function initializes the memory for MDBGC buffers and formats the
> + * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
> + * size as the payload
> + */
>   static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
>   {
>   	struct btintel_pcie_mdbgc_ctxt db_frag;
> @@ -255,15 +261,19 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
>   
>   	data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
>   
> +	/* Allocate fragment context structure */

I find the comment redundant. If it should be added, to reduce the 
diffstat size, it’d be great to move the addition into a separate commit.

>   	frag_v_addr = dmam_alloc_coherent(&data->pdev->dev, frag_size,
>   					  &data->mdbgc.frag_p_addr,
>   					  GFP_KERNEL | __GFP_NOWARN);
> -	if (!frag_v_addr)
> +	if (!frag_v_addr) {
> +		BT_ERR("Failed to allocate mdbgc context");
>   		return -ENOMEM;
> +	}

Ditto regarding a separate commit to make review easier. (A possible 
revert would also loose the newly added messages.)

>   
>   	data->mdbgc.frag_v_addr = frag_v_addr;
>   	data->mdbgc.frag_size = frag_size;
>   
> +	/* Initialize fragment header */
>   	memset(&db_frag, 0, sizeof(db_frag));
>   	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
>   	db_frag.ver = BTINTEL_PCIE_MDBGC_FRAG_VERSION;
> @@ -275,7 +285,8 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
>   					  &data->mdbgc.buf1,
>   					  &data->mdbgc.buf1_p_addr,
>   					  &data->mdbgc.buf1_v_addr,
> -					  db_frag.buf1, 0,
> +					  db_frag.buf1,
> +					  0,
>   					  data->mdbgc.count);
>   	if (err)
>   		return err;
> @@ -287,7 +298,8 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
>   					  &data->mdbgc.buf2,
>   					  &data->mdbgc.buf2_p_addr,
>   					  &data->mdbgc.buf2_v_addr,
> -					  db_frag.buf2, 1,
> +					  db_frag.buf2,
> +					  1,
>   					  data->mdbgc.count);
>   	if (err)
>   		return err;
> @@ -299,58 +311,64 @@ static int btintel_pcie_setup_mdbgc(struct btintel_pcie_data *data)
>   					  &data->mdbgc.buf3,
>   					  &data->mdbgc.buf3_p_addr,
>   					  &data->mdbgc.buf3_v_addr,
> -					  db_frag.buf3, 2,
> +					  db_frag.buf3,
> +					  2,
>   					  data->mdbgc.count);
>   	if (err)
>   		return err;
>   
> +	/* Copy fragment to DMA coherent memory */
>   	memcpy(data->mdbgc.frag_v_addr, &db_frag, sizeof(db_frag));
>   	return 0;
>   }
>   
> -/* This function initializes the memory for DBGC buffers */
> +/* This function initializes the memory for DBGC buffers and formats the
> + * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
> + * size as the payload
> + */
>   static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
>   {
>   	struct btintel_pcie_dbgc_ctxt db_frag;
> +	u32 frag_size = sizeof(db_frag);
>   	int err;
>   
>   	data->dbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
>   
> +	/* Allocate fragment context structure */
>   	data->dbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
> -				sizeof(struct btintel_pcie_dbgc_ctxt),
> -				&data->dbgc.frag_p_addr,
> -				GFP_KERNEL | __GFP_NOWARN);
> -	if (!data->dbgc.frag_v_addr)
> +						     frag_size,
> +						     &data->dbgc.frag_p_addr,
> +						     GFP_KERNEL | __GFP_NOWARN);
> +	if (!data->dbgc.frag_v_addr) {
> +		BT_ERR("Failed to allocate dbgc context");
>   		return -ENOMEM;
> +	}
>   
>   	data->dbgc.frag_size = sizeof(struct btintel_pcie_dbgc_ctxt);
>   
> +	/* Initialize fragment header */
>   	memset(&db_frag, 0, sizeof(db_frag));
>   	db_frag.magic_num = BTINTEL_PCIE_MAGIC_NUM;
>   	db_frag.ver = BTINTEL_PCIE_DBGC_FRAG_VERSION;
>   	db_frag.total_size = BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE;
>   	db_frag.num_buf = BTINTEL_PCIE_DBGC_FRAG_BUFFER_COUNT;
>   
> +	/* Allocate DBGC buffers */
>   	err = btintel_pcie_alloc_dbgc_buf(data,
>   					  &data->dbgc.bufs,
>   					  &data->dbgc.buf_p_addr,
>   					  &data->dbgc.buf_v_addr,
> -					  db_frag.bufs, 0,
> +					  db_frag.bufs,
> +					  0,
>   					  data->dbgc.count);
>   	if (err)
>   		return err;
>   
> +	/* Copy fragment to DMA coherent memory */
>   	memcpy(data->dbgc.frag_v_addr, &db_frag, sizeof(db_frag));
>   	return 0;
>   }
>   
> -static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
> -{
> -	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
> -		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
> -		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
> -}
> -
>   static inline void ipc_print_ia_ring(struct hci_dev *hdev, struct ia *ia,
>   				     u16 queue_num)
>   {
> @@ -753,169 +771,673 @@ static void btintel_pcie_release_mac_access(struct btintel_pcie_data *data)
>   	}
>   }
>   
> -static void *btintel_pcie_copy_tlv(void *dest, enum btintel_pcie_tlv_type type,
> -				   void *data, size_t size)
> +static struct scatterlist *btintel_pcie_alloc_sgtable(ssize_t size)
>   {
> -	struct intel_tlv *tlv;
> +	int nents, i;
> +	struct page *page;
> +	struct scatterlist *sg, *result;
> +
> +	if (size <= 0)
> +		return NULL;
> +
> +	nents = DIV_ROUND_UP(size, PAGE_SIZE);
> +	result = kcalloc(nents, sizeof(*result), GFP_KERNEL);
> +	if (!result)
> +		return NULL;
> +
> +	sg_init_table(result, nents);
> +	sg = result;
>   
> -	tlv = dest;
> -	tlv->type = type;
> -	tlv->len = size;
> -	memcpy(tlv->val, data, tlv->len);
> -	return dest + sizeof(*tlv) + size;
> +	for (i = 0; size > 0; i++) {
> +		ssize_t bytes = min_t(ssize_t, size, PAGE_SIZE);
> +
> +		page = alloc_page(GFP_KERNEL | __GFP_ZERO);
> +		if (!page)
> +			goto err_free;
> +
> +		sg_set_page(sg, page, bytes, 0);
> +		sg = sg_next(sg);
> +		size -= bytes;
> +	}
> +	return result;
> +
> +err_free:
> +	for (sg = result; sg; sg = sg_next(sg)) {
> +		page = sg_page(sg);
> +		if (page)
> +			__free_page(page);
> +	}
> +	kfree(result);
> +	return NULL;
>   }
>   
> -static int btintel_pcie_read_dram_buffers(struct btintel_pcie_data *data)
> +static struct btintel_pcie_dump_entry *
> +btintel_pcie_dump_entry_alloc(u32 data_size)
>   {
> -	u32 offset, prev_size, wr_ptr_status, dump_size, data_len;
> -	u32 status_reg, wrap_reg;
> -	struct btintel_pcie_dbgc *dbgc = &data->dbgc;
> +	struct btintel_pcie_dump_entry *entry;
> +
> +	entry = vzalloc(sizeof(*entry) + data_size);
> +	if (!entry)
> +		return NULL;
> +
> +	entry->size = data_size;
> +	INIT_LIST_HEAD(&entry->list);
> +	return entry;
> +}
> +
> +static void btintel_pcie_dump_list_free(struct list_head *list)
> +{
> +	struct btintel_pcie_dump_entry *entry, *tmp;
> +
> +	list_for_each_entry_safe(entry, tmp, list, list) {
> +		list_del(&entry->list);
> +		vfree(entry);
> +	}
> +}
> +
> +static u32 btintel_pcie_dump_list_total_size(struct list_head *list)
> +{
> +	struct btintel_pcie_dump_entry *entry;
> +	u32 total = 0;
> +
> +	list_for_each_entry(entry, list, list)
> +		total += entry->size;
> +
> +	return total;
> +}
> +
> +static int btintel_pcie_dump_dram(struct list_head *list,
> +				  u8 count, struct data_buf *bufs,
> +				  u32 write_ptr, u32 wrap_ctr, u32 cur_frag,
> +				  u32 region_id, const char *name)
> +{
> +	struct btintel_pcie_dump_entry *entry;
> +	struct btintel_pcie_ini_dump_data *dump_data;
> +	struct btintel_pcie_ini_monitor_dump *mon_dump;
> +	struct btintel_pcie_dump_range *range;
> +	u32 mon_hdr_size, ranges_size, payload_size, total_size;
> +	int i;
> +
> +	mon_hdr_size = sizeof(*mon_dump);
> +	ranges_size = count * (sizeof(*range) + BTINTEL_PCIE_DBGC_BUFFER_SIZE);
> +	payload_size = mon_hdr_size + ranges_size;
> +	total_size = sizeof(*dump_data) + payload_size;
> +
> +	entry = btintel_pcie_dump_entry_alloc(total_size);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	dump_data = (void *)entry->data;
> +	dump_data->type = BTINTEL_PCIE_INI_REGION_DRAM_BUFFER;
> +	dump_data->sub_type = 0;
> +	dump_data->sub_type_ver = 0;
> +	dump_data->reserved = 0;
> +	dump_data->len = cpu_to_le32(payload_size);
> +
> +	mon_dump = (void *)dump_data->data;
> +	mon_dump->header.version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
> +	mon_dump->header.region_id = cpu_to_le32(region_id);
> +	mon_dump->header.num_of_ranges = cpu_to_le32(count);
> +	mon_dump->header.name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
> +	memset(mon_dump->header.name, 0, BTINTEL_PCIE_INI_MAX_NAME);
> +	strscpy(mon_dump->header.name, name, BTINTEL_PCIE_INI_MAX_NAME);
> +
> +	mon_dump->write_ptr = cpu_to_le32(write_ptr);
> +	mon_dump->cycle_cnt = cpu_to_le32(wrap_ctr);
> +	mon_dump->cur_frag = cpu_to_le32(cur_frag);
> +
> +	range = (void *)mon_dump->data;
> +	for (i = 0; i < count; i++) {
> +		range->range_data_size =
> +			cpu_to_le32(BTINTEL_PCIE_DBGC_BUFFER_SIZE);
> +		range->dram_base_addr = cpu_to_le64(bufs[i].data_p_addr);
> +		memcpy(range->data, bufs[i].data,
> +		       BTINTEL_PCIE_DBGC_BUFFER_SIZE);
> +		range = (void *)range->data + BTINTEL_PCIE_DBGC_BUFFER_SIZE;
> +	}
> +
> +	list_add_tail(&entry->list, list);
> +	return 0;
> +}
> +
> +static int
> +btintel_pcie_dump_dram_monitor(struct btintel_pcie_data *data,
> +			       struct list_head *list, u8 count,
> +			       struct data_buf *bufs, u32 status_reg,
> +			       u32 wrap_reg, u8 allocation_id, u32 region_id,
> +			       const char *name)
> +{
> +	u32 allocation_offset, write_ptr, wrap_ctr, wr_ptr_status;
> +	u8 cur_frag;
> +
> +	allocation_offset = allocation_id *
> +			    BTINTEL_PCIE_DBGC_ALLOCATION_OFFSET;
> +	wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg +
> +						allocation_offset);
> +	wrap_ctr = btintel_pcie_rd_dev_mem(data, wrap_reg +
> +					  allocation_offset);
> +
> +	write_ptr = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
> +	write_ptr >>= 2;
> +	cur_frag = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
> +	if (cur_frag >= count) {
> +		bt_dev_warn(data->hdev,
> +			    "Invalid DRAM monitor fragment %u for allocation %u",
> +			    cur_frag, allocation_id);
> +		return -EINVAL;
> +	}
> +
> +	bt_dev_dbg(data->hdev,
> +		   "allocation=%u wr_ptr_status=0x%08x write_ptr=0x%06x cur_frag=%u wrap_ctr=0x%08x",
> +		   allocation_id, wr_ptr_status, write_ptr, cur_frag, wrap_ctr);
> +
> +	return btintel_pcie_dump_dram(list, count, bufs, write_ptr, wrap_ctr,
> +				      cur_frag, region_id, name);
> +}
> +
> +static int btintel_pcie_dump_target_region(struct btintel_pcie_data *data,
> +					   struct list_head *list,
> +					   u32 region_id, const char *name,
> +					   u32 addr_start, u32 size)
> +{
> +	struct btintel_pcie_dump_entry *entry;
> +	struct btintel_pcie_ini_dump_data *dump_data;
> +	struct btintel_pcie_ini_dump_header *hdr;
> +	struct btintel_pcie_dump_range *range;
> +	u32 payload_size, total_size, target_mem_offset, tempdata;
> +	u8 *dest;
> +	int i;
> +
> +	if (!size) {
> +		bt_dev_warn(data->hdev, "Skipping empty dump region: %s", name);
> +		return 1;
> +	}
> +
> +	if (!addr_start) {
> +		bt_dev_warn(data->hdev,
> +			    "Skipping dump region with zero address: %s",
> +			    name);
> +		return 1;
> +	}
> +
> +	if (size > BTINTEL_PCIE_REGION_MAX_SIZE) {
> +		bt_dev_warn(data->hdev,
> +			    "Skipping dump region %s: size %u exceeds max %u",
> +			    name, size, BTINTEL_PCIE_REGION_MAX_SIZE);
> +		return 1;
> +	}
> +
> +	if (addr_start > U32_MAX - size) {
> +		bt_dev_warn(data->hdev,
> +			    "Skipping dump region %s: addr_start 0x%08x + size %u would overflow",
> +			    name, addr_start, size);
> +		return 1;
> +	}
> +
> +	/* Align to 4 bytes - target access reads 32-bit words */
> +	size = round_down(size, sizeof(u32));
> +	if (!size) {
> +		bt_dev_warn(data->hdev,
> +			    "Skipping dump region %s: size is smaller than 4-byte access",
> +			    name);
> +		return 1;
> +	}
> +
> +	bt_dev_dbg(data->hdev,
> +		   "Target access: region=%s start=0x%08x size=%u",
> +		   name, addr_start, size);
> +
> +	payload_size = sizeof(*hdr) + sizeof(*range) + size;
> +	total_size = sizeof(*dump_data) + payload_size;
> +	entry = btintel_pcie_dump_entry_alloc(total_size);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	dump_data = (void *)entry->data;
> +	dump_data->type = BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY;
> +	dump_data->sub_type = 0;
> +	dump_data->sub_type_ver = 0;
> +	dump_data->reserved = 0;
> +	dump_data->len = cpu_to_le32(payload_size);
> +
> +	hdr = (void *)dump_data->data;
> +	hdr->version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
> +	hdr->region_id = cpu_to_le32(region_id);
> +	hdr->num_of_ranges = cpu_to_le32(1);
> +	hdr->name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
> +	memset(hdr->name, 0, BTINTEL_PCIE_INI_MAX_NAME);
> +	strscpy(hdr->name, name, BTINTEL_PCIE_INI_MAX_NAME);
> +
> +	range = (void *)(hdr + 1);
> +	range->range_data_size = cpu_to_le32(size);
> +	range->internal_base_addr = cpu_to_le32(addr_start);
> +
> +	dest = (u8 *)range->data;
> +	target_mem_offset = size / sizeof(u32);
> +	for (i = 0; i < target_mem_offset; i++) {
> +		u32 offset = BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET * i;
> +
> +		tempdata = btintel_pcie_rd_dev_mem(data,
> +						   addr_start + offset);
> +		memcpy(dest, &tempdata, sizeof(tempdata));
> +		dest += sizeof(tempdata);
> +	}
> +
> +	list_add_tail(&entry->list, list);
> +	return 0;
> +}
> +
> +static int btintel_pcie_dump_smem_monitor_region(struct btintel_pcie_data *data,
> +						 struct list_head *list,
> +						 u32 region_id,
> +						 const char *name,
> +						 u32 addr_start, u32 size)
> +{
> +	struct btintel_pcie_dump_entry *entry;
> +	struct btintel_pcie_ini_dump_data *dump_data;
> +	struct btintel_pcie_ini_monitor_dump *mon;
> +	struct btintel_pcie_dump_range *range;
> +	u32 payload_size, total_size, target_mem_offset, tempdata;
> +	u8 *dest;
> +	int i;
> +
> +	if (!size || !addr_start) {
> +		bt_dev_err(data->hdev, "Skipping smem dump: size = %u addr = %8.8x",
> +			   size, addr_start);
> +		return -EINVAL;
> +	}
> +
> +	payload_size = sizeof(*mon) + sizeof(*range) + size;
> +	total_size = sizeof(*dump_data) + payload_size;
> +	entry = btintel_pcie_dump_entry_alloc(total_size);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	dump_data = (void *)entry->data;
> +	dump_data->type = BTINTEL_PCIE_INI_REGION_INTERNAL_BUFFER;
> +	dump_data->sub_type = 0;
> +	dump_data->sub_type_ver = 0;
> +	dump_data->reserved = 0;
> +	dump_data->len = cpu_to_le32(payload_size);
> +
> +	mon = (void *)dump_data->data;
> +	mon->header.version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
> +	mon->header.region_id = cpu_to_le32(region_id);
> +	mon->header.num_of_ranges = cpu_to_le32(1);
> +	mon->header.name_len = cpu_to_le32(BTINTEL_PCIE_INI_MAX_NAME);
> +	memset(mon->header.name, 0, BTINTEL_PCIE_INI_MAX_NAME);
> +	strscpy(mon->header.name, name, BTINTEL_PCIE_INI_MAX_NAME);
> +
> +	mon->write_ptr = cpu_to_le32(0);
> +	mon->cycle_cnt = cpu_to_le32(0);
> +	mon->cur_frag = cpu_to_le32(0);
> +
> +	range = (void *)mon->data;
> +	range->range_data_size = cpu_to_le32(size);
> +	range->internal_base_addr = cpu_to_le32(addr_start);
> +
> +	dest = (u8 *)range->data;
> +	target_mem_offset = size / sizeof(u32);
> +	for (i = 0; i < target_mem_offset; i++) {
> +		u32 offset = BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET * i;
> +
> +		tempdata = btintel_pcie_rd_dev_mem(data,
> +						   addr_start + offset);
> +		memcpy(dest, &tempdata, sizeof(tempdata));
> +		dest += sizeof(tempdata);
> +	}
> +
> +	list_add_tail(&entry->list, list);
> +	return 0;
> +}
> +
> +static int btintel_pcie_dump_info(struct btintel_pcie_data *data,
> +				  struct list_head *list,
> +				  u64 regions_mask)
> +{
> +	struct btintel_pcie_dump_entry *entry;
> +	struct btintel_pcie_error_dump_data *tlv;
> +	struct btintel_pcie_ini_dump_info *dump;
> +	u32 size = sizeof(*tlv) + sizeof(*dump);
> +	char build_tag[64];
> +
> +	entry = btintel_pcie_dump_entry_alloc(size);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	tlv = (void *)entry->data;
> +	tlv->type = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_INFO_TYPE);
> +	tlv->len = cpu_to_le32(sizeof(*dump));
> +
> +	dump = (void *)tlv->data;
> +	memset(dump, 0, sizeof(*dump));
> +
> +	dump->version = cpu_to_le32(BTINTEL_PCIE_INI_DUMP_VER);
> +	dump->trigger_reason = cpu_to_le32(data->dmp_hdr.trigger_reason);
> +
> +	if (data->dmp_hdr.trigger_reason ==
> +	    BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT)
> +		dump->time_point =
> +			cpu_to_le32(BTINTEL_PCIE_TIME_POINT_FW_ASSERT);
> +	else if (data->dmp_hdr.trigger_reason ==
> +		 BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER)
> +		dump->time_point =
> +			cpu_to_le32(BTINTEL_PCIE_TIME_POINT_USER_TRIGGER);
> +
> +	dump->hw_type = cpu_to_le32(INTEL_CNVX_TOP_TYPE(data->dmp_hdr.cnvi_top));
> +	dump->hw_step = cpu_to_le32(INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvi_top));
> +	bt_dev_dbg(data->hdev, "hw_type=0x%x hw_step=0x%x (cnvi_top=0x%x)",
> +		   le32_to_cpu(dump->hw_type), le32_to_cpu(dump->hw_step),
> +		   data->dmp_hdr.cnvi_top);
> +
> +	dump->ver_type = cpu_to_le32(data->dmp_hdr.cnvi_bt);
> +	dump->ver_subtype = cpu_to_le32(data->dmp_hdr.fw_sha);
> +	dump->rf_id_type = cpu_to_le32(INTEL_CNVX_TOP_TYPE(data->dmp_hdr.cnvr_top));
> +	dump->rf_id_dash = cpu_to_le32(INTEL_CNVX_TOP_DASH(data->dmp_hdr.cnvr_top));
> +	dump->rf_id_step = cpu_to_le32(INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvr_top));
> +	dump->rf_id_flavor = cpu_to_le32(INTEL_CNVX_TOP_FLAVOR(data->dmp_hdr.cnvr_top));
> +	bt_dev_dbg(data->hdev,
> +		   "rf_id_type=0x%x rf_id_dash=0x%x rf_id_step=0x%x rf_id_flavor=0x%x (cnvr_top=0x%x)",
> +		   le32_to_cpu(dump->rf_id_type),
> +		   le32_to_cpu(dump->rf_id_dash),
> +		   le32_to_cpu(dump->rf_id_step),
> +		   le32_to_cpu(dump->rf_id_flavor),
> +		   data->dmp_hdr.cnvr_top);
> +	dump->lmac_major = cpu_to_le32(0);
> +	dump->lmac_minor = cpu_to_le32(0);
> +	dump->umac_major = cpu_to_le32(0);
> +	dump->umac_minor = cpu_to_le32(0);
> +	dump->fw_mon_mode = cpu_to_le32(BTINTEL_PCIE_FW_MON_MODE_DRAM);
> +
> +	dump->regions_mask = cpu_to_le64(regions_mask);
> +
> +	bt_dev_dbg(data->hdev, "ExpectedRegionIDs regions_mask=0x%016llx",
> +		   le64_to_cpu(dump->regions_mask));
> +
> +	snprintf(build_tag, sizeof(build_tag), "%08X", data->dmp_hdr.fw_sha);
> +
> +	dump->build_tag_len = cpu_to_le32(strlen(build_tag));
> +	memcpy(dump->build_tag, build_tag, min(strlen(build_tag),
> +					       sizeof(dump->build_tag)));
> +
> +	dump->num_of_cfg_names = cpu_to_le32(0);
> +
> +	bt_dev_dbg(data->hdev,
> +		   "HwType=0x%08x HwStep=0x%08x RfIdType=0x%08x RfIdDash=0x%08x RfIdStep=0x%08x RfIdFlavor=0x%08x",
> +		   le32_to_cpu(dump->hw_type), le32_to_cpu(dump->hw_step),
> +		   le32_to_cpu(dump->rf_id_type),
> +		   le32_to_cpu(dump->rf_id_dash),
> +		   le32_to_cpu(dump->rf_id_step),
> +		   le32_to_cpu(dump->rf_id_flavor));
> +	bt_dev_dbg(data->hdev, "VerType=0x%08x VerSubType=0x%08x",
> +		   le32_to_cpu(dump->ver_type), le32_to_cpu(dump->ver_subtype));
> +	bt_dev_dbg(data->hdev,
> +		   "LmacMajor=0x%08x LmacMinor=0x%08x UmacMajor=0x%08x UmacMinor=0x%08x",
> +		   le32_to_cpu(dump->lmac_major), le32_to_cpu(dump->lmac_minor),
> +		   le32_to_cpu(dump->umac_major),
> +		   le32_to_cpu(dump->umac_minor));
> +	bt_dev_dbg(data->hdev, "TriggerReason=0x%04x MonMode=%u BuildTag=%.64s",
> +		   le32_to_cpu(dump->trigger_reason),
> +		   le32_to_cpu(dump->fw_mon_mode),
> +		   dump->build_tag);
> +
> +	list_add(&entry->list, list);
> +
> +	return 0;
> +}
> +
> +static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data)
> +{
> +	return data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_S_SCP2 ||
> +		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_NVL_Hx_SCP2 ||
> +		data->pdev->device == BTINTEL_PCIE_DEVICE_ID_PTL_FMP2;
> +}
> +
> +static void btintel_pcie_dump_mem_range(struct btintel_pcie_data *data,
> +					struct list_head *list, u32 region_id,
> +					const char *name, u32 addr_start,
> +					u32 addr_end, u64 *regions_mask)
> +{
> +	u32 region_size;
> +
> +	if (!addr_start || !addr_end || addr_end < addr_start)
> +		return;
> +
> +	if (addr_end > U32_MAX - 0x04) {
> +		bt_dev_warn(data->hdev,
> +			    "Skipping dump region %s: addr_end 0x%08x would overflow",
> +			    name, addr_end);
> +		return;
> +	}
> +
> +	region_size = (addr_end + 0x04) - addr_start;
> +	if (region_size > BTINTEL_PCIE_REGION_MAX_SIZE) {
> +		bt_dev_warn(data->hdev,
> +			    "Skipping dump region %s: size %u exceeds max %u",
> +			    name, region_size,
> +			    BTINTEL_PCIE_REGION_MAX_SIZE);
> +		return;
> +	}
> +
> +	if (!btintel_pcie_dump_target_region(data, list, region_id, name,
> +					     addr_start, region_size))
> +		*regions_mask |= BIT_ULL(region_id);
> +}
> +
> +static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data)
> +{
> +	struct btintel_pcie_dbgc *dbgc = NULL;
> +	struct btintel_pcie_mdbgc *mdbgc = NULL;
>   	struct hci_dev *hdev = data->hdev;
> -	u8 *pdata, *p, buf_idx, hw_variant;
> -	struct intel_tlv *tlv;
> -	struct timespec64 now;
> -	struct tm tm_now;
> -	char fw_build[128];
> -	char ts[128];
> -	char vendor[64];
> -	char driver[64];
> +	struct btintel_pcie_dump_entry *entry;
> +	struct btintel_pcie_dump_file_hdr *file_hdr;
> +	struct scatterlist *sg_dump_data;
> +	u32 status_reg, wrap_reg;
> +	u32 exception_dump_len;
> +	u32 exc_addr;
> +	u64 regions_mask = 0;
> +	u8 hw_variant;
> +	u32 smem_rd_addr = 0, smem_rd_size = 0;
> +	u32 file_len;
> +	u8 count;
> +	int ret;
> +	LIST_HEAD(dump_list);
>   
>   	if (!IS_ENABLED(CONFIG_DEV_COREDUMP))
>   		return -EOPNOTSUPP;
>   
> +	if (btintel_pcie_is_mdbgc_supported(data)) {
> +		mdbgc = &data->mdbgc;
> +		count = mdbgc->count;
> +	} else {
> +		dbgc = &data->dbgc;
> +		count = dbgc->count;
> +	}
>   
>   	hw_variant = INTEL_HW_VARIANT(data->cnvi);
> -	switch (hw_variant) {
> -	case BTINTEL_HWID_BZRI:
> -	case BTINTEL_HWID_BZRIW:
> +
> +	if (hw_variant == BTINTEL_HWID_BZRI ||
> +	    hw_variant == BTINTEL_HWID_BZRIW) {
>   		status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS;
>   		wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND;
> -		break;
> -	case BTINTEL_HWID_SCP:
> -	case BTINTEL_HWID_SCP2:
> -	case BTINTEL_HWID_SCP2F:
> +	} else if (hw_variant >= BTINTEL_HWID_SCP) {
>   		status_reg = BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP;
>   		wrap_reg = BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP;
> -		break;
> -	default:
> -		bt_dev_err(hdev, "Unsupported Intel hardware variant (0x%2.2x)",
> +	} else {
> +		bt_dev_err(hdev,
> +			   "Unsupported Intel hardware variant (0x%2.2x)",
>   			   hw_variant);
>   		return -EINVAL;
>   	}
>   
> -	wr_ptr_status = btintel_pcie_rd_dev_mem(data, status_reg);
> -	data->dmp_hdr.wrap_ctr = btintel_pcie_rd_dev_mem(data, wrap_reg);
> +	smem_rd_addr = data->dump_info.smem_addr_start;
> +	smem_rd_size = 0;
>   
> -	offset = wr_ptr_status & BTINTEL_PCIE_DBG_OFFSET_BIT_MASK;
> +	if (!smem_rd_addr && !data->dump_info.smem_addr_end) {
> +		bt_dev_dbg(hdev, "smem region not advertised by firmware");
> +	} else if (data->dump_info.smem_addr_end < smem_rd_addr ||
> +		   data->dump_info.smem_addr_end > U32_MAX - 0x04) {
> +		bt_dev_err(hdev,
> +			   "Invalid smem region: start=0x%08x end=0x%08x",
> +			   smem_rd_addr, data->dump_info.smem_addr_end);
> +	} else {
> +		smem_rd_size = (data->dump_info.smem_addr_end + 0x04) -
> +			       smem_rd_addr;
>   
> -	buf_idx = BTINTEL_PCIE_DBGC_DBG_BUF_IDX(wr_ptr_status);
> -	if (buf_idx > dbgc->count) {
> -		bt_dev_warn(hdev, "Buffer index is invalid");
> -		return -EINVAL;
> +		bt_dev_dbg(hdev,
> +			   "smem_region: smem_start_addr=0x%08x smem_end_addr=0x%08x smem_rd_size=%u",
> +			   smem_rd_addr, data->dump_info.smem_addr_end,
> +			   smem_rd_size);
> +
> +		if (smem_rd_size == 0 ||
> +		    smem_rd_size > BTINTEL_PCIE_SMEM_MAX_SIZE) {
> +			bt_dev_err(hdev,
> +				   "Invalid smem region: smem_rd_addr 0x%08x size %u (max %u)",
> +				   smem_rd_addr, smem_rd_size,
> +				   BTINTEL_PCIE_SMEM_MAX_SIZE);
> +			smem_rd_size = 0;
> +		}
>   	}
>   
> -	prev_size = buf_idx * BTINTEL_PCIE_DBGC_BUFFER_SIZE;
> -	if (prev_size + offset >= prev_size)
> -		data->dmp_hdr.write_ptr = prev_size + offset;
> -	else
> -		return -EINVAL;
> +	if (btintel_pcie_is_mdbgc_supported(data)) {
> +		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
> +						     mdbgc->buf1, status_reg,
> +						     wrap_reg,
> +						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_1,
> +						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR1,
> +						     "monitor");
> +		if (!ret)
> +			regions_mask |=
> +				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR1);
> +		else
> +			bt_dev_warn(hdev, "Failed to dump DRAM buf1: %d", ret);
> +
> +		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
> +						     mdbgc->buf2, status_reg,
> +						     wrap_reg,
> +						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_2,
> +						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR2,
> +						     "monitor2");
> +		if (!ret)
> +			regions_mask |=
> +				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR2);
> +		else
> +			bt_dev_warn(hdev, "Failed to dump DRAM buf2: %d", ret);
> +
> +		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
> +						     mdbgc->buf3, status_reg,
> +						     wrap_reg,
> +						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_3,
> +						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR3,
> +						     "monitor3");
> +		if (!ret)
> +			regions_mask |=
> +				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR3);
> +		else
> +			bt_dev_warn(hdev, "Failed to dump DRAM buf3: %d", ret);
> +	} else {
> +		ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count,
> +						     dbgc->bufs, status_reg,
> +						     wrap_reg,
> +						     BTINTEL_PCIE_MDBGC_ALLOCATIONID_1,
> +						     BTINTEL_PCIE_INI_ID_DRAM_MONITOR1,
> +						     "monitor");
> +		if (!ret)
> +			regions_mask |=
> +				BIT_ULL(BTINTEL_PCIE_INI_ID_DRAM_MONITOR1);
> +		else
> +			bt_dev_warn(hdev,
> +				    "Failed to dump DRAM region: %d", ret);
> +	}
>   
> -	strscpy(vendor, "Vendor: Intel\n");
> -	snprintf(driver, sizeof(driver), "Driver: %s\n",
> -		 data->dmp_hdr.driver_name);
> -
> -	ktime_get_real_ts64(&now);
> -	time64_to_tm(now.tv_sec, 0, &tm_now);
> -	snprintf(ts, sizeof(ts), "Dump Time: %02d-%02d-%04ld %02d:%02d:%02d",
> -				 tm_now.tm_mday, tm_now.tm_mon + 1, tm_now.tm_year + 1900,
> -				 tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec);
> -
> -	snprintf(fw_build, sizeof(fw_build),
> -			    "Firmware Timestamp: Year %u WW %02u buildtype %u build %u",
> -			    2000 + (data->dmp_hdr.fw_timestamp >> 8),
> -			    data->dmp_hdr.fw_timestamp & 0xff, data->dmp_hdr.fw_build_type,
> -			    data->dmp_hdr.fw_build_num);
> -
> -	data_len = sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_bt) +
> -		sizeof(*tlv) + sizeof(data->dmp_hdr.write_ptr) +
> -		sizeof(*tlv) + sizeof(data->dmp_hdr.wrap_ctr) +
> -		sizeof(*tlv) + sizeof(data->dmp_hdr.trigger_reason) +
> -		sizeof(*tlv) + sizeof(data->dmp_hdr.fw_sha) +
> -		sizeof(*tlv) + sizeof(data->dmp_hdr.cnvr_top) +
> -		sizeof(*tlv) + sizeof(data->dmp_hdr.cnvi_top) +
> -		sizeof(*tlv) + strlen(ts) +
> -		sizeof(*tlv) + strlen(fw_build) +
> -		sizeof(*tlv) + strlen(vendor) +
> -		sizeof(*tlv) + strlen(driver);
> -
> -	if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
> -		data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_type);
> -		data_len += sizeof(*tlv) + sizeof(data->dmp_hdr.event_id);
> +	if (smem_rd_size &&
> +	    !btintel_pcie_dump_smem_monitor_region(data, &dump_list,
> +						   BTINTEL_PCIE_INI_ID_SMEM,
> +						   "monitor_smem",
> +						   smem_rd_addr,
> +						   smem_rd_size))
> +		regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SMEM);
> +
> +	exc_addr = data->dump_info.exception_dump_addr;
> +	exception_dump_len = data->dump_info.exception_dump_len;
> +	if (exc_addr && exception_dump_len) {
> +		ret = btintel_pcie_dump_target_region(data, &dump_list,
> +						      BTINTEL_PCIE_INI_ID_EXCEPTION_EVT,
> +						      "EXCEPTION_EVT_BUFFER", exc_addr,
> +						      exception_dump_len);
> +		if (!ret)
> +			regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_EXCEPTION_EVT);
>   	}
>   
> -	/*
> -	 * sizeof(u32) - signature
> -	 * sizeof(data_len) - to store tlv data size
> -	 * data_len - TLV data
> -	 */
> -	dump_size = sizeof(u32) + sizeof(data_len) + data_len;
> +	btintel_pcie_dump_mem_range(data, &dump_list,
> +				    BTINTEL_PCIE_INI_ID_DCCM, "DCCM",
> +				    data->dump_info.dccm_addr_start,
> +				    data->dump_info.dccm_addr_end,
> +				    &regions_mask);
> +
> +	btintel_pcie_dump_mem_range(data, &dump_list,
> +				    BTINTEL_PCIE_INI_ID_SDS, "SDS",
> +				    data->dump_info.sds_start_addr_start,
> +				    data->dump_info.sds_start_addr_end,
> +				    &regions_mask);
> +
> +	btintel_pcie_dump_mem_range(data, &dump_list,
> +				    BTINTEL_PCIE_INI_ID_SDS_IOSF, "SDS_IOSF",
> +				    data->dump_info.sds_iosf_data_addr_start,
> +				    data->dump_info.sds_iosf_data_addr_end,
> +				    &regions_mask);
> +
> +	btintel_pcie_dump_mem_range(data, &dump_list,
> +				    BTINTEL_PCIE_INI_ID_ECL, "ECL_REGION",
> +				    data->dump_info.ecl_addr_start,
> +				    data->dump_info.ecl_addr_end,
> +				    &regions_mask);
> +
> +	ret = btintel_pcie_dump_info(data, &dump_list, regions_mask);
> +	if (ret) {
> +		btintel_pcie_dump_list_free(&dump_list);
> +		return ret;
> +	}
>   
> +	file_len = sizeof(*file_hdr) +
> +		   btintel_pcie_dump_list_total_size(&dump_list);
> +	entry = btintel_pcie_dump_entry_alloc(sizeof(*file_hdr));
> +	if (!entry) {
> +		btintel_pcie_dump_list_free(&dump_list);
> +		return -ENOMEM;
> +	}
>   
> -	/* Add debug buffers data length to dump size */
> -	dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
> +	file_hdr = (void *)entry->data;
> +	file_hdr->barker = cpu_to_le32(BTINTEL_PCIE_INI_ERROR_DUMP_BARKER);
> +	file_hdr->file_len = cpu_to_le32(file_len);
> +	list_add(&entry->list, &dump_list);
>   
> -	pdata = vmalloc(dump_size);
> -	if (!pdata)
> -		return -ENOMEM;
> -	p = pdata;
> -
> -	*(u32 *)p = BTINTEL_PCIE_MAGIC_NUM;
> -	p += sizeof(u32);
> -
> -	*(u32 *)p = data_len;
> -	p += sizeof(u32);
> -
> -
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_VENDOR, vendor, strlen(vendor));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_DRIVER, driver, strlen(driver));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_DUMP_TIME, ts, strlen(ts));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_FW_BUILD, fw_build,
> -				  strlen(fw_build));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_BT, &data->dmp_hdr.cnvi_bt,
> -				  sizeof(data->dmp_hdr.cnvi_bt));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_WRITE_PTR, &data->dmp_hdr.write_ptr,
> -				  sizeof(data->dmp_hdr.write_ptr));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_WRAP_CTR, &data->dmp_hdr.wrap_ctr,
> -				  sizeof(data->dmp_hdr.wrap_ctr));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_TRIGGER_REASON, &data->dmp_hdr.trigger_reason,
> -				  sizeof(data->dmp_hdr.trigger_reason));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_FW_SHA, &data->dmp_hdr.fw_sha,
> -				  sizeof(data->dmp_hdr.fw_sha));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVR_TOP, &data->dmp_hdr.cnvr_top,
> -				  sizeof(data->dmp_hdr.cnvr_top));
> -	p = btintel_pcie_copy_tlv(p, BTINTEL_CNVI_TOP, &data->dmp_hdr.cnvi_top,
> -				  sizeof(data->dmp_hdr.cnvi_top));
> -
> -	if (data->dmp_hdr.event_type && data->dmp_hdr.event_id) {
> -		p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_TYPE,
> -					  &data->dmp_hdr.event_type,
> -					  sizeof(data->dmp_hdr.event_type));
> -		p = btintel_pcie_copy_tlv(p, BTINTEL_EVENT_ID,
> -					  &data->dmp_hdr.event_id,
> -					  sizeof(data->dmp_hdr.event_id));
> -		data->dmp_hdr.event_type = 0;
> -		data->dmp_hdr.event_id = 0;
> -	}
> -
> -	memcpy(p, dbgc->bufs[0].data, dbgc->count * BTINTEL_PCIE_DBGC_BUFFER_SIZE);
> -	dev_coredumpv(&hdev->dev, pdata, dump_size, GFP_KERNEL);
> -	return 0;
> +	sg_dump_data = btintel_pcie_alloc_sgtable(file_len);
> +	if (sg_dump_data) {
> +		int sg_entries = sg_nents(sg_dump_data);
> +		u32 offs = 0;
> +
> +		list_for_each_entry(entry, &dump_list, list) {
> +			sg_pcopy_from_buffer(sg_dump_data, sg_entries,
> +					     entry->data, entry->size, offs);
> +			offs += entry->size;
> +		}
> +
> +		bt_dev_dbg(hdev, "triggering dev_coredumpsg()");
> +		dev_coredumpsg(&hdev->dev, sg_dump_data, file_len, GFP_KERNEL);
> +	} else {
> +		bt_dev_err(hdev, "Failed to allocate scatter-gather table for coredump");
> +		ret = -ENOMEM;
> +	}
> +
> +	btintel_pcie_dump_list_free(&dump_list);
> +	return ret;
>   }
>   
>   static void btintel_pcie_dump_traces(struct hci_dev *hdev)
>   {
>   	struct btintel_pcie_data *data = hci_get_drvdata(hdev);
> -	int ret = 0;
> +	int ret;
>   
>   	ret = btintel_pcie_get_mac_access(data);
>   	if (ret) {
> @@ -923,7 +1445,7 @@ static void btintel_pcie_dump_traces(struct hci_dev *hdev)
>   		return;
>   	}
>   
> -	ret = btintel_pcie_read_dram_buffers(data);
> +	ret = btintel_pcie_read_debug_regions(data);
>   
>   	btintel_pcie_release_mac_access(data);
>   
> @@ -1168,19 +1690,19 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
>   	ptr = buffer;
>   	remaining = buffer_len;
>   
> -	/* Parse TLV structures: type(1) + length(2) + value */
> +	/* Parse TLV structures: 1 byte type + 2 bytes length +
> +	 * variable value
> +	 */
>   	while (remaining >= sizeof(struct mbox_tlv)) {
>   		u16 tlv_len;
>   		u32 tlv_total;
>   
>   		tlv = (struct mbox_tlv *)ptr;
>   		tlv_len = le16_to_cpu(tlv->len);
> -		tlv_total = sizeof(tlv->type) +
> -			    sizeof(tlv->len) + tlv_len;
> +		tlv_total = sizeof(tlv->type) + sizeof(tlv->len) + tlv_len;
>   
>   		if (tlv_total > remaining) {
> -			bt_dev_err(data->hdev,
> -				   "TLV parse error: type=%u, len=%u",
> +			bt_dev_err(data->hdev, "TLV parse error: not enough data for TLV value (type=%u, len=%u)",
>   				   tlv->type, tlv_len);
>   			break;
>   		}
> @@ -1189,7 +1711,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
>   		case BTINTEL_PCIE_TLV_TYPE_EXCEPTION_DUMP_ADDRESS:
>   			if (tlv_len < 8) {
>   				bt_dev_err(data->hdev,
> -					   "TLV %s too short: %u",
> +					   "TLV %s too short: len=%u (need 8)",
>   					   btintel_pcie_tlv_str(tlv->type),
>   					   tlv_len);
>   				break;
> @@ -1202,7 +1724,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
>   		case BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS:
>   			if (tlv_len < 8) {
>   				bt_dev_err(data->hdev,
> -					   "TLV %s too short: %u",
> +					   "TLV %s too short: len=%u (need 8)",
>   					   btintel_pcie_tlv_str(tlv->type),
>   					   tlv_len);
>   				break;
> @@ -1221,8 +1743,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
>   				bt_dev_dbg(data->hdev, "SDS TLV: skipped, hw_variant not yet known");
>   				break;
>   			}
> -			if (tlv_len == 16 &&
> -			    hw_variant > BTINTEL_HWID_BZRI) {
> +			if (tlv_len == 16 && hw_variant > BTINTEL_HWID_BZRI) {
>   				data->dump_info.sds_start_addr_start =
>   					get_unaligned_le32(&tlv->val[0]);
>   				data->dump_info.sds_start_addr_end =
> @@ -1255,7 +1776,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
>   		case BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS:
>   			if (tlv_len < 8) {
>   				bt_dev_err(data->hdev,
> -					   "TLV %s too short: %u",
> +					   "TLV %s too short: len=%u (need 8)",
>   					   btintel_pcie_tlv_str(tlv->type),
>   					   tlv_len);
>   				break;
> @@ -1268,7 +1789,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
>   		case BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS:
>   			if (tlv_len < 8) {
>   				bt_dev_err(data->hdev,
> -					   "TLV %s too short: %u",
> +					   "TLV %s too short: len=%u (need 8)",
>   					   btintel_pcie_tlv_str(tlv->type),
>   					   tlv_len);
>   				break;
> @@ -1279,8 +1800,7 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
>   				get_unaligned_le32(&tlv->val[4]);
>   			break;
>   		default:
> -			bt_dev_dbg(data->hdev,
> -				   "Unknown TLV type: %u length: %u",
> +			bt_dev_dbg(data->hdev, "Unknown TLV type: %u length: %u",
>   				   tlv->type, tlv_len);
>   			break;
>   		}
> @@ -1868,11 +2388,8 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
>   		goto exit_on_error;
>   
>   	evt = (void *)buf;
> -	data->dmp_hdr.event_type = evt->event_type;
> -	data->dmp_hdr.event_id = le16_to_cpu(evt->event_id);
> -
>   	bt_dev_dbg(data->hdev, "event type: 0x%2.2x event id: 0x%4.4x len: %u",
> -		   data->dmp_hdr.event_type, data->dmp_hdr.event_id, len);
> +		   evt->event_type, le16_to_cpu(evt->event_id), len);
>   
>   	skb = bt_skb_alloc(len, GFP_KERNEL);
>   	if (!skb) {
> @@ -1898,8 +2415,8 @@ static int btintel_pcie_dump_fwtrigger_event(struct btintel_pcie_data *data)
>    *
>    * Always queue this AFTER any companion event-reader work (hwexp /
>    * fwtrigger) so that, on the ordered @dump_workqueue, the event reader
> - * runs first and populates dmp_hdr.event_type / event_id before
> - * dump_traces consumes them.
> + * runs first and the trigger metadata is populated before dump_traces
> + * consumes it.
>    */
>   static bool btintel_pcie_queue_coredump(struct btintel_pcie_data *data,
>   					u16 trigger_reason)
> @@ -1982,12 +2499,6 @@ static void btintel_pcie_coredump_worker(struct work_struct *work)
>   		goto out;
>   	}
>   
> -	if (btintel_pcie_is_mdbgc_supported(data)) {
> -		bt_dev_info(data->hdev,
> -			    "Skipping coredump: MDBGC dump format not supported yet");
> -		goto out;
> -	}
> -
>   	btintel_pcie_dump_traces(data->hdev);
>   out:
>   	/* Release guard last so a new trigger can run only after this
> diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
> index 5c35e65d3e81..9d84db48a8e5 100644
> --- a/drivers/bluetooth/btintel_pcie.h
> +++ b/drivers/bluetooth/btintel_pcie.h
> @@ -74,11 +74,16 @@
>   #define BTINTEL_PCIE_DBGC_BASE_ADDR_SCP			(0xf0d5d500)
>   #define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP	(BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x1C)
>   #define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP		(BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x2C)
> +#define BTINTEL_PCIE_DBGC_ALLOCATION_OFFSET		0x100
>   
> +#define BTINTEL_PCIE_SMEM_MAX_SIZE		(16 * 1024)
> +#define BTINTEL_PCIE_REGION_MAX_SIZE		(16 * 1024 * 1024)
>   #define BTINTEL_PCIE_DBG_IDX_BIT_MASK		0x0F
>   #define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data)	(((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK)
>   #define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK	0xFFFFFF
>   
> +#define BTINTEL_PCIE_TARGET_ACCESS_FRAG_OFFSET	4
> +
>   /* The DRAM buffer count, each buffer size, and
>    * fragment buffer size
>    */
> @@ -242,7 +247,7 @@ enum {
>   #define BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS        0x05
>   #define BTINTEL_PCIE_TLV_TYPE_SDS_MEM_ADDRESS         0x06
>   #define BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS         0x07
> -#define BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS           0x08
> +#define BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS	      0x08
>   
>   /*
>    * Struct for Context Information (v2)
> @@ -527,10 +532,147 @@ struct btintel_pcie_dump_header {
>   	u32		wrap_ctr;
>   	u16		trigger_reason;
>   	int		state;
> -	u8		event_type;
> -	u16		event_id;
>   };
>   
> +/* Per-fragment range descriptor for dump regions.
> + * Binary-compatible with iwl_fw_ini_error_dump_range.
> + */
> +struct btintel_pcie_dump_range {
> +	__le32	range_data_size;
> +	union {
> +		__le32	internal_base_addr;
> +		__le64	dram_base_addr;
> +		__le32	page_num;
> +	};
> +	__le32	data[];
> +} __packed;
> +
> +/*
> + * INI region types for ini_dump_data.type field.
> + * The unified decoder dispatches parsing logic based on these values.
> + */
> +#define BTINTEL_PCIE_INI_REGION_INTERNAL_BUFFER	2
> +#define BTINTEL_PCIE_INI_REGION_DRAM_BUFFER	3
> +#define BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY	9
> +
> +/* INI region IDs - used in dump header region_id field and regions_mask */
> +#define BTINTEL_PCIE_INI_ID_EXCEPTION_EVT	7
> +#define BTINTEL_PCIE_INI_ID_SMEM		15
> +#define BTINTEL_PCIE_INI_ID_DCCM		39
> +#define BTINTEL_PCIE_INI_ID_SDS			40
> +#define BTINTEL_PCIE_INI_ID_SDS_IOSF		41
> +#define BTINTEL_PCIE_INI_ID_ECL			42
> +#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR3	61
> +#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR2	62
> +#define BTINTEL_PCIE_INI_ID_DRAM_MONITOR1	63
> +
> +/*
> + * INI (Intel INI debug infrastructure) style dump data TLV - wraps each dump
> + * region. INI is the iwlwifi firmware debug format used by the unified decoder.
> + * Compatible with iwl_fw_ini_error_dump_data.
> + */
> +struct btintel_pcie_ini_dump_data {
> +	u8	type;
> +	u8	sub_type;
> +	u8	sub_type_ver;
> +	u8	reserved;
> +	__le32	len;
> +	u8	data[];
> +} __packed;
> +
> +/*
> + * INI-style region dump header.
> + * Compatible with iwl_fw_ini_error_dump_header.
> + */
> +#define BTINTEL_PCIE_INI_MAX_NAME	32
> +#define BTINTEL_PCIE_INI_DUMP_VER	1
> +
> +struct btintel_pcie_ini_dump_header {
> +	__le32	version;
> +	__le32	region_id;
> +	__le32	num_of_ranges;
> +	__le32	name_len;
> +	u8	name[BTINTEL_PCIE_INI_MAX_NAME];
> +};
> +
> +/*
> + * INI-style monitor dump - region header + monitor state.
> + * Compatible with iwl_fw_ini_monitor_dump.
> + */
> +struct btintel_pcie_ini_monitor_dump {
> +	struct btintel_pcie_ini_dump_header header;
> +	__le32	write_ptr;
> +	__le32	cycle_cnt;
> +	__le32	cur_frag;
> +	u8	data[];
> +} __packed;
> +
> +/* Linked list entry for modular dump collection */
> +struct btintel_pcie_dump_entry {
> +	struct list_head	list;
> +	u32			size;
> +	u8			data[];
> +};
> +
> +/* File-level header for coredump output.

/*
  * File-level …

> + * Compatible with iwl_fw_ini_dump_file_hdr.
> + * Uses IWL_FW_INI_ERROR_DUMP_BARKER (0x14789633) for decoder compatibility.
> + */
> +#define BTINTEL_PCIE_INI_ERROR_DUMP_BARKER	0x14789633
> +
> +struct btintel_pcie_dump_file_hdr {
> +	__le32	barker;
> +	__le32	file_len;
> +} __packed;
> +
> +/*
> + * Legacy-style dump data wrapper for dump info TLV.
> + * Compatible with iwl_fw_error_dump_data.
> + * Used only for the dump info entry (type=BTINTEL_PCIE_INI_DUMP_INFO_TYPE).
> + */
> +struct btintel_pcie_error_dump_data {
> +	__le32	type;
> +	__le32	len;
> +	u8	data[];
> +} __packed;
> +
> +/* Use bit 31 as dump info type, matching IWL_INI_DUMP_INFO_TYPE */
> +#define BTINTEL_PCIE_INI_DUMP_INFO_TYPE	BIT(31)
> +
> +/* Time point values matching iwl_fw_ini_time_point for fwdump parser */
> +#define BTINTEL_PCIE_TIME_POINT_FW_ASSERT	4
> +#define BTINTEL_PCIE_TIME_POINT_USER_TRIGGER	9
> +
> +/*
> + * Dump info struct - single TLV containing all metadata.
> + * Compatible with iwl_fw_ini_dump_info.
> + * Packs all device/firmware info that the decoder needs.
> + */
> +struct btintel_pcie_ini_dump_info {
> +	__le32	version;
> +	__le32	time_point;
> +	__le32	trigger_reason;
> +	__le32	external_cfg_state;
> +	__le32	ver_type;
> +	__le32	ver_subtype;
> +	__le32	hw_step;
> +	__le32	hw_type;
> +	__le32	rf_id_flavor;
> +	__le32	rf_id_dash;
> +	__le32	rf_id_step;
> +	__le32	rf_id_type;
> +	__le32	lmac_major;
> +	__le32	lmac_minor;
> +	__le32	umac_major;
> +	__le32	umac_minor;
> +	__le32	fw_mon_mode;
> +	__le64	regions_mask;
> +	__le32	build_tag_len;
> +	u8	build_tag[64];
> +	__le32	num_of_cfg_names;
> +	/* no cfg_names for BT - keep zero-length */
> +} __packed;
> +
>   /* struct btintel_pcie_data
>    * @pdev: pci device
>    * @hdev: hdev device
> @@ -688,4 +830,3 @@ static inline u32 btintel_pcie_rd_dev_mem(struct btintel_pcie_data *data,
>   	btintel_pcie_wr_reg32(data, BTINTEL_PCIE_PRPH_DEV_ADDR_REG, addr);
>   	return btintel_pcie_rd_reg32(data, BTINTEL_PCIE_PRPH_DEV_RD_REG);
>   }
> -

The removal of the last line could also be removed from this commit.

It’d be great if you could resend a smaller commit for review.


Kind regards,

Paul

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

* Re: [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
  2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
                   ` (6 preceding siblings ...)
  2026-09-04  0:29 ` [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
@ 2026-09-04 16:56 ` patchwork-bot+bluetooth
  7 siblings, 0 replies; 15+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-04 16:56 UTC (permalink / raw)
  To: Chandrashekar Devegowda
  Cc: linux-bluetooth, ravishankar.srivatsa, chethan.tumkur.narayan

Hello:

This series was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri, 28 Aug 2026 19:57:22 +0530 you wrote:
> Two identical definitions of BTINTEL_PCIE_MAGIC_NUM were present in
> btintel_pcie.c, one indented with spaces and one with a tab. Remove the
> space-indented duplicate and the surrounding blank lines. Kernel coding
> style requires tabs for indentation, so keep the tab-indented copy.
> 
> No functional change.
> 
> [...]

Here is the summary with links:
  - [v1,1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
    https://git.kernel.org/bluetooth/bluetooth-next/c/1cdf710cc2a1
  - [v1,2/4] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation
    (no matching commit)
  - [v1,3/4] Bluetooth: btintel_pcie: log mailbox handler latency in msix_gp1_handler
    (no matching commit)
  - [v1,4/4] Bluetooth: btintel_pcie: unified decoder coredump format
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

* Re: [PATCH v2 1/3] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
  2026-09-04  0:29 ` [PATCH v2 1/3] " Chandrashekar Devegowda
  2026-09-04  1:10   ` [v2,1/3] " bluez.test.bot
@ 2026-09-04 16:56   ` patchwork-bot+bluetooth
  1 sibling, 0 replies; 15+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-04 16:56 UTC (permalink / raw)
  To: Chandrashekar Devegowda
  Cc: linux-bluetooth, ravishankar.srivatsa, chethan.tumkur.narayan,
	kiran.k

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri,  4 Sep 2026 05:59:36 +0530 you wrote:
> Two identical definitions of BTINTEL_PCIE_MAGIC_NUM were present in
> btintel_pcie.c, one indented with spaces and one with a tab. Remove the
> space-indented duplicate and the surrounding blank lines. Kernel coding
> style requires tabs for indentation, so keep the tab-indented copy.
> 
> No functional change.
> 
> [...]

Here is the summary with links:
  - [v2,1/3] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition
    https://git.kernel.org/bluetooth/bluetooth-next/c/1cdf710cc2a1

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

* Re: [PATCH v2 2/3] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation
  2026-09-04  0:29 ` [PATCH v2 2/3] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
@ 2026-09-04 17:50   ` patchwork-bot+bluetooth
  0 siblings, 0 replies; 15+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-04 17:50 UTC (permalink / raw)
  To: Chandrashekar Devegowda
  Cc: linux-bluetooth, ravishankar.srivatsa, chethan.tumkur.narayan,
	kiran.k

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri,  4 Sep 2026 05:59:37 +0530 you wrote:
> Newer Intel BT PCIe variants (Nova Lake SCP2 and PTL FMP2) require
> three independent DRAM debug (DBGC) buffer pools instead of the
> single pool used by existing controllers.
> 
> Factor the per-pool buffer allocation into a common helper and add
> a multi-DBGC (MDBGC) setup path that allocates three pools using
> the fragment context format. MDBGC is used when the device reports
> an MDBGC-capable variant; otherwise the existing single-pool DBGC
> path is retained.
> 
> [...]

Here is the summary with links:
  - [v2,2/3] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation
    https://git.kernel.org/bluetooth/bluetooth-next/c/61f725843b50

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

* Re: [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format
  2026-09-04  0:29 ` [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
  2026-09-04  0:46   ` [v2,3/3] " bluez.test.bot
  2026-09-04  9:49   ` [PATCH v2 3/3] " Paul Menzel
@ 2026-09-04 17:50   ` patchwork-bot+bluetooth
  2 siblings, 0 replies; 15+ messages in thread
From: patchwork-bot+bluetooth @ 2026-09-04 17:50 UTC (permalink / raw)
  To: Chandrashekar Devegowda
  Cc: linux-bluetooth, ravishankar.srivatsa, chethan.tumkur.narayan,
	kiran.k

Hello:

This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Fri,  4 Sep 2026 05:59:38 +0530 you wrote:
> Replace the legacy TLV-based coredump with a unified INI-format dump
> compatible with the iwlwifi decoder so a single decoder can parse
> coredumps across all Intel BT PCIe controller variants.
> 
> The dump is emitted via dev_coredumpsg() as structured regions built
> from firmware-advertised addresses: DRAM monitor buffers (single or
> MDBGC multi-buffer), SMEM monitor, exception event buffer, DCCM, SDS,
> SDS IOSF, ECL and dump info metadata. Regions are collected only when
> firmware advertises a valid address and size, and only collected
> regions are advertised in regions_mask.
> 
> [...]

Here is the summary with links:
  - [v2,3/3] Bluetooth: btintel_pcie: unified decoder coredump format
    https://git.kernel.org/bluetooth/bluetooth-next/c/755cf7adf8dd

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



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

end of thread, other threads:[~2026-09-04 17:51 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-28 14:27 [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition Chandrashekar Devegowda
2026-08-28 14:27 ` [PATCH v1 2/4] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
2026-08-28 14:27 ` [PATCH v1 3/4] Bluetooth: btintel_pcie: log mailbox handler latency in msix_gp1_handler Chandrashekar Devegowda
2026-08-28 14:27 ` [PATCH v1 4/4] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
2026-08-28 22:15 ` [v1,1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition bluez.test.bot
2026-09-04  0:29 ` [PATCH v2 1/3] " Chandrashekar Devegowda
2026-09-04  1:10   ` [v2,1/3] " bluez.test.bot
2026-09-04 16:56   ` [PATCH v2 1/3] " patchwork-bot+bluetooth
2026-09-04  0:29 ` [PATCH v2 2/3] Bluetooth: btintel_pcie: add MDBGC multi-buffer DBGC allocation Chandrashekar Devegowda
2026-09-04 17:50   ` patchwork-bot+bluetooth
2026-09-04  0:29 ` [PATCH v2 3/3] Bluetooth: btintel_pcie: unified decoder coredump format Chandrashekar Devegowda
2026-09-04  0:46   ` [v2,3/3] " bluez.test.bot
2026-09-04  9:49   ` [PATCH v2 3/3] " Paul Menzel
2026-09-04 17:50   ` patchwork-bot+bluetooth
2026-09-04 16:56 ` [PATCH v1 1/4] Bluetooth: btintel_pcie: remove duplicate BTINTEL_PCIE_MAGIC_NUM definition patchwork-bot+bluetooth

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).