diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c index b6d2932383cf..de0926c61ee7 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.c +++ b/drivers/net/wireless/ath/ath10k/coredump.c @@ -951,6 +951,19 @@ static const struct ath10k_mem_region qca4019_hw10_mem_regions[] = { }, }; +static const struct ath10k_mem_region wcn399x_hw10_mem_regions[] = { + { + /* MSA region start is not fixed, hence it is assigned at runtime */ + .type = ATH10K_MEM_REGION_TYPE_MSA, + .len = 0x100000, + .name = "DRAM", + .section_table = { + .sections = NULL, + .size = 0, + }, + }, +}; + static const struct ath10k_hw_mem_layout hw_mem_layouts[] = { { .hw_id = QCA6174_HW_1_0_VERSION, @@ -1048,6 +1061,14 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = { .size = ARRAY_SIZE(qca4019_hw10_mem_regions), }, }, + { + .hw_id = WCN3990_HW_1_0_DEV_VERSION, + .hw_rev = ATH10K_HW_WCN3990, + .region_table = { + .regions = wcn399x_hw10_mem_regions, + .size = ARRAY_SIZE(wcn399x_hw10_mem_regions), + }, + }, }; static u32 ath10k_coredump_get_ramdump_size(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/coredump.h b/drivers/net/wireless/ath/ath10k/coredump.h index 09de41922f97..8bf03e8c1d3a 100644 --- a/drivers/net/wireless/ath/ath10k/coredump.h +++ b/drivers/net/wireless/ath/ath10k/coredump.h @@ -115,6 +115,7 @@ enum ath10k_mem_region_type { ATH10K_MEM_REGION_TYPE_IRAM2 = 5, ATH10K_MEM_REGION_TYPE_IOSRAM = 6, ATH10K_MEM_REGION_TYPE_IOREG = 7, + ATH10K_MEM_REGION_TYPE_MSA = 8, }; /* Define a section of the region which should be copied. As not all parts diff --git a/drivers/net/wireless/ath/ath10k/qmi.c b/drivers/net/wireless/ath/ath10k/qmi.c index 59e15c946db8..fbc2e4ffc12b 100644 --- a/drivers/net/wireless/ath/ath10k/qmi.c +++ b/drivers/net/wireless/ath/ath10k/qmi.c @@ -829,9 +829,13 @@ ath10k_qmi_driver_event_post(struct ath10k_qmi *qmi, static void ath10k_qmi_event_server_exit(struct ath10k_qmi *qmi) { struct ath10k *ar = qmi->ar; + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); ath10k_qmi_remove_msa_permission(qmi); ath10k_core_free_board_files(ar); + if (!test_bit(ATH10K_SNOC_FLAG_UNREGISTERING, &ar_snoc->flags)) + ath10k_snoc_fw_crashed_dump(ar); + ath10k_snoc_fw_indication(ar, ATH10K_QMI_EVENT_FW_DOWN_IND); ath10k_dbg(ar, ATH10K_DBG_QMI, "wifi fw qmi service disconnected\n"); } diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index c8c92906a178..cd22c8654aa9 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -13,6 +13,7 @@ #include #include "ce.h" +#include "coredump.h" #include "debug.h" #include "hif.h" #include "htc.h" @@ -1389,6 +1390,71 @@ static int ath10k_hw_power_off(struct ath10k *ar) return regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs); } +static void ath10k_msa_dump_memory(struct ath10k *ar, + struct ath10k_fw_crash_data *crash_data) +{ + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + const struct ath10k_hw_mem_layout *mem_layout; + const struct ath10k_mem_region *current_region; + struct ath10k_dump_ram_data_hdr *hdr; + size_t buf_len; + u8 *buf; + + if (!crash_data && !crash_data->ramdump_buf) + return; + + mem_layout = ath10k_coredump_get_mem_layout(ar); + if (!mem_layout) + return; + + current_region = &mem_layout->region_table.regions[0]; + + buf = crash_data->ramdump_buf; + buf_len = crash_data->ramdump_buf_len; + memset(buf, 0, buf_len); + + /* Reserve space for the header. */ + hdr = (void *)buf; + buf += sizeof(*hdr); + buf_len -= sizeof(*hdr); + + hdr->region_type = cpu_to_le32(current_region->type); + hdr->start = cpu_to_le32((unsigned long)ar_snoc->qmi->msa_va); + hdr->length = cpu_to_le32(ar_snoc->qmi->msa_mem_size); + + if (current_region->len < ar_snoc->qmi->msa_mem_size) { + memcpy(buf, ar_snoc->qmi->msa_va, current_region->len); + ath10k_warn(ar, "msa dump length is less than msa size %x, %x\n", + current_region->len, ar_snoc->qmi->msa_mem_size); + } else { + memcpy(buf, ar_snoc->qmi->msa_va, ar_snoc->qmi->msa_mem_size); + } +} + +void ath10k_snoc_fw_crashed_dump(struct ath10k *ar) +{ + struct ath10k_fw_crash_data *crash_data; + char guid[UUID_STRING_LEN + 1]; + + mutex_lock(&ar->dump_mutex); + + spin_lock_bh(&ar->data_lock); + ar->stats.fw_crash_counter++; + spin_unlock_bh(&ar->data_lock); + + crash_data = ath10k_coredump_new(ar); + + if (crash_data) + scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid); + else + scnprintf(guid, sizeof(guid), "n/a"); + + ath10k_err(ar, "firmware crashed! (guid %s)\n", guid); + ath10k_print_driver_info(ar); + ath10k_msa_dump_memory(ar, crash_data); + mutex_unlock(&ar->dump_mutex); +} + static const struct of_device_id ath10k_snoc_dt_match[] = { { .compatible = "qcom,wcn3990-wifi", .data = &drv_priv, diff --git a/drivers/net/wireless/ath/ath10k/snoc.h b/drivers/net/wireless/ath/ath10k/snoc.h index bc2b925b1d5a..c05df45a3945 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.h +++ b/drivers/net/wireless/ath/ath10k/snoc.h @@ -79,5 +79,6 @@ static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar) } int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type); +void ath10k_snoc_fw_crashed_dump(struct ath10k *ar); #endif /* _SNOC_H_ */