* [PATCH v1] Bluetooth: btintel_pcie: support unified decoder coredump format
@ 2026-07-15 15:53 Chandrashekar Devegowda
2026-07-15 16:31 ` Luiz Augusto von Dentz
2026-07-15 17:05 ` [v1] " bluez.test.bot
0 siblings, 2 replies; 3+ messages in thread
From: Chandrashekar Devegowda @ 2026-07-15 15:53 UTC (permalink / raw)
To: linux-bluetooth
Cc: bhelgaas, ravishankar.srivatsa, chethan.tumkur.narayan,
Chandrashekar Devegowda, Kiran K
Refactor the coredump collection to use a unified decoder format
that consolidates exception dump, DCCM, SDS, SDS IOSF, ECL, and
SMEM memory regions into a structured TLV-based dump. This enables
a single decoder tool to parse all dump types across different
Intel BT PCIe controller variants (BlazarI/W, ScP, ScP2/F, Draco).
Key changes:
- Parse dump memory region addresses from FW-provided TLV data
- Add per-variant DBGC register offsets for wrap counter and
write pointer status
- Collect and emit coredump sections with proper headers for
the unified decoder tool
Signed-off-by: Kiran K <kiran.k@intel.com>
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
---
drivers/bluetooth/btintel.c | 12 +
drivers/bluetooth/btintel.h | 1 +
drivers/bluetooth/btintel_pcie.c | 1193 +++++++++++++++++++++++++-----
drivers/bluetooth/btintel_pcie.h | 267 ++++++-
4 files changed, 1299 insertions(+), 174 deletions(-)
diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
index 5e9cac090bd8..b0b8c38ae9e4 100644
--- a/drivers/bluetooth/btintel.c
+++ b/drivers/bluetooth/btintel.c
@@ -65,6 +65,7 @@ static struct {
const char *driver_name;
u8 hw_variant;
u32 fw_build_num;
+ u32 fw_git_sha1;
} coredump_info;
const guid_t btintel_guid_dsm =
@@ -559,6 +560,7 @@ int btintel_version_info_tlv(struct hci_dev *hdev,
coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt);
coredump_info.fw_build_num = version->build_num;
+ coredump_info.fw_git_sha1 = version->git_sha1;
bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,
2000 + (version->timestamp >> 8), version->timestamp & 0xff,
@@ -2448,6 +2450,13 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
btintel_reset_to_bootloader(hdev);
done:
+ {
+ struct btintel_data *intel_data = hci_get_priv(hdev);
+
+ intel_data->cnvi_bt = ver->cnvi_bt;
+ bt_dev_dbg(hdev, "INTEL HW VARIANT is: 0x%2x",
+ INTEL_HW_VARIANT(intel_data->cnvi_bt));
+ }
release_firmware(fw);
return err;
}
@@ -3299,6 +3308,9 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
btintel_version_info_tlv(hdev, &new_ver);
+ /* Update ver with the operational firmware version */
+ *ver = new_ver;
+
finish:
/* Set the event mask for Intel specific vendor events. This enables
* a few extra events that are useful during general operation. It
diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
index 37d93abdd5a3..02a37a3d459d 100644
--- a/drivers/bluetooth/btintel.h
+++ b/drivers/bluetooth/btintel.h
@@ -229,6 +229,7 @@ enum {
struct btintel_data {
DECLARE_BITMAP(flags, __INTEL_NUM_FLAGS);
int (*acpi_reset_method)(struct hci_dev *hdev);
+ u32 cnvi_bt;
};
#define btintel_set_flag(hdev, nr) \
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 013568197a39..3a714854ff50 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>
@@ -73,17 +74,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_BLZR_HWEXP_SIZE 1024
-#define BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR 0xB00A7C00
-
-#define BTINTEL_PCIE_SCP_HWEXP_SIZE 4096
-#define BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR 0xB030F800
-
-#define BTINTEL_PCIE_SCP2_HWEXP_SIZE 4096
-#define BTINTEL_PCIE_SCP2_HWEXP_DMP_ADDR 0xB031D000
-
#define BTINTEL_PCIE_MAGIC_NUM 0xA5A5A5A5
#define BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER 0x17A2
@@ -145,6 +135,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;
@@ -186,6 +190,127 @@ static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt)
}
}
+/* 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_buf: 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,
+ void **v_addr,
+ struct btintel_pcie_dbgc_ctxt_buf *frag_buf,
+ u32 buf_index,
+ u32 buf_count)
+{
+ struct data_buf *b;
+ int i;
+
+ *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;
+ }
+
+ *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_buf[i].buf_addr_lsb = lower_32_bits(b->data_p_addr);
+ frag_buf[i].buf_addr_msb = upper_32_bits(b->data_p_addr);
+ frag_buf[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
+ }
+
+ return 0;
+}
+
+/* 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;
+ int err;
+
+ data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
+
+ /* Allocate fragment context structure */
+ data->mdbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
+ sizeof(struct btintel_pcie_mdbgc_ctxt),
+ &data->mdbgc.frag_p_addr,
+ GFP_KERNEL | __GFP_NOWARN);
+ if (!data->mdbgc.frag_v_addr) {
+ BT_ERR("Failed to allocate mdbgc context");
+ return -ENOMEM;
+ }
+
+ data->mdbgc.frag_size = sizeof(struct btintel_pcie_mdbgc_ctxt);
+
+ /* 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;
+
+ /* 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;
+
+ /* 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 and formats the
* DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
* size as the payload
@@ -193,46 +318,41 @@ static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt)
static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
{
struct btintel_pcie_dbgc_ctxt db_frag;
- struct data_buf *buf;
- int i;
+ int err;
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)
- 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)
- return -ENOMEM;
+ /* 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)
+ 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;
- 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;
- }
+ /* 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,
+ 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;
}
@@ -639,63 +759,440 @@ 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 const char *btintel_pcie_dumptype_to_str(struct hci_dev *hdev,
+ u8 dumptype)
{
- struct intel_tlv *tlv;
+ switch (dumptype) {
+ case BTINTEL_DRAM_REGION:
+ return "DRAM_DUMP";
+ case BTINTEL_SMEM_REGION:
+ return "MONITOR_SMEM";
+ case BTINTEL_DCCM_REGION:
+ return "DCCM_DUMP";
+ case BTINTEL_SDS_REGION:
+ return "SDS_DUMP";
+ case BTINTEL_SDS_IOSF_DATA_REGION:
+ return "SDS_IOSF_DATA_DUMP";
+ case BTINTEL_SDS_FIXED_ROM_REGION:
+ return "SDS_FIXED_ROM_DUMP";
+ case BTINTEL_EXCEPTION_EVT_DEBUG_REGION:
+ return "EXCEPTION_EVT_DEBUG_DATA";
+ case BTINTEL_ECL_REGION:
+ return "ECL_DUMP";
+ default:
+ bt_dev_dbg(hdev, "Unknown dump type: %d", dumptype);
+ return "UNKNOWN_DUMP_TYPE";
+ }
+}
+
+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;
- tlv = dest;
- tlv->type = type;
- tlv->len = size;
- memcpy(tlv->val, data, tlv->len);
- return dest + sizeof(*tlv) + size;
+ nents = DIV_ROUND_UP(size, PAGE_SIZE);
+ result = kcalloc(nents + 1, sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return NULL;
+
+ sg_init_table(result, nents + 1);
+ 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;
+ }
+ sg_mark_end(sg - 1);
+ 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 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,
+ 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_read_dram_buffers(struct btintel_pcie_data *data)
+static int btintel_pcie_dump_target_region(struct btintel_pcie_data *data,
+ struct list_head *list,
+ u8 region_type, u32 region_id,
+ const char *name,
+ u32 addr_start, u32 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;
+ 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;
+ }
+
+ /* Align to 4 bytes - target access reads 32-bit words */
+ size = round_down(size, sizeof(u32));
+
+ bt_dev_info(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 = region_type;
+ 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(data->dmp_hdr.cnvi_top & 0xFFFF);
+ bt_dev_dbg(data->hdev, "hw_type=0x%x (cnvi_top=0x%x)",
+ data->dmp_hdr.cnvi_top & 0xFFFF,
+ 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_git_sha1);
+ dump->hw_step = cpu_to_le32(data->cnvr);
+ dump->rf_id_type = cpu_to_le32(data->dmp_hdr.cnvr_top & 0xFFF);
+ dump->rf_id_flavor = cpu_to_le32(data->dmp_hdr.cnvr_top);
+ bt_dev_dbg(data->hdev, "rf_id_type=0x%x (cnvr_top=0x%x)",
+ data->dmp_hdr.cnvr_top & 0xFFF,
+ 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_git_sha1);
+
+ 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 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_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 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 region_size;
+ u32 exception_dump_len;
+ 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;
+ bt_dev_info(data->hdev, "%s: WRT dump triggered",
+ data->dmp_hdr.trigger_reason == BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT ?
+ "Firmware" : "User");
+
+ 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:
- 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:
- 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)",
- hw_variant);
+
+ if (hw_variant == BTINTEL_HWID_BZRI ||
+ hw_variant == BTINTEL_HWID_BZRIW) {
+ wr_ptr_status =
+ btintel_pcie_rd_dev_mem(data,
+ BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS);
+ data->dmp_hdr.wrap_ctr =
+ btintel_pcie_rd_dev_mem(data,
+ BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND);
+ } else if (hw_variant >= BTINTEL_HWID_SCP) {
+ wr_ptr_status =
+ btintel_pcie_rd_dev_mem(data,
+ BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP);
+ data->dmp_hdr.wrap_ctr =
+ btintel_pcie_rd_dev_mem(data,
+ BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP);
+ } 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);
-
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;
}
@@ -706,102 +1203,193 @@ 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_git_sha1) +
- 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);
+
+ smem_rd_addr = data->dump_info.smem_addr_start;
+ smem_rd_size = (data->dump_info.smem_addr_end + 0x04) -
+ data->dump_info.smem_addr_start;
+
+ bt_dev_info(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;
}
- /*
- * sizeof(u32) - signature
- * sizeof(data_len) - to store tlv data size
- * data_len - TLV data
- */
- dump_size = sizeof(u32) + sizeof(data_len) + data_len;
+ 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) {
+ ret = btintel_pcie_dump_smem_monitor_region(data, &dump_list,
+ BTINTEL_PCIE_INI_ID_SMEM,
+ "monitor_smem",
+ smem_rd_addr, smem_rd_size);
+ if (!ret)
+ regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SMEM);
+ }
+
+ exception_dump_len = data->dump_info.exception_dump_len;
+ ret = btintel_pcie_dump_target_region(data, &dump_list,
+ BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
+ BTINTEL_PCIE_INI_ID_EXCEPTION_EVT,
+ "EXCEPTION_EVT_BUFFER",
+ data->dump_info.exception_dump_addr,
+ exception_dump_len);
+ if (!ret && exception_dump_len)
+ regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_EXCEPTION_EVT);
+
+ if (data->dump_info.dccm_addr_start &&
+ data->dump_info.dccm_addr_end &&
+ data->dump_info.dccm_addr_end >= data->dump_info.dccm_addr_start) {
+ region_size = (data->dump_info.dccm_addr_end + 0x04) -
+ data->dump_info.dccm_addr_start;
+ ret = btintel_pcie_dump_target_region(data, &dump_list,
+ BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
+ BTINTEL_PCIE_INI_ID_DCCM,
+ "DCCM",
+ data->dump_info.dccm_addr_start,
+ region_size);
+ if (!ret)
+ regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_DCCM);
+ }
+
+ if (data->dump_info.sds_start_addr_start &&
+ data->dump_info.sds_start_addr_end &&
+ data->dump_info.sds_start_addr_end >= data->dump_info.sds_start_addr_start) {
+ region_size = (data->dump_info.sds_start_addr_end + 0x04) -
+ data->dump_info.sds_start_addr_start;
+ ret = btintel_pcie_dump_target_region(data, &dump_list,
+ BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
+ BTINTEL_PCIE_INI_ID_SDS,
+ "SDS",
+ data->dump_info.sds_start_addr_start,
+ region_size);
+ if (!ret)
+ regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SDS);
+ }
+
+ if (data->dump_info.sds_iosf_data_addr_start &&
+ data->dump_info.sds_iosf_data_addr_end &&
+ data->dump_info.sds_iosf_data_addr_end >= data->dump_info.sds_iosf_data_addr_start) {
+ region_size = (data->dump_info.sds_iosf_data_addr_end + 0x04) -
+ data->dump_info.sds_iosf_data_addr_start;
+ ret = btintel_pcie_dump_target_region(data, &dump_list,
+ BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
+ BTINTEL_PCIE_INI_ID_SDS_IOSF,
+ "SDS_IOSF",
+ data->dump_info.sds_iosf_data_addr_start,
+ region_size);
+ if (!ret)
+ regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SDS_IOSF);
+ }
+ if (data->dump_info.ecl_addr_start &&
+ data->dump_info.ecl_addr_end &&
+ data->dump_info.ecl_addr_end >= data->dump_info.ecl_addr_start) {
+ region_size = (data->dump_info.ecl_addr_end + 0x04) -
+ data->dump_info.ecl_addr_start;
+ ret = btintel_pcie_dump_target_region(data, &dump_list,
+ BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
+ BTINTEL_PCIE_INI_ID_ECL,
+ "ECL_REGION",
+ data->dump_info.ecl_addr_start,
+ region_size);
+ if (!ret)
+ regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_ECL);
+ }
- /* Add debug buffers data length to dump size */
- dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
+ ret = btintel_pcie_dump_info(data, &dump_list, regions_mask);
+ if (ret) {
+ btintel_pcie_dump_list_free(&dump_list);
+ return ret;
+ }
- pdata = vmalloc(dump_size);
- if (!pdata)
+ 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_git_sha1,
- sizeof(data->dmp_hdr.fw_git_sha1));
- 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);
+ }
+
+ 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_info(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");
+ }
+
+ btintel_pcie_dump_list_free(&dump_list);
return 0;
}
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) {
@@ -809,7 +1397,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);
@@ -957,10 +1545,241 @@ static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data)
return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER;
}
+static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
+{
+ /* Custom TLV structure for mailbox parsing
+ * len is __le16 as per agreemnt with FW
+ */
+ struct mbox_tlv {
+ u8 type;
+ __le16 len;
+ u8 val[];
+ } __packed;
+
+ u8 *buffer, *ptr;
+ u32 buffer_len, err, remaining;
+ struct mbox_tlv *tlv;
+ struct btintel_data *cnvi_data = hci_get_priv(data->hdev);
+ u8 hw_variant = INTEL_HW_VARIANT(cnvi_data->cnvi_bt);
+
+ if (!data->debug_table_size || !data->debug_table_addr)
+ return -EINVAL;
+
+ buffer_len = data->debug_table_size;
+
+ buffer = vmalloc(buffer_len);
+ if (!buffer)
+ return -ENOMEM;
+
+ btintel_pcie_mac_init(data);
+
+ err = btintel_pcie_read_device_mem(data, buffer, data->debug_table_addr,
+ buffer_len);
+ if (err)
+ goto exit_on_error;
+
+ print_hex_dump_debug("dump_info: ", DUMP_PREFIX_OFFSET, 16, 1,
+ buffer, buffer_len, false);
+
+ ptr = buffer;
+ remaining = buffer_len;
+
+ /* 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;
+
+ if (tlv_total > remaining) {
+ bt_dev_err(data->hdev, "TLV parse error: not enough data for TLV value (type=%u, len=%u)",
+ tlv->type, tlv_len);
+ break;
+ }
+
+ switch (tlv->type) {
+ case BTINTEL_PCIE_TLV_TYPE_EXCEPTION_DUMP_ADDRESS:
+ if (tlv_len < 8) {
+ bt_dev_err(data->hdev,
+ "TLV %s too short: len=%u (need 8)",
+ btintel_pcie_dumptype_to_str(data->hdev,
+ tlv->type),
+ tlv_len);
+ break;
+ }
+ data->dump_info.exception_dump_addr = get_unaligned_le32(&tlv->val[0]);
+ data->dump_info.exception_dump_len = get_unaligned_le32(&tlv->val[4]);
+ break;
+ case BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS:
+ if (tlv_len < 8) {
+ bt_dev_err(data->hdev,
+ "TLV %s too short: len=%u (need 8)",
+ btintel_pcie_dumptype_to_str(data->hdev,
+ tlv->type),
+ tlv_len);
+ break;
+ }
+ data->dump_info.dccm_addr_start = get_unaligned_le32(&tlv->val[0]);
+ data->dump_info.dccm_addr_end = get_unaligned_le32(&tlv->val[4]);
+ break;
+ case BTINTEL_PCIE_TLV_TYPE_SDS_MEM_ADDRESS:
+ 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 =
+ get_unaligned_le32(&tlv->val[4]);
+ data->dump_info.sds_iosf_data_addr_start =
+ get_unaligned_le32(&tlv->val[8]);
+ data->dump_info.sds_iosf_data_addr_end =
+ get_unaligned_le32(&tlv->val[12]);
+ } else if (tlv_len == 24 && (hw_variant == BTINTEL_HWID_BZRI ||
+ hw_variant == BTINTEL_HWID_BZRIW)) {
+ data->dump_info.sds_fixed_rom_addr_start =
+ get_unaligned_le32(&tlv->val[0]);
+ data->dump_info.sds_fixed_rom_addr_end =
+ get_unaligned_le32(&tlv->val[4]);
+ data->dump_info.sds_start_addr_start =
+ get_unaligned_le32(&tlv->val[8]);
+ data->dump_info.sds_start_addr_end =
+ get_unaligned_le32(&tlv->val[12]);
+ data->dump_info.sds_iosf_data_addr_start =
+ get_unaligned_le32(&tlv->val[16]);
+ data->dump_info.sds_iosf_data_addr_end =
+ get_unaligned_le32(&tlv->val[20]);
+ } else {
+ bt_dev_err(data->hdev, "SDS TLV: unsupported hw_variant=0x%02x len=%u",
+ hw_variant, tlv_len);
+ }
+ break;
+ case BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS:
+ if (tlv_len < 8) {
+ bt_dev_err(data->hdev,
+ "TLV %s too short: len=%u (need 8)",
+ btintel_pcie_dumptype_to_str(data->hdev,
+ tlv->type),
+ tlv_len);
+ break;
+ }
+ data->dump_info.ecl_addr_start = get_unaligned_le32(&tlv->val[0]);
+ data->dump_info.ecl_addr_end = get_unaligned_le32(&tlv->val[4]);
+ break;
+ case BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS:
+ if (tlv_len < 8) {
+ bt_dev_err(data->hdev,
+ "TLV %s too short: len=%u (need 8)",
+ btintel_pcie_dumptype_to_str(data->hdev,
+ tlv->type),
+ tlv_len);
+ break;
+ }
+ data->dump_info.smem_addr_start = get_unaligned_le32(&tlv->val[0]);
+ data->dump_info.smem_addr_end = get_unaligned_le32(&tlv->val[4]);
+ break;
+ default:
+ bt_dev_dbg(data->hdev, "Unknown TLV type: %u length: %u",
+ tlv->type, tlv_len);
+ break;
+ }
+
+ /* Move to next TLV */
+ ptr += tlv_total;
+ remaining -= tlv_total;
+ }
+
+ bt_dev_info(data->hdev,
+ "exception_dump: addr:0x%08x len:0x%08x",
+ data->dump_info.exception_dump_addr,
+ data->dump_info.exception_dump_len);
+ bt_dev_info(data->hdev,
+ "dccm: start:0x%08x end:0x%08x",
+ data->dump_info.dccm_addr_start,
+ data->dump_info.dccm_addr_end);
+ bt_dev_info(data->hdev,
+ "sds_fixed_rom: start:0x%08x end:0x%08x",
+ data->dump_info.sds_fixed_rom_addr_start,
+ data->dump_info.sds_fixed_rom_addr_end);
+ bt_dev_info(data->hdev,
+ "sds: start:0x%08x end:0x%08x",
+ data->dump_info.sds_start_addr_start,
+ data->dump_info.sds_start_addr_end);
+ bt_dev_info(data->hdev,
+ "sds_iosf: start:0x%08x end:0x%08x",
+ data->dump_info.sds_iosf_data_addr_start,
+ data->dump_info.sds_iosf_data_addr_end);
+ bt_dev_info(data->hdev,
+ "ecl: start:0x%08x end:0x%08x",
+ data->dump_info.ecl_addr_start,
+ data->dump_info.ecl_addr_end);
+ bt_dev_info(data->hdev,
+ "smem: start:0x%08x end:0x%08x",
+ data->dump_info.smem_addr_start,
+ data->dump_info.smem_addr_end);
+
+ vfree(buffer);
+ return 0;
+
+exit_on_error:
+ vfree(buffer);
+ return err;
+}
+
static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
{
- bt_dev_err(data->hdev, "Received gp1 mailbox interrupt");
- btintel_pcie_dump_debug_registers(data->hdev);
+ bool target_access = false;
+ 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);
+ if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX1) {
+ 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");
+ target_access = true;
+ }
+ }
+
+ if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX2) {
+ data->mbox.mbox2 = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_MBOX_2_REG);
+ if (target_access)
+ data->debug_table_addr = data->mbox.mbox2;
+ }
+
+ if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX3) {
+ data->mbox.mbox3 = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_MBOX_3_REG);
+ if (target_access)
+ data->debug_table_size = data->mbox.mbox3;
+ }
+
+ if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4) {
+ data->mbox.mbox4 = btintel_pcie_rd_reg32(data,
+ BTINTEL_PCIE_CSR_MBOX_4_REG);
+ }
+
+ bt_dev_err(data->hdev, "GP1_handler: mbox status: 0x%8.8x mbox_1: 0x%8.8x mbox_2: 0x%8.8x mbox_3: 0x%8.8x mbox_4: 0x%8.8x",
+ data->mbox.mbox_status, data->mbox.mbox1, data->mbox.mbox2,
+ data->mbox.mbox3, data->mbox.mbox4);
+
+ if (target_access && !test_and_set_bit(BTINTEL_PCIE_MAIL_BOX_INTR, &data->flags)) {
+ if (!queue_work(data->dump_workqueue, &data->mbox_work))
+ clear_bit(BTINTEL_PCIE_MAIL_BOX_INTR, &data->flags);
+ }
+
+ /* Mailbox is read, ack to FW */
+ 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_info(data->hdev, "Mailbox acked in %llu usecs", duration);
}
/* This function handles the MSI-X interrupt for gp0 cause (bit 0 in
@@ -1307,23 +2126,25 @@ static void btintel_pcie_read_hwexp(struct btintel_pcie_data *data)
/* only from step B0 onwards */
if (INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvi_top) != 0x01)
return;
- len = BTINTEL_PCIE_BLZR_HWEXP_SIZE; /* exception data length */
- addr = BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR;
break;
case BTINTEL_CNVI_SCP:
- len = BTINTEL_PCIE_SCP_HWEXP_SIZE;
- addr = BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR;
- break;
case BTINTEL_CNVI_SCP2:
case BTINTEL_CNVI_SCP2F:
- len = BTINTEL_PCIE_SCP2_HWEXP_SIZE;
- addr = BTINTEL_PCIE_SCP2_HWEXP_DMP_ADDR;
break;
default:
bt_dev_err(data->hdev, "Unsupported cnvi 0x%8.8x", data->dmp_hdr.cnvi_top);
return;
}
+ len = data->dump_info.exception_dump_len;
+ addr = data->dump_info.exception_dump_addr;
+
+ if (!addr || !len) {
+ bt_dev_err(data->hdev, "Invalid exception address: 0x%8.8x or length: %d",
+ addr, len);
+ return;
+ }
+
buf = kzalloc(len, GFP_KERNEL);
if (!buf)
goto exit_on_error;
@@ -1576,6 +2397,20 @@ static void btintel_pcie_fwtrigger_worker(struct work_struct *work)
clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
}
+static void btintel_pcie_mbox_worker(struct work_struct *work)
+{
+ struct btintel_pcie_data *data = container_of(work,
+ struct btintel_pcie_data, mbox_work);
+
+ if (!data->hdev)
+ goto out;
+
+ btintel_parse_mbox_tlv(data);
+out:
+ /* Release guard last; matches set in gp1 handler. */
+ clear_bit(BTINTEL_PCIE_MAIL_BOX_INTR, &data->flags);
+}
+
static void btintel_pcie_rx_work(struct work_struct *work)
{
struct btintel_pcie_data *data = container_of(work,
@@ -1886,9 +2721,16 @@ static void btintel_pcie_init_ci(struct btintel_pcie_data *data,
ci->num_urbdq1 = data->rxq.count;
ci->urbdq_db_vec = BTINTEL_PCIE_RXQ_NUM;
- ci->dbg_output_mode = 0x01;
- ci->dbgc_addr = data->dbgc.frag_p_addr;
- ci->dbgc_size = data->dbgc.frag_size;
+ ci->dbg_output_mode = BTINTEL_PCIE_DRAM;
+
+ 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;
}
@@ -2116,7 +2958,10 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
v_addr += ci_size;
/* Setup data buffers for dbgc */
- err = btintel_pcie_setup_dbgc(data);
+ if (btintel_pcie_is_mdbgc_supported(data))
+ err = btintel_pcie_setup_mdbgc(data);
+ else
+ err = btintel_pcie_setup_dbgc(data);
if (err)
goto exit_error_txq;
@@ -2380,6 +3225,7 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
goto exit_error;
}
+ data->dmp_hdr.cnvi_bt = ver_tlv.cnvi_bt;
switch (INTEL_HW_PLATFORM(ver_tlv.cnvi_bt)) {
case 0x37:
break;
@@ -2433,7 +3279,6 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
data->dmp_hdr.fw_timestamp = ver_tlv.timestamp;
data->dmp_hdr.fw_build_type = ver_tlv.build_type;
data->dmp_hdr.fw_build_num = ver_tlv.build_num;
- data->dmp_hdr.cnvi_bt = ver_tlv.cnvi_bt;
if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03)
data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1;
@@ -2718,6 +3563,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
disable_work_sync(&data->coredump_work);
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
+ disable_work_sync(&data->mbox_work);
bt_dev_dbg(data->hdev, "Release bluetooth interface");
@@ -2742,6 +3588,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
enable_work(&data->coredump_work);
enable_work(&data->hwexp_work);
enable_work(&data->fwtrigger_work);
+ enable_work(&data->mbox_work);
}
out:
@@ -2948,6 +3795,7 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker);
INIT_WORK(&data->fwtrigger_work, btintel_pcie_fwtrigger_worker);
+ INIT_WORK(&data->mbox_work, btintel_pcie_mbox_worker);
data->boot_stage_cache = 0x00;
data->img_resp_cache = 0x00;
@@ -3017,6 +3865,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
disable_work_sync(&data->coredump_work);
disable_work_sync(&data->hwexp_work);
disable_work_sync(&data->fwtrigger_work);
+ disable_work_sync(&data->mbox_work);
/* Cancel pending reset work. Skip only when remove() is called from
* within the reset work itself (PLDR device_reprobe path) to avoid
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 749369b24031..93aaa1d1e187 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h
@@ -23,6 +23,8 @@
#define BTINTEL_PCIE_CSR_MBOX_3_REG (BTINTEL_PCIE_CSR_BASE + 0x178)
#define BTINTEL_PCIE_CSR_MBOX_4_REG (BTINTEL_PCIE_CSR_BASE + 0x17C)
#define BTINTEL_PCIE_CSR_MBOX_STATUS_REG (BTINTEL_PCIE_CSR_BASE + 0x180)
+#define BTINTEL_PCIE_CSR_IPC_DOORBELL_VEC_REG (BTINTEL_PCIE_CSR_BASE + 0x130)
+#define BTINTEL_PCIE_CSR_DOORBELL_MBOX_READ_CONFIRM (BIT(4))
#define BTINTEL_PCIE_PRPH_DEV_ADDR_REG (BTINTEL_PCIE_CSR_BASE + 0x440)
#define BTINTEL_PCIE_PRPH_DEV_RD_REG (BTINTEL_PCIE_CSR_BASE + 0x458)
#define BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR (BTINTEL_PCIE_CSR_BASE + 0x460)
@@ -67,15 +69,26 @@
#define BTINTEL_PCIE_DBGC_BASE_ADDR (0xf3800300)
#define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x1C)
#define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x2C)
+#define BTINTEL_PCIE_DBGC_IF_BASE_ADDR (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x30)
+#define BTINTEL_PCIE_DBGC_IF_SIZE (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x34)
+
+#define BTINTEL_PCIE_SMEM_BASE_ADDR (0xf4000000)
+#define BTINTEL_PCIE_SMEM_END_ADDR (BTINTEL_PCIE_SMEM_BASE_ADDR + 0x4000)
+
+#define BTINTEL_PCIE_DBGC_BASE_ADDR_BZRI (0xf3800300)
#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_SMEM_FRAGMENT_OFFSET 4
+#define BTINTEL_PCIE_SMEM_MAX_SIZE (16 * 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
*/
@@ -91,6 +104,27 @@
/* Num of alloc Dbg buff (4) + (LSB(4), MSB(4), Size(4)) for each buffer */
#define BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE 196
+#define BTINTEL_PCIE_MDBGC_FRAG_VERSION 2
+#define BTINTEL_PCIE_MDBGC_TOTAL_BUFFER_COUNT (16 * 3)
+
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_1 0
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_2 1
+#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_3 2
+
+/* Magic number(4), version(4), size of payload length(4) */
+#define BTINTEL_PCIE_MDBGC_FRAG_HEADER_SIZE 12
+
+/* Num of alloc Dbg buff (4) + (LSB(4), MSB(4), Size(4)) for each buffer */
+#define BTINTEL_PCIE_MDBGC_FRAG_PAYLOAD_SIZE 196
+
+#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
+
+#define BTINTEL_PCIE_SMEM 0x00
+#define BTINTEL_PCIE_DRAM 0x01
+#define BTINTEL_PCIE_FW_MON_MODE_DRAM 0x02
+
/* Causes for the FH register interrupts */
enum msix_fh_int_causes {
BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0 = BIT(0), /* cause 0 */
@@ -121,7 +155,8 @@ enum {
BTINTEL_PCIE_COREDUMP_INPROGRESS,
BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
- BTINTEL_PCIE_SETUP_DONE
+ BTINTEL_PCIE_SETUP_DONE,
+ BTINTEL_PCIE_MAIL_BOX_INTR
};
enum btintel_pcie_tlv_type {
@@ -137,7 +172,20 @@ enum btintel_pcie_tlv_type {
BTINTEL_VENDOR,
BTINTEL_DRIVER,
BTINTEL_EVENT_TYPE,
- BTINTEL_EVENT_ID
+ BTINTEL_EVENT_ID,
+ BTINTEL_DRAM_REGION,
+ BTINTEL_SMEM_REGION,
+ BTINTEL_DCCM_BASE_ADDR,
+ BTINTEL_DCCM_REGION,
+ BTINTEL_SDS_BASE_ADDR,
+ BTINTEL_SDS_REGION,
+ BTINTEL_SDS_IOSF_BASE_ADDR,
+ BTINTEL_SDS_IOSF_DATA_REGION,
+ BTINTEL_SDS_FIXED_ROM_BASE_ADDR,
+ BTINTEL_SDS_FIXED_ROM_REGION,
+ BTINTEL_EXCEPTION_EVT_DEBUG_REGION,
+ BTINTEL_ECL_BASE_ADDR,
+ BTINTEL_ECL_REGION
};
/* causes for the MBOX interrupts */
@@ -153,6 +201,14 @@ enum btintel_pcie_reset_type {
BTINTEL_PCIE_IOSF_PRR_PLDR = 1,
};
+enum btintel_pcie_mbox_msg {
+ BTINTEL_PCIE_NO_USE = 0,
+ BTINTEL_PCIE_TOP_SILENT_RESET,
+ BTINTEL_PCIE_SB_AUDIO_DEVICE_REPORT,
+ BTINTEL_PCIE_BUILD_SPECIFIC_RESOURCES_MAPPING,
+ BTINTEL_PCIE_LAST_MESSAGE = 4095 /* 0xFFF= 0x100-0x1 */
+};
+
#define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7)
/* Minimum and Maximum number of MSI-X Vector
@@ -198,6 +254,12 @@ enum {
/* RBD buffer size mapping */
#define BTINTEL_PCIE_RBD_SIZE_4K 0x04
+#define BTINTEL_PCIE_TLV_TYPE_EXCEPTION_DUMP_ADDRESS 0x04
+#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
+
/*
* Struct for Context Information (v2)
*
@@ -424,6 +486,62 @@ 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;
+};
+
+/* Structure for packing dump to decoder
+ * @type: TLV type
+ * @len: The length of TLV is u32 as agreed with decoder to accomadate larger
+ * dumps
+ * @val: TLV value
+ */
+struct intel_pcie_tlv {
+ u8 type;
+ u32 len;
+ u8 val[];
+} __packed;
+
+struct btintel_pcie_dump_mem_info {
+ u32 exception_dump_addr;
+ u32 exception_dump_len;
+ u32 dccm_addr_start;
+ u32 dccm_addr_end;
+ u32 sds_fixed_rom_addr_start;
+ u32 sds_fixed_rom_addr_end;
+ u32 sds_start_addr_start;
+ u32 sds_start_addr_end;
+ u32 sds_iosf_data_addr_start;
+ u32 sds_iosf_data_addr_end;
+ u32 ecl_addr_start;
+ u32 ecl_addr_end;
+ u32 smem_addr_start;
+ u32 smem_addr_end;
+};
+
+struct btintel_pcie_mbox {
+ u32 mbox_flags;
+ u32 mbox_status;
+ u32 mbox1;
+ u32 mbox2;
+ u32 mbox3;
+ u32 mbox4;
+};
+
struct btintel_pcie_dump_header {
const char *driver_name;
u32 cnvi_top;
@@ -441,6 +559,145 @@ struct btintel_pcie_dump_header {
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
@@ -522,6 +779,7 @@ struct btintel_pcie_data {
struct work_struct coredump_work;
struct work_struct hwexp_work;
struct work_struct fwtrigger_work;
+ struct work_struct mbox_work;
struct dma_pool *dma_pool;
dma_addr_t dma_p_addr;
@@ -535,10 +793,15 @@ 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;
u32 debug_evt_size;
+ dma_addr_t debug_table_addr;
+ u32 debug_table_size;
+ struct btintel_pcie_dump_mem_info dump_info;
+ struct btintel_pcie_mbox mbox;
};
static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH v1] Bluetooth: btintel_pcie: support unified decoder coredump format
2026-07-15 15:53 [PATCH v1] Bluetooth: btintel_pcie: support unified decoder coredump format Chandrashekar Devegowda
@ 2026-07-15 16:31 ` Luiz Augusto von Dentz
2026-07-15 17:05 ` [v1] " bluez.test.bot
1 sibling, 0 replies; 3+ messages in thread
From: Luiz Augusto von Dentz @ 2026-07-15 16:31 UTC (permalink / raw)
To: Chandrashekar Devegowda
Cc: linux-bluetooth, bhelgaas, ravishankar.srivatsa,
chethan.tumkur.narayan, Kiran K
Hi Chandru,
On Wed, Jul 15, 2026 at 12:03 PM Chandrashekar Devegowda
<chandrashekar.devegowda@intel.com> wrote:
>
> Refactor the coredump collection to use a unified decoder format
> that consolidates exception dump, DCCM, SDS, SDS IOSF, ECL, and
> SMEM memory regions into a structured TLV-based dump. This enables
> a single decoder tool to parse all dump types across different
> Intel BT PCIe controller variants (BlazarI/W, ScP, ScP2/F, Draco).
>
> Key changes:
> - Parse dump memory region addresses from FW-provided TLV data
> - Add per-variant DBGC register offsets for wrap counter and
> write pointer status
> - Collect and emit coredump sections with proper headers for
> the unified decoder tool
Could you split into 3 independent changes then, looks like its going
to be a substantial change.
> Signed-off-by: Kiran K <kiran.k@intel.com>
> Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
> ---
> drivers/bluetooth/btintel.c | 12 +
> drivers/bluetooth/btintel.h | 1 +
> drivers/bluetooth/btintel_pcie.c | 1193 +++++++++++++++++++++++++-----
> drivers/bluetooth/btintel_pcie.h | 267 ++++++-
> 4 files changed, 1299 insertions(+), 174 deletions(-)
>
> diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c
> index 5e9cac090bd8..b0b8c38ae9e4 100644
> --- a/drivers/bluetooth/btintel.c
> +++ b/drivers/bluetooth/btintel.c
> @@ -65,6 +65,7 @@ static struct {
> const char *driver_name;
> u8 hw_variant;
> u32 fw_build_num;
> + u32 fw_git_sha1;
I just name it fw_sha, if that comes from git or not doesn't matter to
the driver.
> } coredump_info;
>
> const guid_t btintel_guid_dsm =
> @@ -559,6 +560,7 @@ int btintel_version_info_tlv(struct hci_dev *hdev,
>
> coredump_info.hw_variant = INTEL_HW_VARIANT(version->cnvi_bt);
> coredump_info.fw_build_num = version->build_num;
> + coredump_info.fw_git_sha1 = version->git_sha1;
>
> bt_dev_info(hdev, "%s timestamp %u.%u buildtype %u build %u", variant,
> 2000 + (version->timestamp >> 8), version->timestamp & 0xff,
> @@ -2448,6 +2450,13 @@ static int btintel_prepare_fw_download_tlv(struct hci_dev *hdev,
> btintel_reset_to_bootloader(hdev);
>
> done:
> + {
> + struct btintel_data *intel_data = hci_get_priv(hdev);
> +
> + intel_data->cnvi_bt = ver->cnvi_bt;
> + bt_dev_dbg(hdev, "INTEL HW VARIANT is: 0x%2x",
> + INTEL_HW_VARIANT(intel_data->cnvi_bt));
> + }
> release_firmware(fw);
> return err;
> }
> @@ -3299,6 +3308,9 @@ int btintel_bootloader_setup_tlv(struct hci_dev *hdev,
>
> btintel_version_info_tlv(hdev, &new_ver);
>
> + /* Update ver with the operational firmware version */
> + *ver = new_ver;
> +
> finish:
> /* Set the event mask for Intel specific vendor events. This enables
> * a few extra events that are useful during general operation. It
> diff --git a/drivers/bluetooth/btintel.h b/drivers/bluetooth/btintel.h
> index 37d93abdd5a3..02a37a3d459d 100644
> --- a/drivers/bluetooth/btintel.h
> +++ b/drivers/bluetooth/btintel.h
> @@ -229,6 +229,7 @@ enum {
> struct btintel_data {
> DECLARE_BITMAP(flags, __INTEL_NUM_FLAGS);
> int (*acpi_reset_method)(struct hci_dev *hdev);
> + u32 cnvi_bt;
> };
>
> #define btintel_set_flag(hdev, nr) \
> diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
> index 013568197a39..3a714854ff50 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>
> @@ -73,17 +74,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_BLZR_HWEXP_SIZE 1024
> -#define BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR 0xB00A7C00
> -
> -#define BTINTEL_PCIE_SCP_HWEXP_SIZE 4096
> -#define BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR 0xB030F800
> -
> -#define BTINTEL_PCIE_SCP2_HWEXP_SIZE 4096
> -#define BTINTEL_PCIE_SCP2_HWEXP_DMP_ADDR 0xB031D000
> -
> #define BTINTEL_PCIE_MAGIC_NUM 0xA5A5A5A5
>
> #define BTINTEL_PCIE_TRIGGER_REASON_USER_TRIGGER 0x17A2
> @@ -145,6 +135,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];
Why don't we store index and count inside btintel_pcie_dbgc_ctxt_buf?
I don't really follow why we need 3 fixed arrays of buffers?
> +};
> +
> struct btintel_pcie_trigger_evt {
> u8 type;
> u8 len;
> @@ -186,6 +190,127 @@ static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt)
> }
> }
>
> +/* 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_buf: 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,
> + void **v_addr,
> + struct btintel_pcie_dbgc_ctxt_buf *frag_buf,
> + u32 buf_index,
> + u32 buf_count)
> +{
> + struct data_buf *b;
> + int i;
> +
> + *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;
> + }
> +
> + *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_buf[i].buf_addr_lsb = lower_32_bits(b->data_p_addr);
> + frag_buf[i].buf_addr_msb = upper_32_bits(b->data_p_addr);
> + frag_buf[i].buf_size = BTINTEL_PCIE_DBGC_BUFFER_SIZE;
> + }
> +
> + return 0;
> +}
> +
> +/* 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;
> + int err;
> +
> + data->mdbgc.count = BTINTEL_PCIE_DBGC_BUFFER_COUNT;
> +
> + /* Allocate fragment context structure */
> + data->mdbgc.frag_v_addr = dmam_alloc_coherent(&data->pdev->dev,
> + sizeof(struct btintel_pcie_mdbgc_ctxt),
> + &data->mdbgc.frag_p_addr,
> + GFP_KERNEL | __GFP_NOWARN);
> + if (!data->mdbgc.frag_v_addr) {
> + BT_ERR("Failed to allocate mdbgc context");
> + return -ENOMEM;
> + }
> +
> + data->mdbgc.frag_size = sizeof(struct btintel_pcie_mdbgc_ctxt);
> +
> + /* 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;
> +
> + /* 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;
> +
> + /* 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 and formats the
> * DBGC fragment which consists header info and DBGC buffer's LSB, MSB and
> * size as the payload
> @@ -193,46 +318,41 @@ static inline char *btintel_pcie_alivectxt_state2str(u32 alive_intr_ctxt)
> static int btintel_pcie_setup_dbgc(struct btintel_pcie_data *data)
> {
> struct btintel_pcie_dbgc_ctxt db_frag;
> - struct data_buf *buf;
> - int i;
> + int err;
>
> 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)
> - 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)
> - return -ENOMEM;
>
> + /* 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)
> + 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;
>
> - 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;
> - }
> + /* 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,
> + 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;
> }
> @@ -639,63 +759,440 @@ 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 const char *btintel_pcie_dumptype_to_str(struct hci_dev *hdev,
> + u8 dumptype)
> {
> - struct intel_tlv *tlv;
> + switch (dumptype) {
> + case BTINTEL_DRAM_REGION:
> + return "DRAM_DUMP";
> + case BTINTEL_SMEM_REGION:
> + return "MONITOR_SMEM";
> + case BTINTEL_DCCM_REGION:
> + return "DCCM_DUMP";
> + case BTINTEL_SDS_REGION:
> + return "SDS_DUMP";
> + case BTINTEL_SDS_IOSF_DATA_REGION:
> + return "SDS_IOSF_DATA_DUMP";
> + case BTINTEL_SDS_FIXED_ROM_REGION:
> + return "SDS_FIXED_ROM_DUMP";
> + case BTINTEL_EXCEPTION_EVT_DEBUG_REGION:
> + return "EXCEPTION_EVT_DEBUG_DATA";
> + case BTINTEL_ECL_REGION:
> + return "ECL_DUMP";
> + default:
> + bt_dev_dbg(hdev, "Unknown dump type: %d", dumptype);
> + return "UNKNOWN_DUMP_TYPE";
> + }
> +}
> +
> +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;
>
> - tlv = dest;
> - tlv->type = type;
> - tlv->len = size;
> - memcpy(tlv->val, data, tlv->len);
> - return dest + sizeof(*tlv) + size;
> + nents = DIV_ROUND_UP(size, PAGE_SIZE);
> + result = kcalloc(nents + 1, sizeof(*result), GFP_KERNEL);
> + if (!result)
> + return NULL;
> +
> + sg_init_table(result, nents + 1);
> + 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;
> + }
> + sg_mark_end(sg - 1);
> + 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 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,
> + 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_read_dram_buffers(struct btintel_pcie_data *data)
> +static int btintel_pcie_dump_target_region(struct btintel_pcie_data *data,
> + struct list_head *list,
> + u8 region_type, u32 region_id,
> + const char *name,
> + u32 addr_start, u32 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;
> + 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;
> + }
> +
> + /* Align to 4 bytes - target access reads 32-bit words */
> + size = round_down(size, sizeof(u32));
> +
> + bt_dev_info(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 = region_type;
> + 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(data->dmp_hdr.cnvi_top & 0xFFFF);
> + bt_dev_dbg(data->hdev, "hw_type=0x%x (cnvi_top=0x%x)",
> + data->dmp_hdr.cnvi_top & 0xFFFF,
> + 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_git_sha1);
> + dump->hw_step = cpu_to_le32(data->cnvr);
> + dump->rf_id_type = cpu_to_le32(data->dmp_hdr.cnvr_top & 0xFFF);
> + dump->rf_id_flavor = cpu_to_le32(data->dmp_hdr.cnvr_top);
> + bt_dev_dbg(data->hdev, "rf_id_type=0x%x (cnvr_top=0x%x)",
> + data->dmp_hdr.cnvr_top & 0xFFF,
> + 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_git_sha1);
> +
> + 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 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_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 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 region_size;
> + u32 exception_dump_len;
> + 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;
>
> + bt_dev_info(data->hdev, "%s: WRT dump triggered",
> + data->dmp_hdr.trigger_reason == BTINTEL_PCIE_TRIGGER_REASON_FW_ASSERT ?
> + "Firmware" : "User");
> +
> + 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:
> - 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:
> - 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)",
> - hw_variant);
> +
> + if (hw_variant == BTINTEL_HWID_BZRI ||
> + hw_variant == BTINTEL_HWID_BZRIW) {
> + wr_ptr_status =
> + btintel_pcie_rd_dev_mem(data,
> + BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS);
> + data->dmp_hdr.wrap_ctr =
> + btintel_pcie_rd_dev_mem(data,
> + BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND);
> + } else if (hw_variant >= BTINTEL_HWID_SCP) {
> + wr_ptr_status =
> + btintel_pcie_rd_dev_mem(data,
> + BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS_SCP);
> + data->dmp_hdr.wrap_ctr =
> + btintel_pcie_rd_dev_mem(data,
> + BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP);
> + } 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);
> -
> 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;
> }
> @@ -706,102 +1203,193 @@ 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_git_sha1) +
> - 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);
> +
> + smem_rd_addr = data->dump_info.smem_addr_start;
> + smem_rd_size = (data->dump_info.smem_addr_end + 0x04) -
> + data->dump_info.smem_addr_start;
> +
> + bt_dev_info(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;
> }
>
> - /*
> - * sizeof(u32) - signature
> - * sizeof(data_len) - to store tlv data size
> - * data_len - TLV data
> - */
> - dump_size = sizeof(u32) + sizeof(data_len) + data_len;
> + 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) {
> + ret = btintel_pcie_dump_smem_monitor_region(data, &dump_list,
> + BTINTEL_PCIE_INI_ID_SMEM,
> + "monitor_smem",
> + smem_rd_addr, smem_rd_size);
> + if (!ret)
> + regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SMEM);
> + }
> +
> + exception_dump_len = data->dump_info.exception_dump_len;
> + ret = btintel_pcie_dump_target_region(data, &dump_list,
> + BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
> + BTINTEL_PCIE_INI_ID_EXCEPTION_EVT,
> + "EXCEPTION_EVT_BUFFER",
> + data->dump_info.exception_dump_addr,
> + exception_dump_len);
> + if (!ret && exception_dump_len)
> + regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_EXCEPTION_EVT);
> +
> + if (data->dump_info.dccm_addr_start &&
> + data->dump_info.dccm_addr_end &&
> + data->dump_info.dccm_addr_end >= data->dump_info.dccm_addr_start) {
> + region_size = (data->dump_info.dccm_addr_end + 0x04) -
> + data->dump_info.dccm_addr_start;
> + ret = btintel_pcie_dump_target_region(data, &dump_list,
> + BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
> + BTINTEL_PCIE_INI_ID_DCCM,
> + "DCCM",
> + data->dump_info.dccm_addr_start,
> + region_size);
> + if (!ret)
> + regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_DCCM);
> + }
> +
> + if (data->dump_info.sds_start_addr_start &&
> + data->dump_info.sds_start_addr_end &&
> + data->dump_info.sds_start_addr_end >= data->dump_info.sds_start_addr_start) {
> + region_size = (data->dump_info.sds_start_addr_end + 0x04) -
> + data->dump_info.sds_start_addr_start;
> + ret = btintel_pcie_dump_target_region(data, &dump_list,
> + BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
> + BTINTEL_PCIE_INI_ID_SDS,
> + "SDS",
> + data->dump_info.sds_start_addr_start,
> + region_size);
> + if (!ret)
> + regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SDS);
> + }
> +
> + if (data->dump_info.sds_iosf_data_addr_start &&
> + data->dump_info.sds_iosf_data_addr_end &&
> + data->dump_info.sds_iosf_data_addr_end >= data->dump_info.sds_iosf_data_addr_start) {
> + region_size = (data->dump_info.sds_iosf_data_addr_end + 0x04) -
> + data->dump_info.sds_iosf_data_addr_start;
> + ret = btintel_pcie_dump_target_region(data, &dump_list,
> + BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
> + BTINTEL_PCIE_INI_ID_SDS_IOSF,
> + "SDS_IOSF",
> + data->dump_info.sds_iosf_data_addr_start,
> + region_size);
> + if (!ret)
> + regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SDS_IOSF);
> + }
>
> + if (data->dump_info.ecl_addr_start &&
> + data->dump_info.ecl_addr_end &&
> + data->dump_info.ecl_addr_end >= data->dump_info.ecl_addr_start) {
> + region_size = (data->dump_info.ecl_addr_end + 0x04) -
> + data->dump_info.ecl_addr_start;
> + ret = btintel_pcie_dump_target_region(data, &dump_list,
> + BTINTEL_PCIE_INI_REGION_DEVICE_MEMORY,
> + BTINTEL_PCIE_INI_ID_ECL,
> + "ECL_REGION",
> + data->dump_info.ecl_addr_start,
> + region_size);
> + if (!ret)
> + regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_ECL);
> + }
>
> - /* Add debug buffers data length to dump size */
> - dump_size += BTINTEL_PCIE_DBGC_BUFFER_SIZE * dbgc->count;
> + ret = btintel_pcie_dump_info(data, &dump_list, regions_mask);
> + if (ret) {
> + btintel_pcie_dump_list_free(&dump_list);
> + return ret;
> + }
>
> - pdata = vmalloc(dump_size);
> - if (!pdata)
> + 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_git_sha1,
> - sizeof(data->dmp_hdr.fw_git_sha1));
> - 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);
> + }
> +
> + 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_info(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");
> + }
> +
> + btintel_pcie_dump_list_free(&dump_list);
> return 0;
> }
>
> 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) {
> @@ -809,7 +1397,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);
>
> @@ -957,10 +1545,241 @@ static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data)
> return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER;
> }
>
> +static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data)
> +{
> + /* Custom TLV structure for mailbox parsing
> + * len is __le16 as per agreemnt with FW
> + */
> + struct mbox_tlv {
> + u8 type;
> + __le16 len;
> + u8 val[];
> + } __packed;
> +
> + u8 *buffer, *ptr;
> + u32 buffer_len, err, remaining;
> + struct mbox_tlv *tlv;
> + struct btintel_data *cnvi_data = hci_get_priv(data->hdev);
> + u8 hw_variant = INTEL_HW_VARIANT(cnvi_data->cnvi_bt);
> +
> + if (!data->debug_table_size || !data->debug_table_addr)
> + return -EINVAL;
> +
> + buffer_len = data->debug_table_size;
> +
> + buffer = vmalloc(buffer_len);
> + if (!buffer)
> + return -ENOMEM;
> +
> + btintel_pcie_mac_init(data);
> +
> + err = btintel_pcie_read_device_mem(data, buffer, data->debug_table_addr,
> + buffer_len);
> + if (err)
> + goto exit_on_error;
> +
> + print_hex_dump_debug("dump_info: ", DUMP_PREFIX_OFFSET, 16, 1,
> + buffer, buffer_len, false);
> +
> + ptr = buffer;
> + remaining = buffer_len;
> +
> + /* 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;
> +
> + if (tlv_total > remaining) {
> + bt_dev_err(data->hdev, "TLV parse error: not enough data for TLV value (type=%u, len=%u)",
> + tlv->type, tlv_len);
> + break;
> + }
> +
> + switch (tlv->type) {
> + case BTINTEL_PCIE_TLV_TYPE_EXCEPTION_DUMP_ADDRESS:
> + if (tlv_len < 8) {
> + bt_dev_err(data->hdev,
> + "TLV %s too short: len=%u (need 8)",
> + btintel_pcie_dumptype_to_str(data->hdev,
> + tlv->type),
> + tlv_len);
> + break;
> + }
> + data->dump_info.exception_dump_addr = get_unaligned_le32(&tlv->val[0]);
> + data->dump_info.exception_dump_len = get_unaligned_le32(&tlv->val[4]);
> + break;
> + case BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS:
> + if (tlv_len < 8) {
> + bt_dev_err(data->hdev,
> + "TLV %s too short: len=%u (need 8)",
> + btintel_pcie_dumptype_to_str(data->hdev,
> + tlv->type),
> + tlv_len);
> + break;
> + }
> + data->dump_info.dccm_addr_start = get_unaligned_le32(&tlv->val[0]);
> + data->dump_info.dccm_addr_end = get_unaligned_le32(&tlv->val[4]);
> + break;
> + case BTINTEL_PCIE_TLV_TYPE_SDS_MEM_ADDRESS:
> + 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 =
> + get_unaligned_le32(&tlv->val[4]);
> + data->dump_info.sds_iosf_data_addr_start =
> + get_unaligned_le32(&tlv->val[8]);
> + data->dump_info.sds_iosf_data_addr_end =
> + get_unaligned_le32(&tlv->val[12]);
> + } else if (tlv_len == 24 && (hw_variant == BTINTEL_HWID_BZRI ||
> + hw_variant == BTINTEL_HWID_BZRIW)) {
> + data->dump_info.sds_fixed_rom_addr_start =
> + get_unaligned_le32(&tlv->val[0]);
> + data->dump_info.sds_fixed_rom_addr_end =
> + get_unaligned_le32(&tlv->val[4]);
> + data->dump_info.sds_start_addr_start =
> + get_unaligned_le32(&tlv->val[8]);
> + data->dump_info.sds_start_addr_end =
> + get_unaligned_le32(&tlv->val[12]);
> + data->dump_info.sds_iosf_data_addr_start =
> + get_unaligned_le32(&tlv->val[16]);
> + data->dump_info.sds_iosf_data_addr_end =
> + get_unaligned_le32(&tlv->val[20]);
> + } else {
> + bt_dev_err(data->hdev, "SDS TLV: unsupported hw_variant=0x%02x len=%u",
> + hw_variant, tlv_len);
> + }
> + break;
> + case BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS:
> + if (tlv_len < 8) {
> + bt_dev_err(data->hdev,
> + "TLV %s too short: len=%u (need 8)",
> + btintel_pcie_dumptype_to_str(data->hdev,
> + tlv->type),
> + tlv_len);
> + break;
> + }
> + data->dump_info.ecl_addr_start = get_unaligned_le32(&tlv->val[0]);
> + data->dump_info.ecl_addr_end = get_unaligned_le32(&tlv->val[4]);
> + break;
> + case BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS:
> + if (tlv_len < 8) {
> + bt_dev_err(data->hdev,
> + "TLV %s too short: len=%u (need 8)",
> + btintel_pcie_dumptype_to_str(data->hdev,
> + tlv->type),
> + tlv_len);
> + break;
> + }
> + data->dump_info.smem_addr_start = get_unaligned_le32(&tlv->val[0]);
> + data->dump_info.smem_addr_end = get_unaligned_le32(&tlv->val[4]);
> + break;
> + default:
> + bt_dev_dbg(data->hdev, "Unknown TLV type: %u length: %u",
> + tlv->type, tlv_len);
> + break;
> + }
> +
> + /* Move to next TLV */
> + ptr += tlv_total;
> + remaining -= tlv_total;
> + }
> +
> + bt_dev_info(data->hdev,
> + "exception_dump: addr:0x%08x len:0x%08x",
> + data->dump_info.exception_dump_addr,
> + data->dump_info.exception_dump_len);
> + bt_dev_info(data->hdev,
> + "dccm: start:0x%08x end:0x%08x",
> + data->dump_info.dccm_addr_start,
> + data->dump_info.dccm_addr_end);
> + bt_dev_info(data->hdev,
> + "sds_fixed_rom: start:0x%08x end:0x%08x",
> + data->dump_info.sds_fixed_rom_addr_start,
> + data->dump_info.sds_fixed_rom_addr_end);
> + bt_dev_info(data->hdev,
> + "sds: start:0x%08x end:0x%08x",
> + data->dump_info.sds_start_addr_start,
> + data->dump_info.sds_start_addr_end);
> + bt_dev_info(data->hdev,
> + "sds_iosf: start:0x%08x end:0x%08x",
> + data->dump_info.sds_iosf_data_addr_start,
> + data->dump_info.sds_iosf_data_addr_end);
> + bt_dev_info(data->hdev,
> + "ecl: start:0x%08x end:0x%08x",
> + data->dump_info.ecl_addr_start,
> + data->dump_info.ecl_addr_end);
> + bt_dev_info(data->hdev,
> + "smem: start:0x%08x end:0x%08x",
> + data->dump_info.smem_addr_start,
> + data->dump_info.smem_addr_end);
Not much of a fan to use bt_dev_info to dump memory containts like
this, actually why is this not sent as a devcoredump?
> +
> + vfree(buffer);
> + return 0;
> +
> +exit_on_error:
> + vfree(buffer);
> + return err;
> +}
> +
> static void btintel_pcie_msix_gp1_handler(struct btintel_pcie_data *data)
> {
> - bt_dev_err(data->hdev, "Received gp1 mailbox interrupt");
> - btintel_pcie_dump_debug_registers(data->hdev);
> + bool target_access = false;
> + 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);
> + if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX1) {
> + 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");
> + target_access = true;
> + }
> + }
> +
> + if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX2) {
> + data->mbox.mbox2 = btintel_pcie_rd_reg32(data,
> + BTINTEL_PCIE_CSR_MBOX_2_REG);
> + if (target_access)
> + data->debug_table_addr = data->mbox.mbox2;
> + }
> +
> + if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX3) {
> + data->mbox.mbox3 = btintel_pcie_rd_reg32(data,
> + BTINTEL_PCIE_CSR_MBOX_3_REG);
> + if (target_access)
> + data->debug_table_size = data->mbox.mbox3;
> + }
> +
> + if (data->mbox.mbox_status & BTINTEL_PCIE_CSR_MBOX_STATUS_MBOX4) {
> + data->mbox.mbox4 = btintel_pcie_rd_reg32(data,
> + BTINTEL_PCIE_CSR_MBOX_4_REG);
> + }
> +
> + bt_dev_err(data->hdev, "GP1_handler: mbox status: 0x%8.8x mbox_1: 0x%8.8x mbox_2: 0x%8.8x mbox_3: 0x%8.8x mbox_4: 0x%8.8x",
> + data->mbox.mbox_status, data->mbox.mbox1, data->mbox.mbox2,
> + data->mbox.mbox3, data->mbox.mbox4);
Ok, this is definitely not within 80 columns. You know we still have
use max 80 columns for the Bluetooth subsystem right?
> + if (target_access && !test_and_set_bit(BTINTEL_PCIE_MAIL_BOX_INTR, &data->flags)) {
> + if (!queue_work(data->dump_workqueue, &data->mbox_work))
> + clear_bit(BTINTEL_PCIE_MAIL_BOX_INTR, &data->flags);
> + }
> +
> + /* Mailbox is read, ack to FW */
> + 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_info(data->hdev, "Mailbox acked in %llu usecs", duration);
> }
>
> /* This function handles the MSI-X interrupt for gp0 cause (bit 0 in
> @@ -1307,23 +2126,25 @@ static void btintel_pcie_read_hwexp(struct btintel_pcie_data *data)
> /* only from step B0 onwards */
> if (INTEL_CNVX_TOP_STEP(data->dmp_hdr.cnvi_top) != 0x01)
> return;
> - len = BTINTEL_PCIE_BLZR_HWEXP_SIZE; /* exception data length */
> - addr = BTINTEL_PCIE_BLZR_HWEXP_DMP_ADDR;
> break;
> case BTINTEL_CNVI_SCP:
> - len = BTINTEL_PCIE_SCP_HWEXP_SIZE;
> - addr = BTINTEL_PCIE_SCP_HWEXP_DMP_ADDR;
> - break;
> case BTINTEL_CNVI_SCP2:
> case BTINTEL_CNVI_SCP2F:
> - len = BTINTEL_PCIE_SCP2_HWEXP_SIZE;
> - addr = BTINTEL_PCIE_SCP2_HWEXP_DMP_ADDR;
> break;
> default:
> bt_dev_err(data->hdev, "Unsupported cnvi 0x%8.8x", data->dmp_hdr.cnvi_top);
> return;
> }
>
> + len = data->dump_info.exception_dump_len;
> + addr = data->dump_info.exception_dump_addr;
> +
> + if (!addr || !len) {
> + bt_dev_err(data->hdev, "Invalid exception address: 0x%8.8x or length: %d",
> + addr, len);
> + return;
> + }
> +
> buf = kzalloc(len, GFP_KERNEL);
> if (!buf)
> goto exit_on_error;
> @@ -1576,6 +2397,20 @@ static void btintel_pcie_fwtrigger_worker(struct work_struct *work)
> clear_bit(BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS, &data->flags);
> }
>
> +static void btintel_pcie_mbox_worker(struct work_struct *work)
> +{
> + struct btintel_pcie_data *data = container_of(work,
> + struct btintel_pcie_data, mbox_work);
> +
> + if (!data->hdev)
> + goto out;
> +
> + btintel_parse_mbox_tlv(data);
> +out:
> + /* Release guard last; matches set in gp1 handler. */
> + clear_bit(BTINTEL_PCIE_MAIL_BOX_INTR, &data->flags);
> +}
> +
> static void btintel_pcie_rx_work(struct work_struct *work)
> {
> struct btintel_pcie_data *data = container_of(work,
> @@ -1886,9 +2721,16 @@ static void btintel_pcie_init_ci(struct btintel_pcie_data *data,
> ci->num_urbdq1 = data->rxq.count;
> ci->urbdq_db_vec = BTINTEL_PCIE_RXQ_NUM;
>
> - ci->dbg_output_mode = 0x01;
> - ci->dbgc_addr = data->dbgc.frag_p_addr;
> - ci->dbgc_size = data->dbgc.frag_size;
> + ci->dbg_output_mode = BTINTEL_PCIE_DRAM;
> +
> + 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;
> }
>
> @@ -2116,7 +2958,10 @@ static int btintel_pcie_alloc(struct btintel_pcie_data *data)
> v_addr += ci_size;
>
> /* Setup data buffers for dbgc */
> - err = btintel_pcie_setup_dbgc(data);
> + if (btintel_pcie_is_mdbgc_supported(data))
> + err = btintel_pcie_setup_mdbgc(data);
> + else
> + err = btintel_pcie_setup_dbgc(data);
> if (err)
> goto exit_error_txq;
>
> @@ -2380,6 +3225,7 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
> goto exit_error;
> }
>
> + data->dmp_hdr.cnvi_bt = ver_tlv.cnvi_bt;
> switch (INTEL_HW_PLATFORM(ver_tlv.cnvi_bt)) {
> case 0x37:
> break;
> @@ -2433,7 +3279,6 @@ static int btintel_pcie_setup_internal(struct hci_dev *hdev)
> data->dmp_hdr.fw_timestamp = ver_tlv.timestamp;
> data->dmp_hdr.fw_build_type = ver_tlv.build_type;
> data->dmp_hdr.fw_build_num = ver_tlv.build_num;
> - data->dmp_hdr.cnvi_bt = ver_tlv.cnvi_bt;
>
> if (ver_tlv.img_type == 0x02 || ver_tlv.img_type == 0x03)
> data->dmp_hdr.fw_git_sha1 = ver_tlv.git_sha1;
> @@ -2718,6 +3563,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
> disable_work_sync(&data->coredump_work);
> disable_work_sync(&data->hwexp_work);
> disable_work_sync(&data->fwtrigger_work);
> + disable_work_sync(&data->mbox_work);
>
> bt_dev_dbg(data->hdev, "Release bluetooth interface");
>
> @@ -2742,6 +3588,7 @@ static void btintel_pcie_reset_work(struct work_struct *wk)
> enable_work(&data->coredump_work);
> enable_work(&data->hwexp_work);
> enable_work(&data->fwtrigger_work);
> + enable_work(&data->mbox_work);
> }
>
> out:
> @@ -2948,6 +3795,7 @@ static int btintel_pcie_probe(struct pci_dev *pdev,
> INIT_WORK(&data->coredump_work, btintel_pcie_coredump_worker);
> INIT_WORK(&data->hwexp_work, btintel_pcie_hwexp_worker);
> INIT_WORK(&data->fwtrigger_work, btintel_pcie_fwtrigger_worker);
> + INIT_WORK(&data->mbox_work, btintel_pcie_mbox_worker);
>
> data->boot_stage_cache = 0x00;
> data->img_resp_cache = 0x00;
> @@ -3017,6 +3865,7 @@ static void btintel_pcie_remove(struct pci_dev *pdev)
> disable_work_sync(&data->coredump_work);
> disable_work_sync(&data->hwexp_work);
> disable_work_sync(&data->fwtrigger_work);
> + disable_work_sync(&data->mbox_work);
>
> /* Cancel pending reset work. Skip only when remove() is called from
> * within the reset work itself (PLDR device_reprobe path) to avoid
> diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
> index 749369b24031..93aaa1d1e187 100644
> --- a/drivers/bluetooth/btintel_pcie.h
> +++ b/drivers/bluetooth/btintel_pcie.h
> @@ -23,6 +23,8 @@
> #define BTINTEL_PCIE_CSR_MBOX_3_REG (BTINTEL_PCIE_CSR_BASE + 0x178)
> #define BTINTEL_PCIE_CSR_MBOX_4_REG (BTINTEL_PCIE_CSR_BASE + 0x17C)
> #define BTINTEL_PCIE_CSR_MBOX_STATUS_REG (BTINTEL_PCIE_CSR_BASE + 0x180)
> +#define BTINTEL_PCIE_CSR_IPC_DOORBELL_VEC_REG (BTINTEL_PCIE_CSR_BASE + 0x130)
> +#define BTINTEL_PCIE_CSR_DOORBELL_MBOX_READ_CONFIRM (BIT(4))
> #define BTINTEL_PCIE_PRPH_DEV_ADDR_REG (BTINTEL_PCIE_CSR_BASE + 0x440)
> #define BTINTEL_PCIE_PRPH_DEV_RD_REG (BTINTEL_PCIE_CSR_BASE + 0x458)
> #define BTINTEL_PCIE_CSR_HBUS_TARG_WRPTR (BTINTEL_PCIE_CSR_BASE + 0x460)
> @@ -67,15 +69,26 @@
> #define BTINTEL_PCIE_DBGC_BASE_ADDR (0xf3800300)
> #define BTINTEL_PCIE_DBGC_CUR_DBGBUFF_STATUS (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x1C)
> #define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x2C)
> +#define BTINTEL_PCIE_DBGC_IF_BASE_ADDR (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x30)
> +#define BTINTEL_PCIE_DBGC_IF_SIZE (BTINTEL_PCIE_DBGC_BASE_ADDR + 0x34)
> +
> +#define BTINTEL_PCIE_SMEM_BASE_ADDR (0xf4000000)
> +#define BTINTEL_PCIE_SMEM_END_ADDR (BTINTEL_PCIE_SMEM_BASE_ADDR + 0x4000)
> +
> +#define BTINTEL_PCIE_DBGC_BASE_ADDR_BZRI (0xf3800300)
>
> #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_SMEM_FRAGMENT_OFFSET 4
> +#define BTINTEL_PCIE_SMEM_MAX_SIZE (16 * 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
> */
> @@ -91,6 +104,27 @@
> /* Num of alloc Dbg buff (4) + (LSB(4), MSB(4), Size(4)) for each buffer */
> #define BTINTEL_PCIE_DBGC_FRAG_PAYLOAD_SIZE 196
>
> +#define BTINTEL_PCIE_MDBGC_FRAG_VERSION 2
> +#define BTINTEL_PCIE_MDBGC_TOTAL_BUFFER_COUNT (16 * 3)
> +
> +#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_1 0
> +#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_2 1
> +#define BTINTEL_PCIE_MDBGC_ALLOCATIONID_3 2
> +
> +/* Magic number(4), version(4), size of payload length(4) */
> +#define BTINTEL_PCIE_MDBGC_FRAG_HEADER_SIZE 12
> +
> +/* Num of alloc Dbg buff (4) + (LSB(4), MSB(4), Size(4)) for each buffer */
> +#define BTINTEL_PCIE_MDBGC_FRAG_PAYLOAD_SIZE 196
> +
> +#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
> +
> +#define BTINTEL_PCIE_SMEM 0x00
> +#define BTINTEL_PCIE_DRAM 0x01
> +#define BTINTEL_PCIE_FW_MON_MODE_DRAM 0x02
> +
> /* Causes for the FH register interrupts */
> enum msix_fh_int_causes {
> BTINTEL_PCIE_MSIX_FH_INT_CAUSES_0 = BIT(0), /* cause 0 */
> @@ -121,7 +155,8 @@ enum {
> BTINTEL_PCIE_COREDUMP_INPROGRESS,
> BTINTEL_PCIE_FWTRIGGER_DUMP_INPROGRESS,
> BTINTEL_PCIE_RECOVERY_IN_PROGRESS,
> - BTINTEL_PCIE_SETUP_DONE
> + BTINTEL_PCIE_SETUP_DONE,
> + BTINTEL_PCIE_MAIL_BOX_INTR
> };
>
> enum btintel_pcie_tlv_type {
> @@ -137,7 +172,20 @@ enum btintel_pcie_tlv_type {
> BTINTEL_VENDOR,
> BTINTEL_DRIVER,
> BTINTEL_EVENT_TYPE,
> - BTINTEL_EVENT_ID
> + BTINTEL_EVENT_ID,
> + BTINTEL_DRAM_REGION,
> + BTINTEL_SMEM_REGION,
> + BTINTEL_DCCM_BASE_ADDR,
> + BTINTEL_DCCM_REGION,
> + BTINTEL_SDS_BASE_ADDR,
> + BTINTEL_SDS_REGION,
> + BTINTEL_SDS_IOSF_BASE_ADDR,
> + BTINTEL_SDS_IOSF_DATA_REGION,
> + BTINTEL_SDS_FIXED_ROM_BASE_ADDR,
> + BTINTEL_SDS_FIXED_ROM_REGION,
> + BTINTEL_EXCEPTION_EVT_DEBUG_REGION,
> + BTINTEL_ECL_BASE_ADDR,
> + BTINTEL_ECL_REGION
> };
>
> /* causes for the MBOX interrupts */
> @@ -153,6 +201,14 @@ enum btintel_pcie_reset_type {
> BTINTEL_PCIE_IOSF_PRR_PLDR = 1,
> };
>
> +enum btintel_pcie_mbox_msg {
> + BTINTEL_PCIE_NO_USE = 0,
> + BTINTEL_PCIE_TOP_SILENT_RESET,
> + BTINTEL_PCIE_SB_AUDIO_DEVICE_REPORT,
> + BTINTEL_PCIE_BUILD_SPECIFIC_RESOURCES_MAPPING,
> + BTINTEL_PCIE_LAST_MESSAGE = 4095 /* 0xFFF= 0x100-0x1 */
> +};
> +
> #define BTINTEL_PCIE_MSIX_NON_AUTO_CLEAR_CAUSE BIT(7)
>
> /* Minimum and Maximum number of MSI-X Vector
> @@ -198,6 +254,12 @@ enum {
> /* RBD buffer size mapping */
> #define BTINTEL_PCIE_RBD_SIZE_4K 0x04
>
> +#define BTINTEL_PCIE_TLV_TYPE_EXCEPTION_DUMP_ADDRESS 0x04
> +#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
> +
> /*
> * Struct for Context Information (v2)
> *
> @@ -424,6 +486,62 @@ 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;
> +};
> +
> +/* Structure for packing dump to decoder
> + * @type: TLV type
> + * @len: The length of TLV is u32 as agreed with decoder to accomadate larger
> + * dumps
> + * @val: TLV value
> + */
> +struct intel_pcie_tlv {
> + u8 type;
> + u32 len;
> + u8 val[];
> +} __packed;
> +
> +struct btintel_pcie_dump_mem_info {
> + u32 exception_dump_addr;
> + u32 exception_dump_len;
> + u32 dccm_addr_start;
> + u32 dccm_addr_end;
> + u32 sds_fixed_rom_addr_start;
> + u32 sds_fixed_rom_addr_end;
> + u32 sds_start_addr_start;
> + u32 sds_start_addr_end;
> + u32 sds_iosf_data_addr_start;
> + u32 sds_iosf_data_addr_end;
> + u32 ecl_addr_start;
> + u32 ecl_addr_end;
> + u32 smem_addr_start;
> + u32 smem_addr_end;
> +};
> +
> +struct btintel_pcie_mbox {
> + u32 mbox_flags;
> + u32 mbox_status;
> + u32 mbox1;
> + u32 mbox2;
> + u32 mbox3;
> + u32 mbox4;
> +};
> +
> struct btintel_pcie_dump_header {
> const char *driver_name;
> u32 cnvi_top;
> @@ -441,6 +559,145 @@ struct btintel_pcie_dump_header {
> 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
> @@ -522,6 +779,7 @@ struct btintel_pcie_data {
> struct work_struct coredump_work;
> struct work_struct hwexp_work;
> struct work_struct fwtrigger_work;
> + struct work_struct mbox_work;
>
> struct dma_pool *dma_pool;
> dma_addr_t dma_p_addr;
> @@ -535,10 +793,15 @@ 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;
> u32 debug_evt_size;
> + dma_addr_t debug_table_addr;
> + u32 debug_table_size;
> + struct btintel_pcie_dump_mem_info dump_info;
> + struct btintel_pcie_mbox mbox;
> };
>
> static inline u32 btintel_pcie_rd_reg32(struct btintel_pcie_data *data,
> --
> 2.43.0
>
>
--
Luiz Augusto von Dentz
^ permalink raw reply [flat|nested] 3+ messages in thread* RE: [v1] Bluetooth: btintel_pcie: support unified decoder coredump format
2026-07-15 15:53 [PATCH v1] Bluetooth: btintel_pcie: support unified decoder coredump format Chandrashekar Devegowda
2026-07-15 16:31 ` Luiz Augusto von Dentz
@ 2026-07-15 17:05 ` bluez.test.bot
1 sibling, 0 replies; 3+ messages in thread
From: bluez.test.bot @ 2026-07-15 17:05 UTC (permalink / raw)
To: linux-bluetooth, chandrashekar.devegowda
[-- Attachment #1: Type: text/plain, Size: 1181 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=1128275
---Test result---
Test Summary:
CheckPatch PASS 1.70 seconds
VerifyFixes PASS 0.08 seconds
VerifySignedoff PASS 0.08 seconds
GitLint PASS 0.20 seconds
SubjectPrefix PASS 0.07 seconds
BuildKernel PASS 20.58 seconds
CheckAllWarning PASS 22.33 seconds
CheckSparse PASS 21.18 seconds
BuildKernel32 PASS 19.55 seconds
CheckKernelLLVM SKIP 0.00 seconds
TestRunnerSetup PASS 360.81 seconds
IncrementalBuild PASS 19.97 seconds
Details
##############################
Test: CheckKernelLLVM - SKIP
Desc: Build kernel with LLVM + context analysis
Output:
Clang not found
https://github.com/bluez/bluetooth-next/pull/445
---
Regards,
Linux Bluetooth
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-15 17:05 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-15 15:53 [PATCH v1] Bluetooth: btintel_pcie: support unified decoder coredump format Chandrashekar Devegowda
2026-07-15 16:31 ` Luiz Augusto von Dentz
2026-07-15 17:05 ` [v1] " bluez.test.bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox