diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/mac80211.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mac80211.c | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 9feca4a..9661a52 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -80,6 +80,8 @@ #include "fw-api-scan.h" #include "iwl-phy-db.h" #include "testmode.h" +#include "iwl-fw-error-dump.h" +#include "iwl-prph.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -645,6 +647,104 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, mvmvif->phy_ctxt = NULL; } +#ifdef CONFIG_IWLWIFI_DEBUGFS +static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) +{ + struct iwl_fw_error_dump_file *dump_file; + struct iwl_fw_error_dump_data *dump_data; + struct iwl_fw_error_dump_info *dump_info; + const struct fw_img *img; + u32 sram_len, sram_ofs; + u32 file_len, rxf_len; + unsigned long flags; + u32 trans_len; + int reg_val; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->fw_error_dump) + return; + + img = &mvm->fw->img[mvm->cur_ucode]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + + /* reading buffer size */ + reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR); + rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS; + + /* the register holds the value divided by 128 */ + rxf_len = rxf_len << 7; + + file_len = sizeof(*dump_file) + + sizeof(*dump_data) * 3 + + sram_len + + rxf_len + + sizeof(*dump_info); + + trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0); + if (trans_len) + file_len += trans_len; + + dump_file = vmalloc(file_len); + if (!dump_file) + return; + + mvm->fw_error_dump = dump_file; + + dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); + dump_file->file_len = cpu_to_le32(file_len); + dump_data = (void *)dump_file->data; + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); + dump_data->len = cpu_to_le32(sizeof(*dump_info)); + dump_info = (void *) dump_data->data; + dump_info->device_family = + mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : + cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, + sizeof(dump_info->fw_human_readable)); + strncpy(dump_info->dev_human_readable, mvm->cfg->name, + sizeof(dump_info->dev_human_readable)); + strncpy(dump_info->bus_human_readable, mvm->dev->bus->name, + sizeof(dump_info->bus_human_readable)); + + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + dump_data->len = cpu_to_le32(rxf_len); + + if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) { + u32 *rxf = (void *)dump_data->data; + int i; + + for (i = 0; i < (rxf_len / sizeof(u32)); i++) { + iwl_trans_write_prph(mvm->trans, + RXF_LD_FENCE_OFFSET_ADDR, + i * sizeof(u32)); + rxf[i] = iwl_trans_read_prph(mvm->trans, + RXF_FIFO_RD_FENCE_ADDR); + } + iwl_trans_release_nic_access(mvm->trans, &flags); + } + + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); + dump_data->len = cpu_to_le32(sram_len); + iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, + sram_len); + + if (trans_len) { + void *buf = iwl_fw_error_next_data(dump_data); + u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf, + trans_len); + dump_data = (void *)((u8 *)buf + real_trans_len); + dump_file->file_len = + cpu_to_le32(file_len - trans_len + real_trans_len); + } +} +#endif + static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { #ifdef CONFIG_IWLWIFI_DEBUGFS |