1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2bfcc09ddSBjoern A. Zeeb /* 3bfcc09ddSBjoern A. Zeeb * Copyright (C) 2005-2014, 2018-2021 Intel Corporation 4bfcc09ddSBjoern A. Zeeb * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 5bfcc09ddSBjoern A. Zeeb * Copyright (C) 2015-2017 Intel Deutschland GmbH 6bfcc09ddSBjoern A. Zeeb */ 7bfcc09ddSBjoern A. Zeeb #include <linux/devcoredump.h> 8bfcc09ddSBjoern A. Zeeb #if defined(__FreeBSD__) 9bfcc09ddSBjoern A. Zeeb #include <linux/delay.h> 10bfcc09ddSBjoern A. Zeeb #endif 11bfcc09ddSBjoern A. Zeeb #include "iwl-drv.h" 12bfcc09ddSBjoern A. Zeeb #include "runtime.h" 13bfcc09ddSBjoern A. Zeeb #include "dbg.h" 14bfcc09ddSBjoern A. Zeeb #include "debugfs.h" 15bfcc09ddSBjoern A. Zeeb #include "iwl-io.h" 16bfcc09ddSBjoern A. Zeeb #include "iwl-prph.h" 17bfcc09ddSBjoern A. Zeeb #include "iwl-csr.h" 18bfcc09ddSBjoern A. Zeeb 19bfcc09ddSBjoern A. Zeeb /** 20bfcc09ddSBjoern A. Zeeb * struct iwl_fw_dump_ptrs - set of pointers needed for the fw-error-dump 21bfcc09ddSBjoern A. Zeeb * 22bfcc09ddSBjoern A. Zeeb * @fwrt_ptr: pointer to the buffer coming from fwrt 23bfcc09ddSBjoern A. Zeeb * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the 24bfcc09ddSBjoern A. Zeeb * transport's data. 25bfcc09ddSBjoern A. Zeeb * @trans_len: length of the valid data in trans_ptr 26bfcc09ddSBjoern A. Zeeb * @fwrt_len: length of the valid data in fwrt_ptr 27bfcc09ddSBjoern A. Zeeb */ 28bfcc09ddSBjoern A. Zeeb struct iwl_fw_dump_ptrs { 29bfcc09ddSBjoern A. Zeeb struct iwl_trans_dump_data *trans_ptr; 30bfcc09ddSBjoern A. Zeeb void *fwrt_ptr; 31bfcc09ddSBjoern A. Zeeb u32 fwrt_len; 32bfcc09ddSBjoern A. Zeeb }; 33bfcc09ddSBjoern A. Zeeb 34bfcc09ddSBjoern A. Zeeb #define RADIO_REG_MAX_READ 0x2ad 35bfcc09ddSBjoern A. Zeeb static void iwl_read_radio_regs(struct iwl_fw_runtime *fwrt, 36bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **dump_data) 37bfcc09ddSBjoern A. Zeeb { 38bfcc09ddSBjoern A. Zeeb u8 *pos = (void *)(*dump_data)->data; 39bfcc09ddSBjoern A. Zeeb int i; 40bfcc09ddSBjoern A. Zeeb 41bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(fwrt, "WRT radio registers dump\n"); 42bfcc09ddSBjoern A. Zeeb 43bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) 44bfcc09ddSBjoern A. Zeeb return; 45bfcc09ddSBjoern A. Zeeb 46bfcc09ddSBjoern A. Zeeb (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); 47bfcc09ddSBjoern A. Zeeb (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); 48bfcc09ddSBjoern A. Zeeb 49bfcc09ddSBjoern A. Zeeb for (i = 0; i < RADIO_REG_MAX_READ; i++) { 50bfcc09ddSBjoern A. Zeeb u32 rd_cmd = RADIO_RSP_RD_CMD; 51bfcc09ddSBjoern A. Zeeb 52bfcc09ddSBjoern A. Zeeb rd_cmd |= i << RADIO_RSP_ADDR_POS; 53bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, RSP_RADIO_CMD, rd_cmd); 54bfcc09ddSBjoern A. Zeeb *pos = (u8)iwl_read_prph_no_grab(fwrt->trans, RSP_RADIO_RDDAT); 55bfcc09ddSBjoern A. Zeeb 56bfcc09ddSBjoern A. Zeeb pos++; 57bfcc09ddSBjoern A. Zeeb } 58bfcc09ddSBjoern A. Zeeb 59bfcc09ddSBjoern A. Zeeb *dump_data = iwl_fw_error_next_data(*dump_data); 60bfcc09ddSBjoern A. Zeeb 61bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 62bfcc09ddSBjoern A. Zeeb } 63bfcc09ddSBjoern A. Zeeb 64bfcc09ddSBjoern A. Zeeb static void iwl_fwrt_dump_rxf(struct iwl_fw_runtime *fwrt, 65bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **dump_data, 66bfcc09ddSBjoern A. Zeeb int size, u32 offset, int fifo_num) 67bfcc09ddSBjoern A. Zeeb { 68bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_fifo *fifo_hdr; 69bfcc09ddSBjoern A. Zeeb u32 *fifo_data; 70bfcc09ddSBjoern A. Zeeb u32 fifo_len; 71bfcc09ddSBjoern A. Zeeb int i; 72bfcc09ddSBjoern A. Zeeb 73bfcc09ddSBjoern A. Zeeb fifo_hdr = (void *)(*dump_data)->data; 74bfcc09ddSBjoern A. Zeeb fifo_data = (void *)fifo_hdr->data; 75bfcc09ddSBjoern A. Zeeb fifo_len = size; 76bfcc09ddSBjoern A. Zeeb 77bfcc09ddSBjoern A. Zeeb /* No need to try to read the data if the length is 0 */ 78bfcc09ddSBjoern A. Zeeb if (fifo_len == 0) 79bfcc09ddSBjoern A. Zeeb return; 80bfcc09ddSBjoern A. Zeeb 81bfcc09ddSBjoern A. Zeeb /* Add a TLV for the RXF */ 82bfcc09ddSBjoern A. Zeeb (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); 83bfcc09ddSBjoern A. Zeeb (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); 84bfcc09ddSBjoern A. Zeeb 85bfcc09ddSBjoern A. Zeeb fifo_hdr->fifo_num = cpu_to_le32(fifo_num); 86bfcc09ddSBjoern A. Zeeb fifo_hdr->available_bytes = 87bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 88bfcc09ddSBjoern A. Zeeb RXF_RD_D_SPACE + offset)); 89bfcc09ddSBjoern A. Zeeb fifo_hdr->wr_ptr = 90bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 91bfcc09ddSBjoern A. Zeeb RXF_RD_WR_PTR + offset)); 92bfcc09ddSBjoern A. Zeeb fifo_hdr->rd_ptr = 93bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 94bfcc09ddSBjoern A. Zeeb RXF_RD_RD_PTR + offset)); 95bfcc09ddSBjoern A. Zeeb fifo_hdr->fence_ptr = 96bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 97bfcc09ddSBjoern A. Zeeb RXF_RD_FENCE_PTR + offset)); 98bfcc09ddSBjoern A. Zeeb fifo_hdr->fence_mode = 99bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 100bfcc09ddSBjoern A. Zeeb RXF_SET_FENCE_MODE + offset)); 101bfcc09ddSBjoern A. Zeeb 102bfcc09ddSBjoern A. Zeeb /* Lock fence */ 103bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, RXF_SET_FENCE_MODE + offset, 0x1); 104bfcc09ddSBjoern A. Zeeb /* Set fence pointer to the same place like WR pointer */ 105bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, RXF_LD_WR2FENCE + offset, 0x1); 106bfcc09ddSBjoern A. Zeeb /* Set fence offset */ 107bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, 108bfcc09ddSBjoern A. Zeeb RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0); 109bfcc09ddSBjoern A. Zeeb 110bfcc09ddSBjoern A. Zeeb /* Read FIFO */ 111bfcc09ddSBjoern A. Zeeb fifo_len /= sizeof(u32); /* Size in DWORDS */ 112bfcc09ddSBjoern A. Zeeb for (i = 0; i < fifo_len; i++) 113bfcc09ddSBjoern A. Zeeb fifo_data[i] = iwl_trans_read_prph(fwrt->trans, 114bfcc09ddSBjoern A. Zeeb RXF_FIFO_RD_FENCE_INC + 115bfcc09ddSBjoern A. Zeeb offset); 116bfcc09ddSBjoern A. Zeeb *dump_data = iwl_fw_error_next_data(*dump_data); 117bfcc09ddSBjoern A. Zeeb } 118bfcc09ddSBjoern A. Zeeb 119bfcc09ddSBjoern A. Zeeb static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt, 120bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **dump_data, 121bfcc09ddSBjoern A. Zeeb int size, u32 offset, int fifo_num) 122bfcc09ddSBjoern A. Zeeb { 123bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_fifo *fifo_hdr; 124bfcc09ddSBjoern A. Zeeb u32 *fifo_data; 125bfcc09ddSBjoern A. Zeeb u32 fifo_len; 126bfcc09ddSBjoern A. Zeeb int i; 127bfcc09ddSBjoern A. Zeeb 128bfcc09ddSBjoern A. Zeeb fifo_hdr = (void *)(*dump_data)->data; 129bfcc09ddSBjoern A. Zeeb fifo_data = (void *)fifo_hdr->data; 130bfcc09ddSBjoern A. Zeeb fifo_len = size; 131bfcc09ddSBjoern A. Zeeb 132bfcc09ddSBjoern A. Zeeb /* No need to try to read the data if the length is 0 */ 133bfcc09ddSBjoern A. Zeeb if (fifo_len == 0) 134bfcc09ddSBjoern A. Zeeb return; 135bfcc09ddSBjoern A. Zeeb 136bfcc09ddSBjoern A. Zeeb /* Add a TLV for the FIFO */ 137bfcc09ddSBjoern A. Zeeb (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); 138bfcc09ddSBjoern A. Zeeb (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); 139bfcc09ddSBjoern A. Zeeb 140bfcc09ddSBjoern A. Zeeb fifo_hdr->fifo_num = cpu_to_le32(fifo_num); 141bfcc09ddSBjoern A. Zeeb fifo_hdr->available_bytes = 142bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 143bfcc09ddSBjoern A. Zeeb TXF_FIFO_ITEM_CNT + offset)); 144bfcc09ddSBjoern A. Zeeb fifo_hdr->wr_ptr = 145bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 146bfcc09ddSBjoern A. Zeeb TXF_WR_PTR + offset)); 147bfcc09ddSBjoern A. Zeeb fifo_hdr->rd_ptr = 148bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 149bfcc09ddSBjoern A. Zeeb TXF_RD_PTR + offset)); 150bfcc09ddSBjoern A. Zeeb fifo_hdr->fence_ptr = 151bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 152bfcc09ddSBjoern A. Zeeb TXF_FENCE_PTR + offset)); 153bfcc09ddSBjoern A. Zeeb fifo_hdr->fence_mode = 154bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 155bfcc09ddSBjoern A. Zeeb TXF_LOCK_FENCE + offset)); 156bfcc09ddSBjoern A. Zeeb 157bfcc09ddSBjoern A. Zeeb /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ 158bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, TXF_READ_MODIFY_ADDR + offset, 159bfcc09ddSBjoern A. Zeeb TXF_WR_PTR + offset); 160bfcc09ddSBjoern A. Zeeb 161bfcc09ddSBjoern A. Zeeb /* Dummy-read to advance the read pointer to the head */ 162bfcc09ddSBjoern A. Zeeb iwl_trans_read_prph(fwrt->trans, TXF_READ_MODIFY_DATA + offset); 163bfcc09ddSBjoern A. Zeeb 164bfcc09ddSBjoern A. Zeeb /* Read FIFO */ 165bfcc09ddSBjoern A. Zeeb for (i = 0; i < fifo_len / sizeof(u32); i++) 166bfcc09ddSBjoern A. Zeeb fifo_data[i] = iwl_trans_read_prph(fwrt->trans, 167bfcc09ddSBjoern A. Zeeb TXF_READ_MODIFY_DATA + 168bfcc09ddSBjoern A. Zeeb offset); 169bfcc09ddSBjoern A. Zeeb 170bfcc09ddSBjoern A. Zeeb if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) 171bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx, 172bfcc09ddSBjoern A. Zeeb fifo_data, fifo_len); 173bfcc09ddSBjoern A. Zeeb 174bfcc09ddSBjoern A. Zeeb *dump_data = iwl_fw_error_next_data(*dump_data); 175bfcc09ddSBjoern A. Zeeb } 176bfcc09ddSBjoern A. Zeeb 177bfcc09ddSBjoern A. Zeeb static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt, 178bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **dump_data) 179bfcc09ddSBjoern A. Zeeb { 180bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; 181bfcc09ddSBjoern A. Zeeb 182bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n"); 183bfcc09ddSBjoern A. Zeeb 184bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) 185bfcc09ddSBjoern A. Zeeb return; 186bfcc09ddSBjoern A. Zeeb 187bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) { 188bfcc09ddSBjoern A. Zeeb /* Pull RXF1 */ 189bfcc09ddSBjoern A. Zeeb iwl_fwrt_dump_rxf(fwrt, dump_data, 190bfcc09ddSBjoern A. Zeeb cfg->lmac[0].rxfifo1_size, 0, 0); 191bfcc09ddSBjoern A. Zeeb /* Pull RXF2 */ 192bfcc09ddSBjoern A. Zeeb iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size, 193bfcc09ddSBjoern A. Zeeb RXF_DIFF_FROM_PREV + 194bfcc09ddSBjoern A. Zeeb fwrt->trans->trans_cfg->umac_prph_offset, 1); 195bfcc09ddSBjoern A. Zeeb /* Pull LMAC2 RXF1 */ 196bfcc09ddSBjoern A. Zeeb if (fwrt->smem_cfg.num_lmacs > 1) 197bfcc09ddSBjoern A. Zeeb iwl_fwrt_dump_rxf(fwrt, dump_data, 198bfcc09ddSBjoern A. Zeeb cfg->lmac[1].rxfifo1_size, 199bfcc09ddSBjoern A. Zeeb LMAC2_PRPH_OFFSET, 2); 200bfcc09ddSBjoern A. Zeeb } 201bfcc09ddSBjoern A. Zeeb 202bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 203bfcc09ddSBjoern A. Zeeb } 204bfcc09ddSBjoern A. Zeeb 205bfcc09ddSBjoern A. Zeeb static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt, 206bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **dump_data) 207bfcc09ddSBjoern A. Zeeb { 208bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_fifo *fifo_hdr; 209bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; 210bfcc09ddSBjoern A. Zeeb u32 *fifo_data; 211bfcc09ddSBjoern A. Zeeb u32 fifo_len; 212bfcc09ddSBjoern A. Zeeb int i, j; 213bfcc09ddSBjoern A. Zeeb 214bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n"); 215bfcc09ddSBjoern A. Zeeb 216bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) 217bfcc09ddSBjoern A. Zeeb return; 218bfcc09ddSBjoern A. Zeeb 219bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) { 220bfcc09ddSBjoern A. Zeeb /* Pull TXF data from LMAC1 */ 221bfcc09ddSBjoern A. Zeeb for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) { 222bfcc09ddSBjoern A. Zeeb /* Mark the number of TXF we're pulling now */ 223bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, TXF_LARC_NUM, i); 224bfcc09ddSBjoern A. Zeeb iwl_fwrt_dump_txf(fwrt, dump_data, 225bfcc09ddSBjoern A. Zeeb cfg->lmac[0].txfifo_size[i], 0, i); 226bfcc09ddSBjoern A. Zeeb } 227bfcc09ddSBjoern A. Zeeb 228bfcc09ddSBjoern A. Zeeb /* Pull TXF data from LMAC2 */ 229bfcc09ddSBjoern A. Zeeb if (fwrt->smem_cfg.num_lmacs > 1) { 230bfcc09ddSBjoern A. Zeeb for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; 231bfcc09ddSBjoern A. Zeeb i++) { 232bfcc09ddSBjoern A. Zeeb /* Mark the number of TXF we're pulling now */ 233bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, 234bfcc09ddSBjoern A. Zeeb TXF_LARC_NUM + 235bfcc09ddSBjoern A. Zeeb LMAC2_PRPH_OFFSET, i); 236bfcc09ddSBjoern A. Zeeb iwl_fwrt_dump_txf(fwrt, dump_data, 237bfcc09ddSBjoern A. Zeeb cfg->lmac[1].txfifo_size[i], 238bfcc09ddSBjoern A. Zeeb LMAC2_PRPH_OFFSET, 239bfcc09ddSBjoern A. Zeeb i + cfg->num_txfifo_entries); 240bfcc09ddSBjoern A. Zeeb } 241bfcc09ddSBjoern A. Zeeb } 242bfcc09ddSBjoern A. Zeeb } 243bfcc09ddSBjoern A. Zeeb 244bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) && 245bfcc09ddSBjoern A. Zeeb fw_has_capa(&fwrt->fw->ucode_capa, 246bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) { 247bfcc09ddSBjoern A. Zeeb /* Pull UMAC internal TXF data from all TXFs */ 248bfcc09ddSBjoern A. Zeeb for (i = 0; 249bfcc09ddSBjoern A. Zeeb i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size); 250bfcc09ddSBjoern A. Zeeb i++) { 251bfcc09ddSBjoern A. Zeeb fifo_hdr = (void *)(*dump_data)->data; 252bfcc09ddSBjoern A. Zeeb fifo_data = (void *)fifo_hdr->data; 253bfcc09ddSBjoern A. Zeeb fifo_len = fwrt->smem_cfg.internal_txfifo_size[i]; 254bfcc09ddSBjoern A. Zeeb 255bfcc09ddSBjoern A. Zeeb /* No need to try to read the data if the length is 0 */ 256bfcc09ddSBjoern A. Zeeb if (fifo_len == 0) 257bfcc09ddSBjoern A. Zeeb continue; 258bfcc09ddSBjoern A. Zeeb 259bfcc09ddSBjoern A. Zeeb /* Add a TLV for the internal FIFOs */ 260bfcc09ddSBjoern A. Zeeb (*dump_data)->type = 261bfcc09ddSBjoern A. Zeeb cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF); 262bfcc09ddSBjoern A. Zeeb (*dump_data)->len = 263bfcc09ddSBjoern A. Zeeb cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); 264bfcc09ddSBjoern A. Zeeb 265bfcc09ddSBjoern A. Zeeb fifo_hdr->fifo_num = cpu_to_le32(i); 266bfcc09ddSBjoern A. Zeeb 267bfcc09ddSBjoern A. Zeeb /* Mark the number of TXF we're pulling now */ 268bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, TXF_CPU2_NUM, i + 269bfcc09ddSBjoern A. Zeeb fwrt->smem_cfg.num_txfifo_entries); 270bfcc09ddSBjoern A. Zeeb 271bfcc09ddSBjoern A. Zeeb fifo_hdr->available_bytes = 272bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 273bfcc09ddSBjoern A. Zeeb TXF_CPU2_FIFO_ITEM_CNT)); 274bfcc09ddSBjoern A. Zeeb fifo_hdr->wr_ptr = 275bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 276bfcc09ddSBjoern A. Zeeb TXF_CPU2_WR_PTR)); 277bfcc09ddSBjoern A. Zeeb fifo_hdr->rd_ptr = 278bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 279bfcc09ddSBjoern A. Zeeb TXF_CPU2_RD_PTR)); 280bfcc09ddSBjoern A. Zeeb fifo_hdr->fence_ptr = 281bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 282bfcc09ddSBjoern A. Zeeb TXF_CPU2_FENCE_PTR)); 283bfcc09ddSBjoern A. Zeeb fifo_hdr->fence_mode = 284bfcc09ddSBjoern A. Zeeb cpu_to_le32(iwl_trans_read_prph(fwrt->trans, 285bfcc09ddSBjoern A. Zeeb TXF_CPU2_LOCK_FENCE)); 286bfcc09ddSBjoern A. Zeeb 287bfcc09ddSBjoern A. Zeeb /* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */ 288bfcc09ddSBjoern A. Zeeb iwl_trans_write_prph(fwrt->trans, 289bfcc09ddSBjoern A. Zeeb TXF_CPU2_READ_MODIFY_ADDR, 290bfcc09ddSBjoern A. Zeeb TXF_CPU2_WR_PTR); 291bfcc09ddSBjoern A. Zeeb 292bfcc09ddSBjoern A. Zeeb /* Dummy-read to advance the read pointer to head */ 293bfcc09ddSBjoern A. Zeeb iwl_trans_read_prph(fwrt->trans, 294bfcc09ddSBjoern A. Zeeb TXF_CPU2_READ_MODIFY_DATA); 295bfcc09ddSBjoern A. Zeeb 296bfcc09ddSBjoern A. Zeeb /* Read FIFO */ 297bfcc09ddSBjoern A. Zeeb fifo_len /= sizeof(u32); /* Size in DWORDS */ 298bfcc09ddSBjoern A. Zeeb for (j = 0; j < fifo_len; j++) 299bfcc09ddSBjoern A. Zeeb fifo_data[j] = 300bfcc09ddSBjoern A. Zeeb iwl_trans_read_prph(fwrt->trans, 301bfcc09ddSBjoern A. Zeeb TXF_CPU2_READ_MODIFY_DATA); 302bfcc09ddSBjoern A. Zeeb *dump_data = iwl_fw_error_next_data(*dump_data); 303bfcc09ddSBjoern A. Zeeb } 304bfcc09ddSBjoern A. Zeeb } 305bfcc09ddSBjoern A. Zeeb 306bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 307bfcc09ddSBjoern A. Zeeb } 308bfcc09ddSBjoern A. Zeeb 309bfcc09ddSBjoern A. Zeeb #define IWL8260_ICCM_OFFSET 0x44000 /* Only for B-step */ 310bfcc09ddSBjoern A. Zeeb #define IWL8260_ICCM_LEN 0xC000 /* Only for B-step */ 311bfcc09ddSBjoern A. Zeeb 312bfcc09ddSBjoern A. Zeeb struct iwl_prph_range { 313bfcc09ddSBjoern A. Zeeb u32 start, end; 314bfcc09ddSBjoern A. Zeeb }; 315bfcc09ddSBjoern A. Zeeb 316bfcc09ddSBjoern A. Zeeb static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = { 317bfcc09ddSBjoern A. Zeeb { .start = 0x00a00000, .end = 0x00a00000 }, 318bfcc09ddSBjoern A. Zeeb { .start = 0x00a0000c, .end = 0x00a00024 }, 319bfcc09ddSBjoern A. Zeeb { .start = 0x00a0002c, .end = 0x00a0003c }, 320bfcc09ddSBjoern A. Zeeb { .start = 0x00a00410, .end = 0x00a00418 }, 321bfcc09ddSBjoern A. Zeeb { .start = 0x00a00420, .end = 0x00a00420 }, 322bfcc09ddSBjoern A. Zeeb { .start = 0x00a00428, .end = 0x00a00428 }, 323bfcc09ddSBjoern A. Zeeb { .start = 0x00a00430, .end = 0x00a0043c }, 324bfcc09ddSBjoern A. Zeeb { .start = 0x00a00444, .end = 0x00a00444 }, 325bfcc09ddSBjoern A. Zeeb { .start = 0x00a004c0, .end = 0x00a004cc }, 326bfcc09ddSBjoern A. Zeeb { .start = 0x00a004d8, .end = 0x00a004d8 }, 327bfcc09ddSBjoern A. Zeeb { .start = 0x00a004e0, .end = 0x00a004f0 }, 328bfcc09ddSBjoern A. Zeeb { .start = 0x00a00840, .end = 0x00a00840 }, 329bfcc09ddSBjoern A. Zeeb { .start = 0x00a00850, .end = 0x00a00858 }, 330bfcc09ddSBjoern A. Zeeb { .start = 0x00a01004, .end = 0x00a01008 }, 331bfcc09ddSBjoern A. Zeeb { .start = 0x00a01010, .end = 0x00a01010 }, 332bfcc09ddSBjoern A. Zeeb { .start = 0x00a01018, .end = 0x00a01018 }, 333bfcc09ddSBjoern A. Zeeb { .start = 0x00a01024, .end = 0x00a01024 }, 334bfcc09ddSBjoern A. Zeeb { .start = 0x00a0102c, .end = 0x00a01034 }, 335bfcc09ddSBjoern A. Zeeb { .start = 0x00a0103c, .end = 0x00a01040 }, 336bfcc09ddSBjoern A. Zeeb { .start = 0x00a01048, .end = 0x00a01094 }, 337bfcc09ddSBjoern A. Zeeb { .start = 0x00a01c00, .end = 0x00a01c20 }, 338bfcc09ddSBjoern A. Zeeb { .start = 0x00a01c58, .end = 0x00a01c58 }, 339bfcc09ddSBjoern A. Zeeb { .start = 0x00a01c7c, .end = 0x00a01c7c }, 340bfcc09ddSBjoern A. Zeeb { .start = 0x00a01c28, .end = 0x00a01c54 }, 341bfcc09ddSBjoern A. Zeeb { .start = 0x00a01c5c, .end = 0x00a01c5c }, 342bfcc09ddSBjoern A. Zeeb { .start = 0x00a01c60, .end = 0x00a01cdc }, 343bfcc09ddSBjoern A. Zeeb { .start = 0x00a01ce0, .end = 0x00a01d0c }, 344bfcc09ddSBjoern A. Zeeb { .start = 0x00a01d18, .end = 0x00a01d20 }, 345bfcc09ddSBjoern A. Zeeb { .start = 0x00a01d2c, .end = 0x00a01d30 }, 346bfcc09ddSBjoern A. Zeeb { .start = 0x00a01d40, .end = 0x00a01d5c }, 347bfcc09ddSBjoern A. Zeeb { .start = 0x00a01d80, .end = 0x00a01d80 }, 348bfcc09ddSBjoern A. Zeeb { .start = 0x00a01d98, .end = 0x00a01d9c }, 349bfcc09ddSBjoern A. Zeeb { .start = 0x00a01da8, .end = 0x00a01da8 }, 350bfcc09ddSBjoern A. Zeeb { .start = 0x00a01db8, .end = 0x00a01df4 }, 351bfcc09ddSBjoern A. Zeeb { .start = 0x00a01dc0, .end = 0x00a01dfc }, 352bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e00, .end = 0x00a01e2c }, 353bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e40, .end = 0x00a01e60 }, 354bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e68, .end = 0x00a01e6c }, 355bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e74, .end = 0x00a01e74 }, 356bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e84, .end = 0x00a01e90 }, 357bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e9c, .end = 0x00a01ec4 }, 358bfcc09ddSBjoern A. Zeeb { .start = 0x00a01ed0, .end = 0x00a01ee0 }, 359bfcc09ddSBjoern A. Zeeb { .start = 0x00a01f00, .end = 0x00a01f1c }, 360bfcc09ddSBjoern A. Zeeb { .start = 0x00a01f44, .end = 0x00a01ffc }, 361bfcc09ddSBjoern A. Zeeb { .start = 0x00a02000, .end = 0x00a02048 }, 362bfcc09ddSBjoern A. Zeeb { .start = 0x00a02068, .end = 0x00a020f0 }, 363bfcc09ddSBjoern A. Zeeb { .start = 0x00a02100, .end = 0x00a02118 }, 364bfcc09ddSBjoern A. Zeeb { .start = 0x00a02140, .end = 0x00a0214c }, 365bfcc09ddSBjoern A. Zeeb { .start = 0x00a02168, .end = 0x00a0218c }, 366bfcc09ddSBjoern A. Zeeb { .start = 0x00a021c0, .end = 0x00a021c0 }, 367bfcc09ddSBjoern A. Zeeb { .start = 0x00a02400, .end = 0x00a02410 }, 368bfcc09ddSBjoern A. Zeeb { .start = 0x00a02418, .end = 0x00a02420 }, 369bfcc09ddSBjoern A. Zeeb { .start = 0x00a02428, .end = 0x00a0242c }, 370bfcc09ddSBjoern A. Zeeb { .start = 0x00a02434, .end = 0x00a02434 }, 371bfcc09ddSBjoern A. Zeeb { .start = 0x00a02440, .end = 0x00a02460 }, 372bfcc09ddSBjoern A. Zeeb { .start = 0x00a02468, .end = 0x00a024b0 }, 373bfcc09ddSBjoern A. Zeeb { .start = 0x00a024c8, .end = 0x00a024cc }, 374bfcc09ddSBjoern A. Zeeb { .start = 0x00a02500, .end = 0x00a02504 }, 375bfcc09ddSBjoern A. Zeeb { .start = 0x00a0250c, .end = 0x00a02510 }, 376bfcc09ddSBjoern A. Zeeb { .start = 0x00a02540, .end = 0x00a02554 }, 377bfcc09ddSBjoern A. Zeeb { .start = 0x00a02580, .end = 0x00a025f4 }, 378bfcc09ddSBjoern A. Zeeb { .start = 0x00a02600, .end = 0x00a0260c }, 379bfcc09ddSBjoern A. Zeeb { .start = 0x00a02648, .end = 0x00a02650 }, 380bfcc09ddSBjoern A. Zeeb { .start = 0x00a02680, .end = 0x00a02680 }, 381bfcc09ddSBjoern A. Zeeb { .start = 0x00a026c0, .end = 0x00a026d0 }, 382bfcc09ddSBjoern A. Zeeb { .start = 0x00a02700, .end = 0x00a0270c }, 383bfcc09ddSBjoern A. Zeeb { .start = 0x00a02804, .end = 0x00a02804 }, 384bfcc09ddSBjoern A. Zeeb { .start = 0x00a02818, .end = 0x00a0281c }, 385bfcc09ddSBjoern A. Zeeb { .start = 0x00a02c00, .end = 0x00a02db4 }, 386bfcc09ddSBjoern A. Zeeb { .start = 0x00a02df4, .end = 0x00a02fb0 }, 387bfcc09ddSBjoern A. Zeeb { .start = 0x00a03000, .end = 0x00a03014 }, 388bfcc09ddSBjoern A. Zeeb { .start = 0x00a0301c, .end = 0x00a0302c }, 389bfcc09ddSBjoern A. Zeeb { .start = 0x00a03034, .end = 0x00a03038 }, 390bfcc09ddSBjoern A. Zeeb { .start = 0x00a03040, .end = 0x00a03048 }, 391bfcc09ddSBjoern A. Zeeb { .start = 0x00a03060, .end = 0x00a03068 }, 392bfcc09ddSBjoern A. Zeeb { .start = 0x00a03070, .end = 0x00a03074 }, 393bfcc09ddSBjoern A. Zeeb { .start = 0x00a0307c, .end = 0x00a0307c }, 394bfcc09ddSBjoern A. Zeeb { .start = 0x00a03080, .end = 0x00a03084 }, 395bfcc09ddSBjoern A. Zeeb { .start = 0x00a0308c, .end = 0x00a03090 }, 396bfcc09ddSBjoern A. Zeeb { .start = 0x00a03098, .end = 0x00a03098 }, 397bfcc09ddSBjoern A. Zeeb { .start = 0x00a030a0, .end = 0x00a030a0 }, 398bfcc09ddSBjoern A. Zeeb { .start = 0x00a030a8, .end = 0x00a030b4 }, 399bfcc09ddSBjoern A. Zeeb { .start = 0x00a030bc, .end = 0x00a030bc }, 400bfcc09ddSBjoern A. Zeeb { .start = 0x00a030c0, .end = 0x00a0312c }, 401bfcc09ddSBjoern A. Zeeb { .start = 0x00a03c00, .end = 0x00a03c5c }, 402bfcc09ddSBjoern A. Zeeb { .start = 0x00a04400, .end = 0x00a04454 }, 403bfcc09ddSBjoern A. Zeeb { .start = 0x00a04460, .end = 0x00a04474 }, 404bfcc09ddSBjoern A. Zeeb { .start = 0x00a044c0, .end = 0x00a044ec }, 405bfcc09ddSBjoern A. Zeeb { .start = 0x00a04500, .end = 0x00a04504 }, 406bfcc09ddSBjoern A. Zeeb { .start = 0x00a04510, .end = 0x00a04538 }, 407bfcc09ddSBjoern A. Zeeb { .start = 0x00a04540, .end = 0x00a04548 }, 408bfcc09ddSBjoern A. Zeeb { .start = 0x00a04560, .end = 0x00a0457c }, 409bfcc09ddSBjoern A. Zeeb { .start = 0x00a04590, .end = 0x00a04598 }, 410bfcc09ddSBjoern A. Zeeb { .start = 0x00a045c0, .end = 0x00a045f4 }, 411bfcc09ddSBjoern A. Zeeb }; 412bfcc09ddSBjoern A. Zeeb 413bfcc09ddSBjoern A. Zeeb static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = { 414bfcc09ddSBjoern A. Zeeb { .start = 0x00a05c00, .end = 0x00a05c18 }, 415bfcc09ddSBjoern A. Zeeb { .start = 0x00a05400, .end = 0x00a056e8 }, 416bfcc09ddSBjoern A. Zeeb { .start = 0x00a08000, .end = 0x00a098bc }, 417bfcc09ddSBjoern A. Zeeb { .start = 0x00a02400, .end = 0x00a02758 }, 418bfcc09ddSBjoern A. Zeeb { .start = 0x00a04764, .end = 0x00a0476c }, 419bfcc09ddSBjoern A. Zeeb { .start = 0x00a04770, .end = 0x00a04774 }, 420bfcc09ddSBjoern A. Zeeb { .start = 0x00a04620, .end = 0x00a04624 }, 421bfcc09ddSBjoern A. Zeeb }; 422bfcc09ddSBjoern A. Zeeb 423bfcc09ddSBjoern A. Zeeb static const struct iwl_prph_range iwl_prph_dump_addr_22000[] = { 424bfcc09ddSBjoern A. Zeeb { .start = 0x00a00000, .end = 0x00a00000 }, 425bfcc09ddSBjoern A. Zeeb { .start = 0x00a0000c, .end = 0x00a00024 }, 426bfcc09ddSBjoern A. Zeeb { .start = 0x00a0002c, .end = 0x00a00034 }, 427bfcc09ddSBjoern A. Zeeb { .start = 0x00a0003c, .end = 0x00a0003c }, 428bfcc09ddSBjoern A. Zeeb { .start = 0x00a00410, .end = 0x00a00418 }, 429bfcc09ddSBjoern A. Zeeb { .start = 0x00a00420, .end = 0x00a00420 }, 430bfcc09ddSBjoern A. Zeeb { .start = 0x00a00428, .end = 0x00a00428 }, 431bfcc09ddSBjoern A. Zeeb { .start = 0x00a00430, .end = 0x00a0043c }, 432bfcc09ddSBjoern A. Zeeb { .start = 0x00a00444, .end = 0x00a00444 }, 433bfcc09ddSBjoern A. Zeeb { .start = 0x00a00840, .end = 0x00a00840 }, 434bfcc09ddSBjoern A. Zeeb { .start = 0x00a00850, .end = 0x00a00858 }, 435bfcc09ddSBjoern A. Zeeb { .start = 0x00a01004, .end = 0x00a01008 }, 436bfcc09ddSBjoern A. Zeeb { .start = 0x00a01010, .end = 0x00a01010 }, 437bfcc09ddSBjoern A. Zeeb { .start = 0x00a01018, .end = 0x00a01018 }, 438bfcc09ddSBjoern A. Zeeb { .start = 0x00a01024, .end = 0x00a01024 }, 439bfcc09ddSBjoern A. Zeeb { .start = 0x00a0102c, .end = 0x00a01034 }, 440bfcc09ddSBjoern A. Zeeb { .start = 0x00a0103c, .end = 0x00a01040 }, 441bfcc09ddSBjoern A. Zeeb { .start = 0x00a01048, .end = 0x00a01050 }, 442bfcc09ddSBjoern A. Zeeb { .start = 0x00a01058, .end = 0x00a01058 }, 443bfcc09ddSBjoern A. Zeeb { .start = 0x00a01060, .end = 0x00a01070 }, 444bfcc09ddSBjoern A. Zeeb { .start = 0x00a0108c, .end = 0x00a0108c }, 445bfcc09ddSBjoern A. Zeeb { .start = 0x00a01c20, .end = 0x00a01c28 }, 446bfcc09ddSBjoern A. Zeeb { .start = 0x00a01d10, .end = 0x00a01d10 }, 447bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e28, .end = 0x00a01e2c }, 448bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e60, .end = 0x00a01e60 }, 449bfcc09ddSBjoern A. Zeeb { .start = 0x00a01e80, .end = 0x00a01e80 }, 450bfcc09ddSBjoern A. Zeeb { .start = 0x00a01ea0, .end = 0x00a01ea0 }, 451bfcc09ddSBjoern A. Zeeb { .start = 0x00a02000, .end = 0x00a0201c }, 452bfcc09ddSBjoern A. Zeeb { .start = 0x00a02024, .end = 0x00a02024 }, 453bfcc09ddSBjoern A. Zeeb { .start = 0x00a02040, .end = 0x00a02048 }, 454bfcc09ddSBjoern A. Zeeb { .start = 0x00a020c0, .end = 0x00a020e0 }, 455bfcc09ddSBjoern A. Zeeb { .start = 0x00a02400, .end = 0x00a02404 }, 456bfcc09ddSBjoern A. Zeeb { .start = 0x00a0240c, .end = 0x00a02414 }, 457bfcc09ddSBjoern A. Zeeb { .start = 0x00a0241c, .end = 0x00a0243c }, 458bfcc09ddSBjoern A. Zeeb { .start = 0x00a02448, .end = 0x00a024bc }, 459bfcc09ddSBjoern A. Zeeb { .start = 0x00a024c4, .end = 0x00a024cc }, 460bfcc09ddSBjoern A. Zeeb { .start = 0x00a02508, .end = 0x00a02508 }, 461bfcc09ddSBjoern A. Zeeb { .start = 0x00a02510, .end = 0x00a02514 }, 462bfcc09ddSBjoern A. Zeeb { .start = 0x00a0251c, .end = 0x00a0251c }, 463bfcc09ddSBjoern A. Zeeb { .start = 0x00a0252c, .end = 0x00a0255c }, 464bfcc09ddSBjoern A. Zeeb { .start = 0x00a02564, .end = 0x00a025a0 }, 465bfcc09ddSBjoern A. Zeeb { .start = 0x00a025a8, .end = 0x00a025b4 }, 466bfcc09ddSBjoern A. Zeeb { .start = 0x00a025c0, .end = 0x00a025c0 }, 467bfcc09ddSBjoern A. Zeeb { .start = 0x00a025e8, .end = 0x00a025f4 }, 468bfcc09ddSBjoern A. Zeeb { .start = 0x00a02c08, .end = 0x00a02c18 }, 469bfcc09ddSBjoern A. Zeeb { .start = 0x00a02c2c, .end = 0x00a02c38 }, 470bfcc09ddSBjoern A. Zeeb { .start = 0x00a02c68, .end = 0x00a02c78 }, 471bfcc09ddSBjoern A. Zeeb { .start = 0x00a03000, .end = 0x00a03000 }, 472bfcc09ddSBjoern A. Zeeb { .start = 0x00a03010, .end = 0x00a03014 }, 473bfcc09ddSBjoern A. Zeeb { .start = 0x00a0301c, .end = 0x00a0302c }, 474bfcc09ddSBjoern A. Zeeb { .start = 0x00a03034, .end = 0x00a03038 }, 475bfcc09ddSBjoern A. Zeeb { .start = 0x00a03040, .end = 0x00a03044 }, 476bfcc09ddSBjoern A. Zeeb { .start = 0x00a03060, .end = 0x00a03068 }, 477bfcc09ddSBjoern A. Zeeb { .start = 0x00a03070, .end = 0x00a03070 }, 478bfcc09ddSBjoern A. Zeeb { .start = 0x00a0307c, .end = 0x00a03084 }, 479bfcc09ddSBjoern A. Zeeb { .start = 0x00a0308c, .end = 0x00a03090 }, 480bfcc09ddSBjoern A. Zeeb { .start = 0x00a03098, .end = 0x00a03098 }, 481bfcc09ddSBjoern A. Zeeb { .start = 0x00a030a0, .end = 0x00a030a0 }, 482bfcc09ddSBjoern A. Zeeb { .start = 0x00a030a8, .end = 0x00a030b4 }, 483bfcc09ddSBjoern A. Zeeb { .start = 0x00a030bc, .end = 0x00a030c0 }, 484bfcc09ddSBjoern A. Zeeb { .start = 0x00a030c8, .end = 0x00a030f4 }, 485bfcc09ddSBjoern A. Zeeb { .start = 0x00a03100, .end = 0x00a0312c }, 486bfcc09ddSBjoern A. Zeeb { .start = 0x00a03c00, .end = 0x00a03c5c }, 487bfcc09ddSBjoern A. Zeeb { .start = 0x00a04400, .end = 0x00a04454 }, 488bfcc09ddSBjoern A. Zeeb { .start = 0x00a04460, .end = 0x00a04474 }, 489bfcc09ddSBjoern A. Zeeb { .start = 0x00a044c0, .end = 0x00a044ec }, 490bfcc09ddSBjoern A. Zeeb { .start = 0x00a04500, .end = 0x00a04504 }, 491bfcc09ddSBjoern A. Zeeb { .start = 0x00a04510, .end = 0x00a04538 }, 492bfcc09ddSBjoern A. Zeeb { .start = 0x00a04540, .end = 0x00a04548 }, 493bfcc09ddSBjoern A. Zeeb { .start = 0x00a04560, .end = 0x00a04560 }, 494bfcc09ddSBjoern A. Zeeb { .start = 0x00a04570, .end = 0x00a0457c }, 495bfcc09ddSBjoern A. Zeeb { .start = 0x00a04590, .end = 0x00a04590 }, 496bfcc09ddSBjoern A. Zeeb { .start = 0x00a04598, .end = 0x00a04598 }, 497bfcc09ddSBjoern A. Zeeb { .start = 0x00a045c0, .end = 0x00a045f4 }, 498bfcc09ddSBjoern A. Zeeb { .start = 0x00a05c18, .end = 0x00a05c1c }, 499bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c000, .end = 0x00a0c018 }, 500bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c020, .end = 0x00a0c028 }, 501bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c038, .end = 0x00a0c094 }, 502bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c0c0, .end = 0x00a0c104 }, 503bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c10c, .end = 0x00a0c118 }, 504bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c150, .end = 0x00a0c174 }, 505bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c17c, .end = 0x00a0c188 }, 506bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c190, .end = 0x00a0c198 }, 507bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c1a0, .end = 0x00a0c1a8 }, 508bfcc09ddSBjoern A. Zeeb { .start = 0x00a0c1b0, .end = 0x00a0c1b8 }, 509bfcc09ddSBjoern A. Zeeb }; 510bfcc09ddSBjoern A. Zeeb 511bfcc09ddSBjoern A. Zeeb static const struct iwl_prph_range iwl_prph_dump_addr_ax210[] = { 512bfcc09ddSBjoern A. Zeeb { .start = 0x00d03c00, .end = 0x00d03c64 }, 513bfcc09ddSBjoern A. Zeeb { .start = 0x00d05c18, .end = 0x00d05c1c }, 514bfcc09ddSBjoern A. Zeeb { .start = 0x00d0c000, .end = 0x00d0c174 }, 515bfcc09ddSBjoern A. Zeeb }; 516bfcc09ddSBjoern A. Zeeb 517bfcc09ddSBjoern A. Zeeb static void iwl_read_prph_block(struct iwl_trans *trans, u32 start, 518bfcc09ddSBjoern A. Zeeb u32 len_bytes, __le32 *data) 519bfcc09ddSBjoern A. Zeeb { 520bfcc09ddSBjoern A. Zeeb u32 i; 521bfcc09ddSBjoern A. Zeeb 522bfcc09ddSBjoern A. Zeeb for (i = 0; i < len_bytes; i += 4) 523bfcc09ddSBjoern A. Zeeb *data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i)); 524bfcc09ddSBjoern A. Zeeb } 525bfcc09ddSBjoern A. Zeeb 526bfcc09ddSBjoern A. Zeeb static void iwl_dump_prph(struct iwl_fw_runtime *fwrt, 527bfcc09ddSBjoern A. Zeeb const struct iwl_prph_range *iwl_prph_dump_addr, 528bfcc09ddSBjoern A. Zeeb u32 range_len, void *ptr) 529bfcc09ddSBjoern A. Zeeb { 530bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_prph *prph; 531bfcc09ddSBjoern A. Zeeb struct iwl_trans *trans = fwrt->trans; 532bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **data = 533bfcc09ddSBjoern A. Zeeb (struct iwl_fw_error_dump_data **)ptr; 534bfcc09ddSBjoern A. Zeeb u32 i; 535bfcc09ddSBjoern A. Zeeb 536bfcc09ddSBjoern A. Zeeb if (!data) 537bfcc09ddSBjoern A. Zeeb return; 538bfcc09ddSBjoern A. Zeeb 539bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(trans, "WRT PRPH dump\n"); 540bfcc09ddSBjoern A. Zeeb 541bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(trans)) 542bfcc09ddSBjoern A. Zeeb return; 543bfcc09ddSBjoern A. Zeeb 544bfcc09ddSBjoern A. Zeeb for (i = 0; i < range_len; i++) { 545bfcc09ddSBjoern A. Zeeb /* The range includes both boundaries */ 546bfcc09ddSBjoern A. Zeeb int num_bytes_in_chunk = iwl_prph_dump_addr[i].end - 547bfcc09ddSBjoern A. Zeeb iwl_prph_dump_addr[i].start + 4; 548bfcc09ddSBjoern A. Zeeb 549bfcc09ddSBjoern A. Zeeb (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); 550bfcc09ddSBjoern A. Zeeb (*data)->len = cpu_to_le32(sizeof(*prph) + 551bfcc09ddSBjoern A. Zeeb num_bytes_in_chunk); 552bfcc09ddSBjoern A. Zeeb prph = (void *)(*data)->data; 553bfcc09ddSBjoern A. Zeeb prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start); 554bfcc09ddSBjoern A. Zeeb 555bfcc09ddSBjoern A. Zeeb iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start, 556bfcc09ddSBjoern A. Zeeb /* our range is inclusive, hence + 4 */ 557bfcc09ddSBjoern A. Zeeb iwl_prph_dump_addr[i].end - 558bfcc09ddSBjoern A. Zeeb iwl_prph_dump_addr[i].start + 4, 559bfcc09ddSBjoern A. Zeeb (void *)prph->data); 560bfcc09ddSBjoern A. Zeeb 561bfcc09ddSBjoern A. Zeeb *data = iwl_fw_error_next_data(*data); 562bfcc09ddSBjoern A. Zeeb } 563bfcc09ddSBjoern A. Zeeb 564bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(trans); 565bfcc09ddSBjoern A. Zeeb } 566bfcc09ddSBjoern A. Zeeb 567bfcc09ddSBjoern A. Zeeb /* 568bfcc09ddSBjoern A. Zeeb * alloc_sgtable - allocates scallerlist table in the given size, 569bfcc09ddSBjoern A. Zeeb * fills it with pages and returns it 570bfcc09ddSBjoern A. Zeeb * @size: the size (in bytes) of the table 571bfcc09ddSBjoern A. Zeeb */ 572bfcc09ddSBjoern A. Zeeb static struct scatterlist *alloc_sgtable(int size) 573bfcc09ddSBjoern A. Zeeb { 574bfcc09ddSBjoern A. Zeeb int alloc_size, nents, i; 575bfcc09ddSBjoern A. Zeeb struct page *new_page; 576bfcc09ddSBjoern A. Zeeb struct scatterlist *iter; 577bfcc09ddSBjoern A. Zeeb struct scatterlist *table; 578bfcc09ddSBjoern A. Zeeb 579bfcc09ddSBjoern A. Zeeb nents = DIV_ROUND_UP(size, PAGE_SIZE); 580bfcc09ddSBjoern A. Zeeb table = kcalloc(nents, sizeof(*table), GFP_KERNEL); 581bfcc09ddSBjoern A. Zeeb if (!table) 582bfcc09ddSBjoern A. Zeeb return NULL; 583bfcc09ddSBjoern A. Zeeb sg_init_table(table, nents); 584bfcc09ddSBjoern A. Zeeb iter = table; 585bfcc09ddSBjoern A. Zeeb for_each_sg(table, iter, sg_nents(table), i) { 586bfcc09ddSBjoern A. Zeeb new_page = alloc_page(GFP_KERNEL); 587bfcc09ddSBjoern A. Zeeb if (!new_page) { 588bfcc09ddSBjoern A. Zeeb /* release all previous allocated pages in the table */ 589bfcc09ddSBjoern A. Zeeb iter = table; 590bfcc09ddSBjoern A. Zeeb for_each_sg(table, iter, sg_nents(table), i) { 591bfcc09ddSBjoern A. Zeeb new_page = sg_page(iter); 592bfcc09ddSBjoern A. Zeeb if (new_page) 593bfcc09ddSBjoern A. Zeeb __free_page(new_page); 594bfcc09ddSBjoern A. Zeeb } 595bfcc09ddSBjoern A. Zeeb kfree(table); 596bfcc09ddSBjoern A. Zeeb return NULL; 597bfcc09ddSBjoern A. Zeeb } 598bfcc09ddSBjoern A. Zeeb alloc_size = min_t(int, size, PAGE_SIZE); 599bfcc09ddSBjoern A. Zeeb size -= PAGE_SIZE; 600bfcc09ddSBjoern A. Zeeb sg_set_page(iter, new_page, alloc_size, 0); 601bfcc09ddSBjoern A. Zeeb } 602bfcc09ddSBjoern A. Zeeb return table; 603bfcc09ddSBjoern A. Zeeb } 604bfcc09ddSBjoern A. Zeeb 605bfcc09ddSBjoern A. Zeeb static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt, 606bfcc09ddSBjoern A. Zeeb const struct iwl_prph_range *iwl_prph_dump_addr, 607bfcc09ddSBjoern A. Zeeb u32 range_len, void *ptr) 608bfcc09ddSBjoern A. Zeeb { 609bfcc09ddSBjoern A. Zeeb u32 *prph_len = (u32 *)ptr; 610bfcc09ddSBjoern A. Zeeb int i, num_bytes_in_chunk; 611bfcc09ddSBjoern A. Zeeb 612bfcc09ddSBjoern A. Zeeb if (!prph_len) 613bfcc09ddSBjoern A. Zeeb return; 614bfcc09ddSBjoern A. Zeeb 615bfcc09ddSBjoern A. Zeeb for (i = 0; i < range_len; i++) { 616bfcc09ddSBjoern A. Zeeb /* The range includes both boundaries */ 617bfcc09ddSBjoern A. Zeeb num_bytes_in_chunk = 618bfcc09ddSBjoern A. Zeeb iwl_prph_dump_addr[i].end - 619bfcc09ddSBjoern A. Zeeb iwl_prph_dump_addr[i].start + 4; 620bfcc09ddSBjoern A. Zeeb 621bfcc09ddSBjoern A. Zeeb *prph_len += sizeof(struct iwl_fw_error_dump_data) + 622bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_error_dump_prph) + 623bfcc09ddSBjoern A. Zeeb num_bytes_in_chunk; 624bfcc09ddSBjoern A. Zeeb } 625bfcc09ddSBjoern A. Zeeb } 626bfcc09ddSBjoern A. Zeeb 627bfcc09ddSBjoern A. Zeeb static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr, 628bfcc09ddSBjoern A. Zeeb void (*handler)(struct iwl_fw_runtime *, 629bfcc09ddSBjoern A. Zeeb const struct iwl_prph_range *, 630bfcc09ddSBjoern A. Zeeb u32, void *)) 631bfcc09ddSBjoern A. Zeeb { 632bfcc09ddSBjoern A. Zeeb u32 range_len; 633bfcc09ddSBjoern A. Zeeb 634bfcc09ddSBjoern A. Zeeb if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 635bfcc09ddSBjoern A. Zeeb range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210); 636bfcc09ddSBjoern A. Zeeb handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr); 637bfcc09ddSBjoern A. Zeeb } else if (fwrt->trans->trans_cfg->device_family >= 638bfcc09ddSBjoern A. Zeeb IWL_DEVICE_FAMILY_22000) { 639bfcc09ddSBjoern A. Zeeb range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000); 640bfcc09ddSBjoern A. Zeeb handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr); 641bfcc09ddSBjoern A. Zeeb } else { 642bfcc09ddSBjoern A. Zeeb range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm); 643bfcc09ddSBjoern A. Zeeb handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr); 644bfcc09ddSBjoern A. Zeeb 645bfcc09ddSBjoern A. Zeeb if (fwrt->trans->trans_cfg->mq_rx_supported) { 646bfcc09ddSBjoern A. Zeeb range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000); 647bfcc09ddSBjoern A. Zeeb handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr); 648bfcc09ddSBjoern A. Zeeb } 649bfcc09ddSBjoern A. Zeeb } 650bfcc09ddSBjoern A. Zeeb } 651bfcc09ddSBjoern A. Zeeb 652bfcc09ddSBjoern A. Zeeb static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt, 653bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **dump_data, 654bfcc09ddSBjoern A. Zeeb u32 len, u32 ofs, u32 type) 655bfcc09ddSBjoern A. Zeeb { 656bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_mem *dump_mem; 657bfcc09ddSBjoern A. Zeeb 658bfcc09ddSBjoern A. Zeeb if (!len) 659bfcc09ddSBjoern A. Zeeb return; 660bfcc09ddSBjoern A. Zeeb 661bfcc09ddSBjoern A. Zeeb (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); 662bfcc09ddSBjoern A. Zeeb (*dump_data)->len = cpu_to_le32(len + sizeof(*dump_mem)); 663bfcc09ddSBjoern A. Zeeb dump_mem = (void *)(*dump_data)->data; 664bfcc09ddSBjoern A. Zeeb dump_mem->type = cpu_to_le32(type); 665bfcc09ddSBjoern A. Zeeb dump_mem->offset = cpu_to_le32(ofs); 666bfcc09ddSBjoern A. Zeeb iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len); 667bfcc09ddSBjoern A. Zeeb *dump_data = iwl_fw_error_next_data(*dump_data); 668bfcc09ddSBjoern A. Zeeb 669bfcc09ddSBjoern A. Zeeb if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) 670bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, ofs, 671bfcc09ddSBjoern A. Zeeb dump_mem->data, len); 672bfcc09ddSBjoern A. Zeeb 673bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type); 674bfcc09ddSBjoern A. Zeeb } 675bfcc09ddSBjoern A. Zeeb 676bfcc09ddSBjoern A. Zeeb #define ADD_LEN(len, item_len, const_len) \ 677bfcc09ddSBjoern A. Zeeb do {size_t item = item_len; len += (!!item) * const_len + item; } \ 678bfcc09ddSBjoern A. Zeeb while (0) 679bfcc09ddSBjoern A. Zeeb 680bfcc09ddSBjoern A. Zeeb static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt, 681bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_shared_mem_cfg *mem_cfg) 682bfcc09ddSBjoern A. Zeeb { 683bfcc09ddSBjoern A. Zeeb size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) + 684bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_error_dump_fifo); 685bfcc09ddSBjoern A. Zeeb u32 fifo_len = 0; 686bfcc09ddSBjoern A. Zeeb int i; 687bfcc09ddSBjoern A. Zeeb 688bfcc09ddSBjoern A. Zeeb if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) 689bfcc09ddSBjoern A. Zeeb return 0; 690bfcc09ddSBjoern A. Zeeb 691bfcc09ddSBjoern A. Zeeb /* Count RXF2 size */ 692bfcc09ddSBjoern A. Zeeb ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len); 693bfcc09ddSBjoern A. Zeeb 694bfcc09ddSBjoern A. Zeeb /* Count RXF1 sizes */ 695bfcc09ddSBjoern A. Zeeb if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC)) 696bfcc09ddSBjoern A. Zeeb mem_cfg->num_lmacs = MAX_NUM_LMAC; 697bfcc09ddSBjoern A. Zeeb 698bfcc09ddSBjoern A. Zeeb for (i = 0; i < mem_cfg->num_lmacs; i++) 699bfcc09ddSBjoern A. Zeeb ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len); 700bfcc09ddSBjoern A. Zeeb 701bfcc09ddSBjoern A. Zeeb return fifo_len; 702bfcc09ddSBjoern A. Zeeb } 703bfcc09ddSBjoern A. Zeeb 704bfcc09ddSBjoern A. Zeeb static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt, 705bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_shared_mem_cfg *mem_cfg) 706bfcc09ddSBjoern A. Zeeb { 707bfcc09ddSBjoern A. Zeeb size_t hdr_len = sizeof(struct iwl_fw_error_dump_data) + 708bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_error_dump_fifo); 709bfcc09ddSBjoern A. Zeeb u32 fifo_len = 0; 710bfcc09ddSBjoern A. Zeeb int i; 711bfcc09ddSBjoern A. Zeeb 712bfcc09ddSBjoern A. Zeeb if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) 713bfcc09ddSBjoern A. Zeeb goto dump_internal_txf; 714bfcc09ddSBjoern A. Zeeb 715bfcc09ddSBjoern A. Zeeb /* Count TXF sizes */ 716bfcc09ddSBjoern A. Zeeb if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC)) 717bfcc09ddSBjoern A. Zeeb mem_cfg->num_lmacs = MAX_NUM_LMAC; 718bfcc09ddSBjoern A. Zeeb 719bfcc09ddSBjoern A. Zeeb for (i = 0; i < mem_cfg->num_lmacs; i++) { 720bfcc09ddSBjoern A. Zeeb int j; 721bfcc09ddSBjoern A. Zeeb 722bfcc09ddSBjoern A. Zeeb for (j = 0; j < mem_cfg->num_txfifo_entries; j++) 723bfcc09ddSBjoern A. Zeeb ADD_LEN(fifo_len, mem_cfg->lmac[i].txfifo_size[j], 724bfcc09ddSBjoern A. Zeeb hdr_len); 725bfcc09ddSBjoern A. Zeeb } 726bfcc09ddSBjoern A. Zeeb 727bfcc09ddSBjoern A. Zeeb dump_internal_txf: 728bfcc09ddSBjoern A. Zeeb if (!(iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) && 729bfcc09ddSBjoern A. Zeeb fw_has_capa(&fwrt->fw->ucode_capa, 730bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG))) 731bfcc09ddSBjoern A. Zeeb goto out; 732bfcc09ddSBjoern A. Zeeb 733bfcc09ddSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); i++) 734bfcc09ddSBjoern A. Zeeb ADD_LEN(fifo_len, mem_cfg->internal_txfifo_size[i], hdr_len); 735bfcc09ddSBjoern A. Zeeb 736bfcc09ddSBjoern A. Zeeb out: 737bfcc09ddSBjoern A. Zeeb return fifo_len; 738bfcc09ddSBjoern A. Zeeb } 739bfcc09ddSBjoern A. Zeeb 740bfcc09ddSBjoern A. Zeeb static void iwl_dump_paging(struct iwl_fw_runtime *fwrt, 741bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data **data) 742bfcc09ddSBjoern A. Zeeb { 743bfcc09ddSBjoern A. Zeeb int i; 744bfcc09ddSBjoern A. Zeeb 745bfcc09ddSBjoern A. Zeeb IWL_DEBUG_INFO(fwrt, "WRT paging dump\n"); 746bfcc09ddSBjoern A. Zeeb for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) { 747bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_paging *paging; 748bfcc09ddSBjoern A. Zeeb struct page *pages = 749bfcc09ddSBjoern A. Zeeb fwrt->fw_paging_db[i].fw_paging_block; 750bfcc09ddSBjoern A. Zeeb dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys; 751bfcc09ddSBjoern A. Zeeb 752bfcc09ddSBjoern A. Zeeb (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); 753bfcc09ddSBjoern A. Zeeb (*data)->len = cpu_to_le32(sizeof(*paging) + 754bfcc09ddSBjoern A. Zeeb PAGING_BLOCK_SIZE); 755bfcc09ddSBjoern A. Zeeb paging = (void *)(*data)->data; 756bfcc09ddSBjoern A. Zeeb paging->index = cpu_to_le32(i); 757bfcc09ddSBjoern A. Zeeb dma_sync_single_for_cpu(fwrt->trans->dev, addr, 758bfcc09ddSBjoern A. Zeeb PAGING_BLOCK_SIZE, 759bfcc09ddSBjoern A. Zeeb DMA_BIDIRECTIONAL); 760bfcc09ddSBjoern A. Zeeb memcpy(paging->data, page_address(pages), 761bfcc09ddSBjoern A. Zeeb PAGING_BLOCK_SIZE); 762bfcc09ddSBjoern A. Zeeb dma_sync_single_for_device(fwrt->trans->dev, addr, 763bfcc09ddSBjoern A. Zeeb PAGING_BLOCK_SIZE, 764bfcc09ddSBjoern A. Zeeb DMA_BIDIRECTIONAL); 765bfcc09ddSBjoern A. Zeeb (*data) = iwl_fw_error_next_data(*data); 766bfcc09ddSBjoern A. Zeeb 767bfcc09ddSBjoern A. Zeeb if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) 768bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, 769bfcc09ddSBjoern A. Zeeb fwrt->fw_paging_db[i].fw_offs, 770bfcc09ddSBjoern A. Zeeb paging->data, 771bfcc09ddSBjoern A. Zeeb PAGING_BLOCK_SIZE); 772bfcc09ddSBjoern A. Zeeb } 773bfcc09ddSBjoern A. Zeeb } 774bfcc09ddSBjoern A. Zeeb 775bfcc09ddSBjoern A. Zeeb static struct iwl_fw_error_dump_file * 776bfcc09ddSBjoern A. Zeeb iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, 777bfcc09ddSBjoern A. Zeeb struct iwl_fw_dump_ptrs *fw_error_dump, 778bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *data) 779bfcc09ddSBjoern A. Zeeb { 780bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_file *dump_file; 781bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data *dump_data; 782bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_info *dump_info; 783bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_smem_cfg *dump_smem_cfg; 784bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_trigger_desc *dump_trig; 785bfcc09ddSBjoern A. Zeeb u32 sram_len, sram_ofs; 786bfcc09ddSBjoern A. Zeeb const struct iwl_fw_dbg_mem_seg_tlv *fw_mem = fwrt->fw->dbg.mem_tlv; 787bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg; 788bfcc09ddSBjoern A. Zeeb u32 file_len, fifo_len = 0, prph_len = 0, radio_len = 0; 789bfcc09ddSBjoern A. Zeeb u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->cfg->smem_len; 790bfcc09ddSBjoern A. Zeeb u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ? 791bfcc09ddSBjoern A. Zeeb 0 : fwrt->trans->cfg->dccm2_len; 792bfcc09ddSBjoern A. Zeeb int i; 793bfcc09ddSBjoern A. Zeeb 794bfcc09ddSBjoern A. Zeeb /* SRAM - include stack CCM if driver knows the values for it */ 795bfcc09ddSBjoern A. Zeeb if (!fwrt->trans->cfg->dccm_offset || !fwrt->trans->cfg->dccm_len) { 796bfcc09ddSBjoern A. Zeeb const struct fw_img *img; 797bfcc09ddSBjoern A. Zeeb 798bfcc09ddSBjoern A. Zeeb if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX) 799bfcc09ddSBjoern A. Zeeb return NULL; 800bfcc09ddSBjoern A. Zeeb img = &fwrt->fw->img[fwrt->cur_fw_img]; 801bfcc09ddSBjoern A. Zeeb sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; 802bfcc09ddSBjoern A. Zeeb sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; 803bfcc09ddSBjoern A. Zeeb } else { 804bfcc09ddSBjoern A. Zeeb sram_ofs = fwrt->trans->cfg->dccm_offset; 805bfcc09ddSBjoern A. Zeeb sram_len = fwrt->trans->cfg->dccm_len; 806bfcc09ddSBjoern A. Zeeb } 807bfcc09ddSBjoern A. Zeeb 808bfcc09ddSBjoern A. Zeeb /* reading RXF/TXF sizes */ 809bfcc09ddSBjoern A. Zeeb if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) { 810bfcc09ddSBjoern A. Zeeb fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg); 811bfcc09ddSBjoern A. Zeeb fifo_len += iwl_fw_txf_len(fwrt, mem_cfg); 812bfcc09ddSBjoern A. Zeeb 813bfcc09ddSBjoern A. Zeeb /* Make room for PRPH registers */ 814bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH)) 815bfcc09ddSBjoern A. Zeeb iwl_fw_prph_handler(fwrt, &prph_len, 816bfcc09ddSBjoern A. Zeeb iwl_fw_get_prph_len); 817bfcc09ddSBjoern A. Zeeb 818bfcc09ddSBjoern A. Zeeb if (fwrt->trans->trans_cfg->device_family == 819bfcc09ddSBjoern A. Zeeb IWL_DEVICE_FAMILY_7000 && 820bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG)) 821bfcc09ddSBjoern A. Zeeb radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; 822bfcc09ddSBjoern A. Zeeb } 823bfcc09ddSBjoern A. Zeeb 824bfcc09ddSBjoern A. Zeeb file_len = sizeof(*dump_file) + fifo_len + prph_len + radio_len; 825bfcc09ddSBjoern A. Zeeb 826bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) 827bfcc09ddSBjoern A. Zeeb file_len += sizeof(*dump_data) + sizeof(*dump_info); 828bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) 829bfcc09ddSBjoern A. Zeeb file_len += sizeof(*dump_data) + sizeof(*dump_smem_cfg); 830bfcc09ddSBjoern A. Zeeb 831bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) { 832bfcc09ddSBjoern A. Zeeb size_t hdr_len = sizeof(*dump_data) + 833bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_error_dump_mem); 834bfcc09ddSBjoern A. Zeeb 835bfcc09ddSBjoern A. Zeeb /* Dump SRAM only if no mem_tlvs */ 836bfcc09ddSBjoern A. Zeeb if (!fwrt->fw->dbg.n_mem_tlv) 837bfcc09ddSBjoern A. Zeeb ADD_LEN(file_len, sram_len, hdr_len); 838bfcc09ddSBjoern A. Zeeb 839bfcc09ddSBjoern A. Zeeb /* Make room for all mem types that exist */ 840bfcc09ddSBjoern A. Zeeb ADD_LEN(file_len, smem_len, hdr_len); 841bfcc09ddSBjoern A. Zeeb ADD_LEN(file_len, sram2_len, hdr_len); 842bfcc09ddSBjoern A. Zeeb 843bfcc09ddSBjoern A. Zeeb for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) 844bfcc09ddSBjoern A. Zeeb ADD_LEN(file_len, le32_to_cpu(fw_mem[i].len), hdr_len); 845bfcc09ddSBjoern A. Zeeb } 846bfcc09ddSBjoern A. Zeeb 847bfcc09ddSBjoern A. Zeeb /* Make room for fw's virtual image pages, if it exists */ 848bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_is_paging_enabled(fwrt)) 849bfcc09ddSBjoern A. Zeeb file_len += fwrt->num_of_paging_blk * 850bfcc09ddSBjoern A. Zeeb (sizeof(*dump_data) + 851bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_error_dump_paging) + 852bfcc09ddSBjoern A. Zeeb PAGING_BLOCK_SIZE); 853bfcc09ddSBjoern A. Zeeb 854bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { 855bfcc09ddSBjoern A. Zeeb file_len += sizeof(*dump_data) + 856bfcc09ddSBjoern A. Zeeb fwrt->trans->cfg->d3_debug_data_length * 2; 857bfcc09ddSBjoern A. Zeeb } 858bfcc09ddSBjoern A. Zeeb 859bfcc09ddSBjoern A. Zeeb /* If we only want a monitor dump, reset the file length */ 860bfcc09ddSBjoern A. Zeeb if (data->monitor_only) { 861bfcc09ddSBjoern A. Zeeb file_len = sizeof(*dump_file) + sizeof(*dump_data) * 2 + 862bfcc09ddSBjoern A. Zeeb sizeof(*dump_info) + sizeof(*dump_smem_cfg); 863bfcc09ddSBjoern A. Zeeb } 864bfcc09ddSBjoern A. Zeeb 865bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) && 866bfcc09ddSBjoern A. Zeeb data->desc) 867bfcc09ddSBjoern A. Zeeb file_len += sizeof(*dump_data) + sizeof(*dump_trig) + 868bfcc09ddSBjoern A. Zeeb data->desc->len; 869bfcc09ddSBjoern A. Zeeb 870bfcc09ddSBjoern A. Zeeb dump_file = vzalloc(file_len); 871bfcc09ddSBjoern A. Zeeb if (!dump_file) 872bfcc09ddSBjoern A. Zeeb return NULL; 873bfcc09ddSBjoern A. Zeeb 874bfcc09ddSBjoern A. Zeeb fw_error_dump->fwrt_ptr = dump_file; 875bfcc09ddSBjoern A. Zeeb 876bfcc09ddSBjoern A. Zeeb dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER); 877bfcc09ddSBjoern A. Zeeb dump_data = (void *)dump_file->data; 878bfcc09ddSBjoern A. Zeeb 879bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) { 880bfcc09ddSBjoern A. Zeeb dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO); 881bfcc09ddSBjoern A. Zeeb dump_data->len = cpu_to_le32(sizeof(*dump_info)); 882bfcc09ddSBjoern A. Zeeb dump_info = (void *)dump_data->data; 883bfcc09ddSBjoern A. Zeeb dump_info->hw_type = 884bfcc09ddSBjoern A. Zeeb cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->hw_rev)); 885bfcc09ddSBjoern A. Zeeb dump_info->hw_step = 886bfcc09ddSBjoern A. Zeeb cpu_to_le32(CSR_HW_REV_STEP(fwrt->trans->hw_rev)); 887bfcc09ddSBjoern A. Zeeb memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable, 888bfcc09ddSBjoern A. Zeeb sizeof(dump_info->fw_human_readable)); 889bfcc09ddSBjoern A. Zeeb strncpy(dump_info->dev_human_readable, fwrt->trans->name, 890bfcc09ddSBjoern A. Zeeb sizeof(dump_info->dev_human_readable) - 1); 891bfcc09ddSBjoern A. Zeeb #if defined(__linux__) 892bfcc09ddSBjoern A. Zeeb strncpy(dump_info->bus_human_readable, fwrt->dev->bus->name, 893bfcc09ddSBjoern A. Zeeb sizeof(dump_info->bus_human_readable) - 1); 894bfcc09ddSBjoern A. Zeeb #elif defined(__FreeBSD__) /* XXX TODO */ 895bfcc09ddSBjoern A. Zeeb strncpy(dump_info->bus_human_readable, "<bus>", 896bfcc09ddSBjoern A. Zeeb sizeof(dump_info->bus_human_readable) - 1); 897bfcc09ddSBjoern A. Zeeb #endif 898bfcc09ddSBjoern A. Zeeb dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs; 899bfcc09ddSBjoern A. Zeeb dump_info->lmac_err_id[0] = 900bfcc09ddSBjoern A. Zeeb cpu_to_le32(fwrt->dump.lmac_err_id[0]); 901bfcc09ddSBjoern A. Zeeb if (fwrt->smem_cfg.num_lmacs > 1) 902bfcc09ddSBjoern A. Zeeb dump_info->lmac_err_id[1] = 903bfcc09ddSBjoern A. Zeeb cpu_to_le32(fwrt->dump.lmac_err_id[1]); 904bfcc09ddSBjoern A. Zeeb dump_info->umac_err_id = cpu_to_le32(fwrt->dump.umac_err_id); 905bfcc09ddSBjoern A. Zeeb 906bfcc09ddSBjoern A. Zeeb dump_data = iwl_fw_error_next_data(dump_data); 907bfcc09ddSBjoern A. Zeeb } 908bfcc09ddSBjoern A. Zeeb 909bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) { 910bfcc09ddSBjoern A. Zeeb /* Dump shared memory configuration */ 911bfcc09ddSBjoern A. Zeeb dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG); 912bfcc09ddSBjoern A. Zeeb dump_data->len = cpu_to_le32(sizeof(*dump_smem_cfg)); 913bfcc09ddSBjoern A. Zeeb dump_smem_cfg = (void *)dump_data->data; 914bfcc09ddSBjoern A. Zeeb dump_smem_cfg->num_lmacs = cpu_to_le32(mem_cfg->num_lmacs); 915bfcc09ddSBjoern A. Zeeb dump_smem_cfg->num_txfifo_entries = 916bfcc09ddSBjoern A. Zeeb cpu_to_le32(mem_cfg->num_txfifo_entries); 917bfcc09ddSBjoern A. Zeeb for (i = 0; i < MAX_NUM_LMAC; i++) { 918bfcc09ddSBjoern A. Zeeb int j; 919bfcc09ddSBjoern A. Zeeb u32 *txf_size = mem_cfg->lmac[i].txfifo_size; 920bfcc09ddSBjoern A. Zeeb 921bfcc09ddSBjoern A. Zeeb for (j = 0; j < TX_FIFO_MAX_NUM; j++) 922bfcc09ddSBjoern A. Zeeb dump_smem_cfg->lmac[i].txfifo_size[j] = 923bfcc09ddSBjoern A. Zeeb cpu_to_le32(txf_size[j]); 924bfcc09ddSBjoern A. Zeeb dump_smem_cfg->lmac[i].rxfifo1_size = 925bfcc09ddSBjoern A. Zeeb cpu_to_le32(mem_cfg->lmac[i].rxfifo1_size); 926bfcc09ddSBjoern A. Zeeb } 927bfcc09ddSBjoern A. Zeeb dump_smem_cfg->rxfifo2_size = 928bfcc09ddSBjoern A. Zeeb cpu_to_le32(mem_cfg->rxfifo2_size); 929bfcc09ddSBjoern A. Zeeb dump_smem_cfg->internal_txfifo_addr = 930bfcc09ddSBjoern A. Zeeb cpu_to_le32(mem_cfg->internal_txfifo_addr); 931bfcc09ddSBjoern A. Zeeb for (i = 0; i < TX_FIFO_INTERNAL_MAX_NUM; i++) { 932bfcc09ddSBjoern A. Zeeb dump_smem_cfg->internal_txfifo_size[i] = 933bfcc09ddSBjoern A. Zeeb cpu_to_le32(mem_cfg->internal_txfifo_size[i]); 934bfcc09ddSBjoern A. Zeeb } 935bfcc09ddSBjoern A. Zeeb 936bfcc09ddSBjoern A. Zeeb dump_data = iwl_fw_error_next_data(dump_data); 937bfcc09ddSBjoern A. Zeeb } 938bfcc09ddSBjoern A. Zeeb 939bfcc09ddSBjoern A. Zeeb /* We only dump the FIFOs if the FW is in error state */ 940bfcc09ddSBjoern A. Zeeb if (fifo_len) { 941bfcc09ddSBjoern A. Zeeb iwl_fw_dump_rxf(fwrt, &dump_data); 942bfcc09ddSBjoern A. Zeeb iwl_fw_dump_txf(fwrt, &dump_data); 943bfcc09ddSBjoern A. Zeeb } 944bfcc09ddSBjoern A. Zeeb 945bfcc09ddSBjoern A. Zeeb if (radio_len) 946bfcc09ddSBjoern A. Zeeb iwl_read_radio_regs(fwrt, &dump_data); 947bfcc09ddSBjoern A. Zeeb 948bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) && 949bfcc09ddSBjoern A. Zeeb data->desc) { 950bfcc09ddSBjoern A. Zeeb dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); 951bfcc09ddSBjoern A. Zeeb dump_data->len = cpu_to_le32(sizeof(*dump_trig) + 952bfcc09ddSBjoern A. Zeeb data->desc->len); 953bfcc09ddSBjoern A. Zeeb dump_trig = (void *)dump_data->data; 954bfcc09ddSBjoern A. Zeeb memcpy(dump_trig, &data->desc->trig_desc, 955bfcc09ddSBjoern A. Zeeb sizeof(*dump_trig) + data->desc->len); 956bfcc09ddSBjoern A. Zeeb 957bfcc09ddSBjoern A. Zeeb dump_data = iwl_fw_error_next_data(dump_data); 958bfcc09ddSBjoern A. Zeeb } 959bfcc09ddSBjoern A. Zeeb 960bfcc09ddSBjoern A. Zeeb /* In case we only want monitor dump, skip to dump trasport data */ 961bfcc09ddSBjoern A. Zeeb if (data->monitor_only) 962bfcc09ddSBjoern A. Zeeb goto out; 963bfcc09ddSBjoern A. Zeeb 964bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) { 965bfcc09ddSBjoern A. Zeeb const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem = 966bfcc09ddSBjoern A. Zeeb fwrt->fw->dbg.mem_tlv; 967bfcc09ddSBjoern A. Zeeb 968bfcc09ddSBjoern A. Zeeb if (!fwrt->fw->dbg.n_mem_tlv) 969bfcc09ddSBjoern A. Zeeb iwl_fw_dump_mem(fwrt, &dump_data, sram_len, sram_ofs, 970bfcc09ddSBjoern A. Zeeb IWL_FW_ERROR_DUMP_MEM_SRAM); 971bfcc09ddSBjoern A. Zeeb 972bfcc09ddSBjoern A. Zeeb for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) { 973bfcc09ddSBjoern A. Zeeb u32 len = le32_to_cpu(fw_dbg_mem[i].len); 974bfcc09ddSBjoern A. Zeeb u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs); 975bfcc09ddSBjoern A. Zeeb 976bfcc09ddSBjoern A. Zeeb iwl_fw_dump_mem(fwrt, &dump_data, len, ofs, 977bfcc09ddSBjoern A. Zeeb le32_to_cpu(fw_dbg_mem[i].data_type)); 978bfcc09ddSBjoern A. Zeeb } 979bfcc09ddSBjoern A. Zeeb 980bfcc09ddSBjoern A. Zeeb iwl_fw_dump_mem(fwrt, &dump_data, smem_len, 981bfcc09ddSBjoern A. Zeeb fwrt->trans->cfg->smem_offset, 982bfcc09ddSBjoern A. Zeeb IWL_FW_ERROR_DUMP_MEM_SMEM); 983bfcc09ddSBjoern A. Zeeb 984bfcc09ddSBjoern A. Zeeb iwl_fw_dump_mem(fwrt, &dump_data, sram2_len, 985bfcc09ddSBjoern A. Zeeb fwrt->trans->cfg->dccm2_offset, 986bfcc09ddSBjoern A. Zeeb IWL_FW_ERROR_DUMP_MEM_SRAM); 987bfcc09ddSBjoern A. Zeeb } 988bfcc09ddSBjoern A. Zeeb 989bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { 990bfcc09ddSBjoern A. Zeeb u32 addr = fwrt->trans->cfg->d3_debug_data_base_addr; 991bfcc09ddSBjoern A. Zeeb size_t data_size = fwrt->trans->cfg->d3_debug_data_length; 992bfcc09ddSBjoern A. Zeeb 993bfcc09ddSBjoern A. Zeeb dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); 994bfcc09ddSBjoern A. Zeeb dump_data->len = cpu_to_le32(data_size * 2); 995bfcc09ddSBjoern A. Zeeb 996bfcc09ddSBjoern A. Zeeb memcpy(dump_data->data, fwrt->dump.d3_debug_data, data_size); 997bfcc09ddSBjoern A. Zeeb 998bfcc09ddSBjoern A. Zeeb kfree(fwrt->dump.d3_debug_data); 999bfcc09ddSBjoern A. Zeeb fwrt->dump.d3_debug_data = NULL; 1000bfcc09ddSBjoern A. Zeeb 1001bfcc09ddSBjoern A. Zeeb iwl_trans_read_mem_bytes(fwrt->trans, addr, 1002bfcc09ddSBjoern A. Zeeb dump_data->data + data_size, 1003bfcc09ddSBjoern A. Zeeb data_size); 1004bfcc09ddSBjoern A. Zeeb 1005bfcc09ddSBjoern A. Zeeb if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) 1006bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, addr, 1007bfcc09ddSBjoern A. Zeeb dump_data->data + data_size, 1008bfcc09ddSBjoern A. Zeeb data_size); 1009bfcc09ddSBjoern A. Zeeb 1010bfcc09ddSBjoern A. Zeeb dump_data = iwl_fw_error_next_data(dump_data); 1011bfcc09ddSBjoern A. Zeeb } 1012bfcc09ddSBjoern A. Zeeb 1013bfcc09ddSBjoern A. Zeeb /* Dump fw's virtual image */ 1014bfcc09ddSBjoern A. Zeeb if (iwl_fw_dbg_is_paging_enabled(fwrt)) 1015bfcc09ddSBjoern A. Zeeb iwl_dump_paging(fwrt, &dump_data); 1016bfcc09ddSBjoern A. Zeeb 1017bfcc09ddSBjoern A. Zeeb if (prph_len) 1018bfcc09ddSBjoern A. Zeeb iwl_fw_prph_handler(fwrt, &dump_data, iwl_dump_prph); 1019bfcc09ddSBjoern A. Zeeb 1020bfcc09ddSBjoern A. Zeeb out: 1021bfcc09ddSBjoern A. Zeeb dump_file->file_len = cpu_to_le32(file_len); 1022bfcc09ddSBjoern A. Zeeb return dump_file; 1023bfcc09ddSBjoern A. Zeeb } 1024bfcc09ddSBjoern A. Zeeb 1025bfcc09ddSBjoern A. Zeeb /** 1026bfcc09ddSBjoern A. Zeeb * struct iwl_dump_ini_region_data - region data 1027bfcc09ddSBjoern A. Zeeb * @reg_tlv: region TLV 1028bfcc09ddSBjoern A. Zeeb * @dump_data: dump data 1029bfcc09ddSBjoern A. Zeeb */ 1030bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data { 1031bfcc09ddSBjoern A. Zeeb struct iwl_ucode_tlv *reg_tlv; 1032bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *dump_data; 1033bfcc09ddSBjoern A. Zeeb }; 1034bfcc09ddSBjoern A. Zeeb 1035bfcc09ddSBjoern A. Zeeb static int 1036bfcc09ddSBjoern A. Zeeb iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt, 1037bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1038bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1039bfcc09ddSBjoern A. Zeeb { 1040bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1041bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1042bfcc09ddSBjoern A. Zeeb __le32 *val = range->data; 1043bfcc09ddSBjoern A. Zeeb u32 prph_val; 1044bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(reg->addrs[idx]) + 1045bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->dev_addr.offset); 1046bfcc09ddSBjoern A. Zeeb int i; 1047bfcc09ddSBjoern A. Zeeb 1048bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1049bfcc09ddSBjoern A. Zeeb range->range_data_size = reg->dev_addr.size; 1050bfcc09ddSBjoern A. Zeeb for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) { 1051bfcc09ddSBjoern A. Zeeb prph_val = iwl_read_prph(fwrt->trans, addr + i); 1052bfcc09ddSBjoern A. Zeeb if (prph_val == 0x5a5a5a5a) 1053bfcc09ddSBjoern A. Zeeb return -EBUSY; 1054bfcc09ddSBjoern A. Zeeb *val++ = cpu_to_le32(prph_val); 1055bfcc09ddSBjoern A. Zeeb } 1056bfcc09ddSBjoern A. Zeeb 1057bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1058bfcc09ddSBjoern A. Zeeb } 1059bfcc09ddSBjoern A. Zeeb 1060bfcc09ddSBjoern A. Zeeb static int 1061bfcc09ddSBjoern A. Zeeb iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt, 1062bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1063bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1064bfcc09ddSBjoern A. Zeeb { 1065bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1066bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1067bfcc09ddSBjoern A. Zeeb __le32 *val = range->data; 1068bfcc09ddSBjoern A. Zeeb u32 indirect_wr_addr = WMAL_INDRCT_RD_CMD1; 1069bfcc09ddSBjoern A. Zeeb u32 indirect_rd_addr = WMAL_MRSPF_1; 1070bfcc09ddSBjoern A. Zeeb u32 prph_val; 1071bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(reg->addrs[idx]); 1072bfcc09ddSBjoern A. Zeeb u32 dphy_state; 1073bfcc09ddSBjoern A. Zeeb u32 dphy_addr; 1074bfcc09ddSBjoern A. Zeeb int i; 1075bfcc09ddSBjoern A. Zeeb 1076bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1077bfcc09ddSBjoern A. Zeeb range->range_data_size = reg->dev_addr.size; 1078bfcc09ddSBjoern A. Zeeb 1079bfcc09ddSBjoern A. Zeeb if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) 1080bfcc09ddSBjoern A. Zeeb indirect_wr_addr = WMAL_INDRCT_CMD1; 1081bfcc09ddSBjoern A. Zeeb 1082bfcc09ddSBjoern A. Zeeb indirect_wr_addr += le32_to_cpu(reg->dev_addr.offset); 1083bfcc09ddSBjoern A. Zeeb indirect_rd_addr += le32_to_cpu(reg->dev_addr.offset); 1084bfcc09ddSBjoern A. Zeeb 1085bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) 1086bfcc09ddSBjoern A. Zeeb return -EBUSY; 1087bfcc09ddSBjoern A. Zeeb 1088bfcc09ddSBjoern A. Zeeb dphy_addr = (reg->dev_addr.offset) ? WFPM_LMAC2_PS_CTL_RW : 1089bfcc09ddSBjoern A. Zeeb WFPM_LMAC1_PS_CTL_RW; 1090bfcc09ddSBjoern A. Zeeb dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr); 1091bfcc09ddSBjoern A. Zeeb 1092bfcc09ddSBjoern A. Zeeb for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) { 1093bfcc09ddSBjoern A. Zeeb if (dphy_state == HBUS_TIMEOUT || 1094bfcc09ddSBjoern A. Zeeb (dphy_state & WFPM_PS_CTL_RW_PHYRF_PD_FSM_CURSTATE_MSK) != 1095bfcc09ddSBjoern A. Zeeb WFPM_PHYRF_STATE_ON) { 1096bfcc09ddSBjoern A. Zeeb *val++ = cpu_to_le32(WFPM_DPHY_OFF); 1097bfcc09ddSBjoern A. Zeeb continue; 1098bfcc09ddSBjoern A. Zeeb } 1099bfcc09ddSBjoern A. Zeeb 1100bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, indirect_wr_addr, 1101bfcc09ddSBjoern A. Zeeb WMAL_INDRCT_CMD(addr + i)); 1102bfcc09ddSBjoern A. Zeeb prph_val = iwl_read_prph_no_grab(fwrt->trans, 1103bfcc09ddSBjoern A. Zeeb indirect_rd_addr); 1104bfcc09ddSBjoern A. Zeeb *val++ = cpu_to_le32(prph_val); 1105bfcc09ddSBjoern A. Zeeb } 1106bfcc09ddSBjoern A. Zeeb 1107bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 1108bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1109bfcc09ddSBjoern A. Zeeb } 1110bfcc09ddSBjoern A. Zeeb 1111bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, 1112bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1113bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1114bfcc09ddSBjoern A. Zeeb { 1115bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1116bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1117bfcc09ddSBjoern A. Zeeb __le32 *val = range->data; 1118bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(reg->addrs[idx]) + 1119bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->dev_addr.offset); 1120bfcc09ddSBjoern A. Zeeb int i; 1121bfcc09ddSBjoern A. Zeeb 1122bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1123bfcc09ddSBjoern A. Zeeb range->range_data_size = reg->dev_addr.size; 1124bfcc09ddSBjoern A. Zeeb for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) 1125bfcc09ddSBjoern A. Zeeb *val++ = cpu_to_le32(iwl_trans_read32(fwrt->trans, addr + i)); 1126bfcc09ddSBjoern A. Zeeb 1127bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1128bfcc09ddSBjoern A. Zeeb } 1129bfcc09ddSBjoern A. Zeeb 1130bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_config_iter(struct iwl_fw_runtime *fwrt, 1131bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1132bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1133bfcc09ddSBjoern A. Zeeb { 1134bfcc09ddSBjoern A. Zeeb struct iwl_trans *trans = fwrt->trans; 1135bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1136bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1137bfcc09ddSBjoern A. Zeeb __le32 *val = range->data; 1138bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(reg->addrs[idx]) + 1139bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->dev_addr.offset); 1140bfcc09ddSBjoern A. Zeeb int i; 1141bfcc09ddSBjoern A. Zeeb 1142bfcc09ddSBjoern A. Zeeb /* we shouldn't get here if the trans doesn't have read_config32 */ 1143bfcc09ddSBjoern A. Zeeb if (WARN_ON_ONCE(!trans->ops->read_config32)) 1144bfcc09ddSBjoern A. Zeeb return -EOPNOTSUPP; 1145bfcc09ddSBjoern A. Zeeb 1146bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1147bfcc09ddSBjoern A. Zeeb range->range_data_size = reg->dev_addr.size; 1148bfcc09ddSBjoern A. Zeeb for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) { 1149bfcc09ddSBjoern A. Zeeb int ret; 1150bfcc09ddSBjoern A. Zeeb u32 tmp; 1151bfcc09ddSBjoern A. Zeeb 1152bfcc09ddSBjoern A. Zeeb ret = trans->ops->read_config32(trans, addr + i, &tmp); 1153bfcc09ddSBjoern A. Zeeb if (ret < 0) 1154bfcc09ddSBjoern A. Zeeb return ret; 1155bfcc09ddSBjoern A. Zeeb 1156bfcc09ddSBjoern A. Zeeb *val++ = cpu_to_le32(tmp); 1157bfcc09ddSBjoern A. Zeeb } 1158bfcc09ddSBjoern A. Zeeb 1159bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1160bfcc09ddSBjoern A. Zeeb } 1161bfcc09ddSBjoern A. Zeeb 1162bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, 1163bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1164bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1165bfcc09ddSBjoern A. Zeeb { 1166bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1167bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1168bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(reg->addrs[idx]) + 1169bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->dev_addr.offset); 1170bfcc09ddSBjoern A. Zeeb 1171bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1172bfcc09ddSBjoern A. Zeeb range->range_data_size = reg->dev_addr.size; 1173bfcc09ddSBjoern A. Zeeb iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, 1174bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->dev_addr.size)); 1175bfcc09ddSBjoern A. Zeeb 1176bfcc09ddSBjoern A. Zeeb if ((le32_to_cpu(reg->id) & IWL_FW_INI_REGION_V2_MASK) == 1177bfcc09ddSBjoern A. Zeeb IWL_FW_INI_HW_SMEM_REGION_ID && 1178bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) 1179bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx, 1180bfcc09ddSBjoern A. Zeeb range->data, 1181bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->dev_addr.size)); 1182bfcc09ddSBjoern A. Zeeb 1183bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1184bfcc09ddSBjoern A. Zeeb } 1185bfcc09ddSBjoern A. Zeeb 1186bfcc09ddSBjoern A. Zeeb static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, 1187bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1188bfcc09ddSBjoern A. Zeeb { 1189bfcc09ddSBjoern A. Zeeb struct page *page = fwrt->fw_paging_db[idx].fw_paging_block; 1190bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1191bfcc09ddSBjoern A. Zeeb dma_addr_t addr = fwrt->fw_paging_db[idx].fw_paging_phys; 1192bfcc09ddSBjoern A. Zeeb u32 page_size = fwrt->fw_paging_db[idx].fw_paging_size; 1193bfcc09ddSBjoern A. Zeeb 1194bfcc09ddSBjoern A. Zeeb range->page_num = cpu_to_le32(idx); 1195bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(page_size); 1196bfcc09ddSBjoern A. Zeeb dma_sync_single_for_cpu(fwrt->trans->dev, addr, page_size, 1197bfcc09ddSBjoern A. Zeeb DMA_BIDIRECTIONAL); 1198bfcc09ddSBjoern A. Zeeb memcpy(range->data, page_address(page), page_size); 1199bfcc09ddSBjoern A. Zeeb dma_sync_single_for_device(fwrt->trans->dev, addr, page_size, 1200bfcc09ddSBjoern A. Zeeb DMA_BIDIRECTIONAL); 1201bfcc09ddSBjoern A. Zeeb 1202bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1203bfcc09ddSBjoern A. Zeeb } 1204bfcc09ddSBjoern A. Zeeb 1205bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, 1206bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1207bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1208bfcc09ddSBjoern A. Zeeb { 1209bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range; 1210bfcc09ddSBjoern A. Zeeb u32 page_size; 1211bfcc09ddSBjoern A. Zeeb 1212bfcc09ddSBjoern A. Zeeb /* all paged index start from 1 to skip CSS section */ 1213bfcc09ddSBjoern A. Zeeb idx++; 1214bfcc09ddSBjoern A. Zeeb 1215bfcc09ddSBjoern A. Zeeb if (!fwrt->trans->trans_cfg->gen2) 1216bfcc09ddSBjoern A. Zeeb return _iwl_dump_ini_paging_iter(fwrt, range_ptr, idx); 1217bfcc09ddSBjoern A. Zeeb 1218bfcc09ddSBjoern A. Zeeb range = range_ptr; 1219bfcc09ddSBjoern A. Zeeb page_size = fwrt->trans->init_dram.paging[idx].size; 1220bfcc09ddSBjoern A. Zeeb 1221bfcc09ddSBjoern A. Zeeb range->page_num = cpu_to_le32(idx); 1222bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(page_size); 1223bfcc09ddSBjoern A. Zeeb memcpy(range->data, fwrt->trans->init_dram.paging[idx].block, 1224bfcc09ddSBjoern A. Zeeb page_size); 1225bfcc09ddSBjoern A. Zeeb 1226bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1227bfcc09ddSBjoern A. Zeeb } 1228bfcc09ddSBjoern A. Zeeb 1229bfcc09ddSBjoern A. Zeeb static int 1230bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt, 1231bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1232bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1233bfcc09ddSBjoern A. Zeeb { 1234bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1235bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1236bfcc09ddSBjoern A. Zeeb struct iwl_dram_data *frag; 1237bfcc09ddSBjoern A. Zeeb u32 alloc_id = le32_to_cpu(reg->dram_alloc_id); 1238bfcc09ddSBjoern A. Zeeb 1239bfcc09ddSBjoern A. Zeeb frag = &fwrt->trans->dbg.fw_mon_ini[alloc_id].frags[idx]; 1240bfcc09ddSBjoern A. Zeeb 1241bfcc09ddSBjoern A. Zeeb range->dram_base_addr = cpu_to_le64(frag->physical); 1242bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(frag->size); 1243bfcc09ddSBjoern A. Zeeb 1244bfcc09ddSBjoern A. Zeeb memcpy(range->data, frag->block, frag->size); 1245bfcc09ddSBjoern A. Zeeb 1246bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1247bfcc09ddSBjoern A. Zeeb } 1248bfcc09ddSBjoern A. Zeeb 1249bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_mon_smem_iter(struct iwl_fw_runtime *fwrt, 1250bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1251bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1252bfcc09ddSBjoern A. Zeeb { 1253bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1254bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1255bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(reg->internal_buffer.base_addr); 1256bfcc09ddSBjoern A. Zeeb 1257bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1258bfcc09ddSBjoern A. Zeeb range->range_data_size = reg->internal_buffer.size; 1259bfcc09ddSBjoern A. Zeeb iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, 1260bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->internal_buffer.size)); 1261bfcc09ddSBjoern A. Zeeb 1262bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1263bfcc09ddSBjoern A. Zeeb } 1264bfcc09ddSBjoern A. Zeeb 1265bfcc09ddSBjoern A. Zeeb static bool iwl_ini_txf_iter(struct iwl_fw_runtime *fwrt, 1266bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, int idx) 1267bfcc09ddSBjoern A. Zeeb { 1268bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1269bfcc09ddSBjoern A. Zeeb struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data; 1270bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg; 1271bfcc09ddSBjoern A. Zeeb int txf_num = cfg->num_txfifo_entries; 1272bfcc09ddSBjoern A. Zeeb int int_txf_num = ARRAY_SIZE(cfg->internal_txfifo_size); 1273bfcc09ddSBjoern A. Zeeb u32 lmac_bitmap = le32_to_cpu(reg->fifos.fid[0]); 1274bfcc09ddSBjoern A. Zeeb 1275bfcc09ddSBjoern A. Zeeb if (!idx) { 1276bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(reg->fifos.offset) && cfg->num_lmacs == 1) { 1277bfcc09ddSBjoern A. Zeeb IWL_ERR(fwrt, "WRT: Invalid lmac offset 0x%x\n", 1278bfcc09ddSBjoern A. Zeeb le32_to_cpu(reg->fifos.offset)); 1279bfcc09ddSBjoern A. Zeeb return false; 1280bfcc09ddSBjoern A. Zeeb } 1281bfcc09ddSBjoern A. Zeeb 1282bfcc09ddSBjoern A. Zeeb iter->internal_txf = 0; 1283bfcc09ddSBjoern A. Zeeb iter->fifo_size = 0; 1284bfcc09ddSBjoern A. Zeeb iter->fifo = -1; 1285bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(reg->fifos.offset)) 1286bfcc09ddSBjoern A. Zeeb iter->lmac = 1; 1287bfcc09ddSBjoern A. Zeeb else 1288bfcc09ddSBjoern A. Zeeb iter->lmac = 0; 1289bfcc09ddSBjoern A. Zeeb } 1290bfcc09ddSBjoern A. Zeeb 1291bfcc09ddSBjoern A. Zeeb if (!iter->internal_txf) { 1292bfcc09ddSBjoern A. Zeeb for (iter->fifo++; iter->fifo < txf_num; iter->fifo++) { 1293bfcc09ddSBjoern A. Zeeb iter->fifo_size = 1294bfcc09ddSBjoern A. Zeeb cfg->lmac[iter->lmac].txfifo_size[iter->fifo]; 1295bfcc09ddSBjoern A. Zeeb if (iter->fifo_size && (lmac_bitmap & BIT(iter->fifo))) 1296bfcc09ddSBjoern A. Zeeb return true; 1297bfcc09ddSBjoern A. Zeeb } 1298bfcc09ddSBjoern A. Zeeb iter->fifo--; 1299bfcc09ddSBjoern A. Zeeb } 1300bfcc09ddSBjoern A. Zeeb 1301bfcc09ddSBjoern A. Zeeb iter->internal_txf = 1; 1302bfcc09ddSBjoern A. Zeeb 1303bfcc09ddSBjoern A. Zeeb if (!fw_has_capa(&fwrt->fw->ucode_capa, 1304bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) 1305bfcc09ddSBjoern A. Zeeb return false; 1306bfcc09ddSBjoern A. Zeeb 1307bfcc09ddSBjoern A. Zeeb for (iter->fifo++; iter->fifo < int_txf_num + txf_num; iter->fifo++) { 1308bfcc09ddSBjoern A. Zeeb iter->fifo_size = 1309bfcc09ddSBjoern A. Zeeb cfg->internal_txfifo_size[iter->fifo - txf_num]; 1310bfcc09ddSBjoern A. Zeeb if (iter->fifo_size && (lmac_bitmap & BIT(iter->fifo))) 1311bfcc09ddSBjoern A. Zeeb return true; 1312bfcc09ddSBjoern A. Zeeb } 1313bfcc09ddSBjoern A. Zeeb 1314bfcc09ddSBjoern A. Zeeb return false; 1315bfcc09ddSBjoern A. Zeeb } 1316bfcc09ddSBjoern A. Zeeb 1317bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt, 1318bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1319bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1320bfcc09ddSBjoern A. Zeeb { 1321bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1322bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1323bfcc09ddSBjoern A. Zeeb struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data; 1324bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data; 1325bfcc09ddSBjoern A. Zeeb u32 offs = le32_to_cpu(reg->fifos.offset), addr; 1326bfcc09ddSBjoern A. Zeeb u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs); 1327bfcc09ddSBjoern A. Zeeb u32 registers_size = registers_num * sizeof(*reg_dump); 1328bfcc09ddSBjoern A. Zeeb __le32 *data; 1329bfcc09ddSBjoern A. Zeeb int i; 1330bfcc09ddSBjoern A. Zeeb 1331bfcc09ddSBjoern A. Zeeb if (!iwl_ini_txf_iter(fwrt, reg_data, idx)) 1332bfcc09ddSBjoern A. Zeeb return -EIO; 1333bfcc09ddSBjoern A. Zeeb 1334bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) 1335bfcc09ddSBjoern A. Zeeb return -EBUSY; 1336bfcc09ddSBjoern A. Zeeb 1337bfcc09ddSBjoern A. Zeeb range->fifo_hdr.fifo_num = cpu_to_le32(iter->fifo); 1338bfcc09ddSBjoern A. Zeeb range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num); 1339bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(iter->fifo_size + registers_size); 1340bfcc09ddSBjoern A. Zeeb 1341bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, TXF_LARC_NUM + offs, iter->fifo); 1342bfcc09ddSBjoern A. Zeeb 1343bfcc09ddSBjoern A. Zeeb /* 1344bfcc09ddSBjoern A. Zeeb * read txf registers. for each register, write to the dump the 1345bfcc09ddSBjoern A. Zeeb * register address and its value 1346bfcc09ddSBjoern A. Zeeb */ 1347bfcc09ddSBjoern A. Zeeb for (i = 0; i < registers_num; i++) { 1348bfcc09ddSBjoern A. Zeeb addr = le32_to_cpu(reg->addrs[i]) + offs; 1349bfcc09ddSBjoern A. Zeeb 1350bfcc09ddSBjoern A. Zeeb reg_dump->addr = cpu_to_le32(addr); 1351bfcc09ddSBjoern A. Zeeb reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, 1352bfcc09ddSBjoern A. Zeeb addr)); 1353bfcc09ddSBjoern A. Zeeb 1354bfcc09ddSBjoern A. Zeeb reg_dump++; 1355bfcc09ddSBjoern A. Zeeb } 1356bfcc09ddSBjoern A. Zeeb 1357bfcc09ddSBjoern A. Zeeb if (reg->fifos.hdr_only) { 1358bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(registers_size); 1359bfcc09ddSBjoern A. Zeeb goto out; 1360bfcc09ddSBjoern A. Zeeb } 1361bfcc09ddSBjoern A. Zeeb 1362bfcc09ddSBjoern A. Zeeb /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ 1363bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_ADDR + offs, 1364bfcc09ddSBjoern A. Zeeb TXF_WR_PTR + offs); 1365bfcc09ddSBjoern A. Zeeb 1366bfcc09ddSBjoern A. Zeeb /* Dummy-read to advance the read pointer to the head */ 1367bfcc09ddSBjoern A. Zeeb iwl_read_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_DATA + offs); 1368bfcc09ddSBjoern A. Zeeb 1369bfcc09ddSBjoern A. Zeeb /* Read FIFO */ 1370bfcc09ddSBjoern A. Zeeb addr = TXF_READ_MODIFY_DATA + offs; 1371bfcc09ddSBjoern A. Zeeb data = (void *)reg_dump; 1372bfcc09ddSBjoern A. Zeeb for (i = 0; i < iter->fifo_size; i += sizeof(*data)) 1373bfcc09ddSBjoern A. Zeeb *data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr)); 1374bfcc09ddSBjoern A. Zeeb 1375bfcc09ddSBjoern A. Zeeb if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf) 1376bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx, 1377bfcc09ddSBjoern A. Zeeb reg_dump, iter->fifo_size); 1378bfcc09ddSBjoern A. Zeeb 1379bfcc09ddSBjoern A. Zeeb out: 1380bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 1381bfcc09ddSBjoern A. Zeeb 1382bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1383bfcc09ddSBjoern A. Zeeb } 1384bfcc09ddSBjoern A. Zeeb 1385bfcc09ddSBjoern A. Zeeb struct iwl_ini_rxf_data { 1386bfcc09ddSBjoern A. Zeeb u32 fifo_num; 1387bfcc09ddSBjoern A. Zeeb u32 size; 1388bfcc09ddSBjoern A. Zeeb u32 offset; 1389bfcc09ddSBjoern A. Zeeb }; 1390bfcc09ddSBjoern A. Zeeb 1391bfcc09ddSBjoern A. Zeeb static void iwl_ini_get_rxf_data(struct iwl_fw_runtime *fwrt, 1392bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1393bfcc09ddSBjoern A. Zeeb struct iwl_ini_rxf_data *data) 1394bfcc09ddSBjoern A. Zeeb { 1395bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1396bfcc09ddSBjoern A. Zeeb u32 fid1 = le32_to_cpu(reg->fifos.fid[0]); 1397bfcc09ddSBjoern A. Zeeb u32 fid2 = le32_to_cpu(reg->fifos.fid[1]); 1398bfcc09ddSBjoern A. Zeeb u8 fifo_idx; 1399bfcc09ddSBjoern A. Zeeb 1400bfcc09ddSBjoern A. Zeeb if (!data) 1401bfcc09ddSBjoern A. Zeeb return; 1402bfcc09ddSBjoern A. Zeeb 1403bfcc09ddSBjoern A. Zeeb /* make sure only one bit is set in only one fid */ 1404bfcc09ddSBjoern A. Zeeb if (WARN_ONCE(hweight_long(fid1) + hweight_long(fid2) != 1, 1405bfcc09ddSBjoern A. Zeeb "fid1=%x, fid2=%x\n", fid1, fid2)) 1406bfcc09ddSBjoern A. Zeeb return; 1407bfcc09ddSBjoern A. Zeeb 1408bfcc09ddSBjoern A. Zeeb memset(data, 0, sizeof(*data)); 1409bfcc09ddSBjoern A. Zeeb 1410bfcc09ddSBjoern A. Zeeb if (fid1) { 1411bfcc09ddSBjoern A. Zeeb fifo_idx = ffs(fid1) - 1; 1412bfcc09ddSBjoern A. Zeeb if (WARN_ONCE(fifo_idx >= MAX_NUM_LMAC, "fifo_idx=%d\n", 1413bfcc09ddSBjoern A. Zeeb fifo_idx)) 1414bfcc09ddSBjoern A. Zeeb return; 1415bfcc09ddSBjoern A. Zeeb 1416bfcc09ddSBjoern A. Zeeb data->size = fwrt->smem_cfg.lmac[fifo_idx].rxfifo1_size; 1417bfcc09ddSBjoern A. Zeeb data->fifo_num = fifo_idx; 1418bfcc09ddSBjoern A. Zeeb } else { 1419bfcc09ddSBjoern A. Zeeb u8 max_idx; 1420bfcc09ddSBjoern A. Zeeb 1421bfcc09ddSBjoern A. Zeeb fifo_idx = ffs(fid2) - 1; 1422bfcc09ddSBjoern A. Zeeb if (iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP, 1423bfcc09ddSBjoern A. Zeeb SHARED_MEM_CFG_CMD, 0) <= 3) 1424bfcc09ddSBjoern A. Zeeb max_idx = 0; 1425bfcc09ddSBjoern A. Zeeb else 1426bfcc09ddSBjoern A. Zeeb max_idx = 1; 1427bfcc09ddSBjoern A. Zeeb 1428bfcc09ddSBjoern A. Zeeb if (WARN_ONCE(fifo_idx > max_idx, 1429bfcc09ddSBjoern A. Zeeb "invalid umac fifo idx %d", fifo_idx)) 1430bfcc09ddSBjoern A. Zeeb return; 1431bfcc09ddSBjoern A. Zeeb 1432bfcc09ddSBjoern A. Zeeb /* use bit 31 to distinguish between umac and lmac rxf while 1433bfcc09ddSBjoern A. Zeeb * parsing the dump 1434bfcc09ddSBjoern A. Zeeb */ 1435bfcc09ddSBjoern A. Zeeb data->fifo_num = fifo_idx | IWL_RXF_UMAC_BIT; 1436bfcc09ddSBjoern A. Zeeb 1437bfcc09ddSBjoern A. Zeeb switch (fifo_idx) { 1438bfcc09ddSBjoern A. Zeeb case 0: 1439bfcc09ddSBjoern A. Zeeb data->size = fwrt->smem_cfg.rxfifo2_size; 1440bfcc09ddSBjoern A. Zeeb data->offset = iwl_umac_prph(fwrt->trans, 1441bfcc09ddSBjoern A. Zeeb RXF_DIFF_FROM_PREV); 1442bfcc09ddSBjoern A. Zeeb break; 1443bfcc09ddSBjoern A. Zeeb case 1: 1444bfcc09ddSBjoern A. Zeeb data->size = fwrt->smem_cfg.rxfifo2_control_size; 1445bfcc09ddSBjoern A. Zeeb data->offset = iwl_umac_prph(fwrt->trans, 1446bfcc09ddSBjoern A. Zeeb RXF2C_DIFF_FROM_PREV); 1447bfcc09ddSBjoern A. Zeeb break; 1448bfcc09ddSBjoern A. Zeeb } 1449bfcc09ddSBjoern A. Zeeb } 1450bfcc09ddSBjoern A. Zeeb } 1451bfcc09ddSBjoern A. Zeeb 1452bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt, 1453bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1454bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1455bfcc09ddSBjoern A. Zeeb { 1456bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1457bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1458bfcc09ddSBjoern A. Zeeb struct iwl_ini_rxf_data rxf_data; 1459bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data; 1460bfcc09ddSBjoern A. Zeeb u32 offs = le32_to_cpu(reg->fifos.offset), addr; 1461bfcc09ddSBjoern A. Zeeb u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs); 1462bfcc09ddSBjoern A. Zeeb u32 registers_size = registers_num * sizeof(*reg_dump); 1463bfcc09ddSBjoern A. Zeeb __le32 *data; 1464bfcc09ddSBjoern A. Zeeb int i; 1465bfcc09ddSBjoern A. Zeeb 1466bfcc09ddSBjoern A. Zeeb iwl_ini_get_rxf_data(fwrt, reg_data, &rxf_data); 1467bfcc09ddSBjoern A. Zeeb if (!rxf_data.size) 1468bfcc09ddSBjoern A. Zeeb return -EIO; 1469bfcc09ddSBjoern A. Zeeb 1470bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) 1471bfcc09ddSBjoern A. Zeeb return -EBUSY; 1472bfcc09ddSBjoern A. Zeeb 1473bfcc09ddSBjoern A. Zeeb range->fifo_hdr.fifo_num = cpu_to_le32(rxf_data.fifo_num); 1474bfcc09ddSBjoern A. Zeeb range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num); 1475bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(rxf_data.size + registers_size); 1476bfcc09ddSBjoern A. Zeeb 1477bfcc09ddSBjoern A. Zeeb /* 1478bfcc09ddSBjoern A. Zeeb * read rxf registers. for each register, write to the dump the 1479bfcc09ddSBjoern A. Zeeb * register address and its value 1480bfcc09ddSBjoern A. Zeeb */ 1481bfcc09ddSBjoern A. Zeeb for (i = 0; i < registers_num; i++) { 1482bfcc09ddSBjoern A. Zeeb addr = le32_to_cpu(reg->addrs[i]) + offs; 1483bfcc09ddSBjoern A. Zeeb 1484bfcc09ddSBjoern A. Zeeb reg_dump->addr = cpu_to_le32(addr); 1485bfcc09ddSBjoern A. Zeeb reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, 1486bfcc09ddSBjoern A. Zeeb addr)); 1487bfcc09ddSBjoern A. Zeeb 1488bfcc09ddSBjoern A. Zeeb reg_dump++; 1489bfcc09ddSBjoern A. Zeeb } 1490bfcc09ddSBjoern A. Zeeb 1491bfcc09ddSBjoern A. Zeeb if (reg->fifos.hdr_only) { 1492bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(registers_size); 1493bfcc09ddSBjoern A. Zeeb goto out; 1494bfcc09ddSBjoern A. Zeeb } 1495bfcc09ddSBjoern A. Zeeb 1496bfcc09ddSBjoern A. Zeeb offs = rxf_data.offset; 1497bfcc09ddSBjoern A. Zeeb 1498bfcc09ddSBjoern A. Zeeb /* Lock fence */ 1499bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, RXF_SET_FENCE_MODE + offs, 0x1); 1500bfcc09ddSBjoern A. Zeeb /* Set fence pointer to the same place like WR pointer */ 1501bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, RXF_LD_WR2FENCE + offs, 0x1); 1502bfcc09ddSBjoern A. Zeeb /* Set fence offset */ 1503bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, RXF_LD_FENCE_OFFSET_ADDR + offs, 1504bfcc09ddSBjoern A. Zeeb 0x0); 1505bfcc09ddSBjoern A. Zeeb 1506bfcc09ddSBjoern A. Zeeb /* Read FIFO */ 1507bfcc09ddSBjoern A. Zeeb addr = RXF_FIFO_RD_FENCE_INC + offs; 1508bfcc09ddSBjoern A. Zeeb data = (void *)reg_dump; 1509bfcc09ddSBjoern A. Zeeb for (i = 0; i < rxf_data.size; i += sizeof(*data)) 1510bfcc09ddSBjoern A. Zeeb *data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr)); 1511bfcc09ddSBjoern A. Zeeb 1512bfcc09ddSBjoern A. Zeeb out: 1513bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 1514bfcc09ddSBjoern A. Zeeb 1515bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1516bfcc09ddSBjoern A. Zeeb } 1517bfcc09ddSBjoern A. Zeeb 1518bfcc09ddSBjoern A. Zeeb static int 1519bfcc09ddSBjoern A. Zeeb iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt, 1520bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1521bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1522bfcc09ddSBjoern A. Zeeb { 1523bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1524bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_err_table *err_table = ®->err_table; 1525bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1526bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(err_table->base_addr) + 1527bfcc09ddSBjoern A. Zeeb le32_to_cpu(err_table->offset); 1528bfcc09ddSBjoern A. Zeeb 1529bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1530bfcc09ddSBjoern A. Zeeb range->range_data_size = err_table->size; 1531bfcc09ddSBjoern A. Zeeb iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, 1532bfcc09ddSBjoern A. Zeeb le32_to_cpu(err_table->size)); 1533bfcc09ddSBjoern A. Zeeb 1534bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1535bfcc09ddSBjoern A. Zeeb } 1536bfcc09ddSBjoern A. Zeeb 1537bfcc09ddSBjoern A. Zeeb static int 1538bfcc09ddSBjoern A. Zeeb iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt, 1539bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1540bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1541bfcc09ddSBjoern A. Zeeb { 1542bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1543bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_special_device_memory *special_mem = 1544bfcc09ddSBjoern A. Zeeb ®->special_mem; 1545bfcc09ddSBjoern A. Zeeb 1546bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1547bfcc09ddSBjoern A. Zeeb u32 addr = le32_to_cpu(special_mem->base_addr) + 1548bfcc09ddSBjoern A. Zeeb le32_to_cpu(special_mem->offset); 1549bfcc09ddSBjoern A. Zeeb 1550bfcc09ddSBjoern A. Zeeb range->internal_base_addr = cpu_to_le32(addr); 1551bfcc09ddSBjoern A. Zeeb range->range_data_size = special_mem->size; 1552bfcc09ddSBjoern A. Zeeb iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data, 1553bfcc09ddSBjoern A. Zeeb le32_to_cpu(special_mem->size)); 1554bfcc09ddSBjoern A. Zeeb 1555bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1556bfcc09ddSBjoern A. Zeeb } 1557bfcc09ddSBjoern A. Zeeb 1558bfcc09ddSBjoern A. Zeeb static int 1559bfcc09ddSBjoern A. Zeeb iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt, 1560bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1561bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1562bfcc09ddSBjoern A. Zeeb { 1563bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1564bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1565bfcc09ddSBjoern A. Zeeb __le32 *val = range->data; 1566bfcc09ddSBjoern A. Zeeb u32 prph_data; 1567bfcc09ddSBjoern A. Zeeb int i; 1568bfcc09ddSBjoern A. Zeeb 1569bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) 1570bfcc09ddSBjoern A. Zeeb return -EBUSY; 1571bfcc09ddSBjoern A. Zeeb 1572bfcc09ddSBjoern A. Zeeb range->range_data_size = reg->dev_addr.size; 1573bfcc09ddSBjoern A. Zeeb iwl_write_prph_no_grab(fwrt->trans, DBGI_SRAM_TARGET_ACCESS_CFG, 1574bfcc09ddSBjoern A. Zeeb DBGI_SRAM_TARGET_ACCESS_CFG_RESET_ADDRESS_MSK); 1575bfcc09ddSBjoern A. Zeeb for (i = 0; i < (le32_to_cpu(reg->dev_addr.size) / 4); i++) { 1576bfcc09ddSBjoern A. Zeeb prph_data = iwl_read_prph(fwrt->trans, (i % 2) ? 1577bfcc09ddSBjoern A. Zeeb DBGI_SRAM_TARGET_ACCESS_RDATA_MSB : 1578bfcc09ddSBjoern A. Zeeb DBGI_SRAM_TARGET_ACCESS_RDATA_LSB); 1579bfcc09ddSBjoern A. Zeeb if (prph_data == 0x5a5a5a5a) { 1580bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 1581bfcc09ddSBjoern A. Zeeb return -EBUSY; 1582bfcc09ddSBjoern A. Zeeb } 1583bfcc09ddSBjoern A. Zeeb *val++ = cpu_to_le32(prph_data); 1584bfcc09ddSBjoern A. Zeeb } 1585bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 1586bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1587bfcc09ddSBjoern A. Zeeb } 1588bfcc09ddSBjoern A. Zeeb 1589bfcc09ddSBjoern A. Zeeb static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt, 1590bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1591bfcc09ddSBjoern A. Zeeb void *range_ptr, int idx) 1592bfcc09ddSBjoern A. Zeeb { 1593bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_range *range = range_ptr; 1594bfcc09ddSBjoern A. Zeeb struct iwl_rx_packet *pkt = reg_data->dump_data->fw_pkt; 1595bfcc09ddSBjoern A. Zeeb u32 pkt_len; 1596bfcc09ddSBjoern A. Zeeb 1597bfcc09ddSBjoern A. Zeeb if (!pkt) 1598bfcc09ddSBjoern A. Zeeb return -EIO; 1599bfcc09ddSBjoern A. Zeeb 1600bfcc09ddSBjoern A. Zeeb pkt_len = iwl_rx_packet_payload_len(pkt); 1601bfcc09ddSBjoern A. Zeeb 1602bfcc09ddSBjoern A. Zeeb memcpy(&range->fw_pkt_hdr, &pkt->hdr, sizeof(range->fw_pkt_hdr)); 1603bfcc09ddSBjoern A. Zeeb range->range_data_size = cpu_to_le32(pkt_len); 1604bfcc09ddSBjoern A. Zeeb 1605bfcc09ddSBjoern A. Zeeb memcpy(range->data, pkt->data, pkt_len); 1606bfcc09ddSBjoern A. Zeeb 1607bfcc09ddSBjoern A. Zeeb return sizeof(*range) + le32_to_cpu(range->range_data_size); 1608bfcc09ddSBjoern A. Zeeb } 1609bfcc09ddSBjoern A. Zeeb 1610bfcc09ddSBjoern A. Zeeb static void * 1611bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, 1612bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1613bfcc09ddSBjoern A. Zeeb void *data) 1614bfcc09ddSBjoern A. Zeeb { 1615bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump *dump = data; 1616bfcc09ddSBjoern A. Zeeb 1617bfcc09ddSBjoern A. Zeeb dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER); 1618bfcc09ddSBjoern A. Zeeb 1619bfcc09ddSBjoern A. Zeeb return dump->data; 1620bfcc09ddSBjoern A. Zeeb } 1621bfcc09ddSBjoern A. Zeeb 1622bfcc09ddSBjoern A. Zeeb /** 1623bfcc09ddSBjoern A. Zeeb * mask_apply_and_normalize - applies mask on val and normalize the result 1624bfcc09ddSBjoern A. Zeeb * 1625bfcc09ddSBjoern A. Zeeb * The normalization is based on the first set bit in the mask 1626bfcc09ddSBjoern A. Zeeb * 1627bfcc09ddSBjoern A. Zeeb * @val: value 1628bfcc09ddSBjoern A. Zeeb * @mask: mask to apply and to normalize with 1629bfcc09ddSBjoern A. Zeeb */ 1630bfcc09ddSBjoern A. Zeeb static u32 mask_apply_and_normalize(u32 val, u32 mask) 1631bfcc09ddSBjoern A. Zeeb { 1632bfcc09ddSBjoern A. Zeeb return (val & mask) >> (ffs(mask) - 1); 1633bfcc09ddSBjoern A. Zeeb } 1634bfcc09ddSBjoern A. Zeeb 1635bfcc09ddSBjoern A. Zeeb static __le32 iwl_get_mon_reg(struct iwl_fw_runtime *fwrt, u32 alloc_id, 1636bfcc09ddSBjoern A. Zeeb const struct iwl_fw_mon_reg *reg_info) 1637bfcc09ddSBjoern A. Zeeb { 1638bfcc09ddSBjoern A. Zeeb u32 val, offs; 1639bfcc09ddSBjoern A. Zeeb 1640bfcc09ddSBjoern A. Zeeb /* The header addresses of DBGCi is calculate as follows: 1641bfcc09ddSBjoern A. Zeeb * DBGC1 address + (0x100 * i) 1642bfcc09ddSBjoern A. Zeeb */ 1643bfcc09ddSBjoern A. Zeeb offs = (alloc_id - IWL_FW_INI_ALLOCATION_ID_DBGC1) * 0x100; 1644bfcc09ddSBjoern A. Zeeb 1645bfcc09ddSBjoern A. Zeeb if (!reg_info || !reg_info->addr || !reg_info->mask) 1646bfcc09ddSBjoern A. Zeeb return 0; 1647bfcc09ddSBjoern A. Zeeb 1648bfcc09ddSBjoern A. Zeeb val = iwl_read_prph_no_grab(fwrt->trans, reg_info->addr + offs); 1649bfcc09ddSBjoern A. Zeeb 1650bfcc09ddSBjoern A. Zeeb return cpu_to_le32(mask_apply_and_normalize(val, reg_info->mask)); 1651bfcc09ddSBjoern A. Zeeb } 1652bfcc09ddSBjoern A. Zeeb 1653bfcc09ddSBjoern A. Zeeb static void * 1654bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, 1655bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1656bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_monitor_dump *data, 1657bfcc09ddSBjoern A. Zeeb const struct iwl_fw_mon_regs *addrs) 1658bfcc09ddSBjoern A. Zeeb { 1659bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1660bfcc09ddSBjoern A. Zeeb u32 alloc_id = le32_to_cpu(reg->dram_alloc_id); 1661bfcc09ddSBjoern A. Zeeb 1662bfcc09ddSBjoern A. Zeeb if (!iwl_trans_grab_nic_access(fwrt->trans)) { 1663bfcc09ddSBjoern A. Zeeb IWL_ERR(fwrt, "Failed to get monitor header\n"); 1664bfcc09ddSBjoern A. Zeeb return NULL; 1665bfcc09ddSBjoern A. Zeeb } 1666bfcc09ddSBjoern A. Zeeb 1667bfcc09ddSBjoern A. Zeeb data->write_ptr = iwl_get_mon_reg(fwrt, alloc_id, 1668bfcc09ddSBjoern A. Zeeb &addrs->write_ptr); 1669bfcc09ddSBjoern A. Zeeb if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { 1670bfcc09ddSBjoern A. Zeeb u32 wrt_ptr = le32_to_cpu(data->write_ptr); 1671bfcc09ddSBjoern A. Zeeb 1672bfcc09ddSBjoern A. Zeeb data->write_ptr = cpu_to_le32(wrt_ptr >> 2); 1673bfcc09ddSBjoern A. Zeeb } 1674bfcc09ddSBjoern A. Zeeb data->cycle_cnt = iwl_get_mon_reg(fwrt, alloc_id, 1675bfcc09ddSBjoern A. Zeeb &addrs->cycle_cnt); 1676bfcc09ddSBjoern A. Zeeb data->cur_frag = iwl_get_mon_reg(fwrt, alloc_id, 1677bfcc09ddSBjoern A. Zeeb &addrs->cur_frag); 1678bfcc09ddSBjoern A. Zeeb 1679bfcc09ddSBjoern A. Zeeb iwl_trans_release_nic_access(fwrt->trans); 1680bfcc09ddSBjoern A. Zeeb 1681bfcc09ddSBjoern A. Zeeb data->header.version = cpu_to_le32(IWL_INI_DUMP_VER); 1682bfcc09ddSBjoern A. Zeeb 1683bfcc09ddSBjoern A. Zeeb return data->data; 1684bfcc09ddSBjoern A. Zeeb } 1685bfcc09ddSBjoern A. Zeeb 1686bfcc09ddSBjoern A. Zeeb static void * 1687bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt, 1688bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1689bfcc09ddSBjoern A. Zeeb void *data) 1690bfcc09ddSBjoern A. Zeeb { 1691bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; 1692bfcc09ddSBjoern A. Zeeb 1693bfcc09ddSBjoern A. Zeeb return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, 1694bfcc09ddSBjoern A. Zeeb &fwrt->trans->cfg->mon_dram_regs); 1695bfcc09ddSBjoern A. Zeeb } 1696bfcc09ddSBjoern A. Zeeb 1697bfcc09ddSBjoern A. Zeeb static void * 1698bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt, 1699bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1700bfcc09ddSBjoern A. Zeeb void *data) 1701bfcc09ddSBjoern A. Zeeb { 1702bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data; 1703bfcc09ddSBjoern A. Zeeb 1704bfcc09ddSBjoern A. Zeeb return iwl_dump_ini_mon_fill_header(fwrt, reg_data, mon_dump, 1705bfcc09ddSBjoern A. Zeeb &fwrt->trans->cfg->mon_smem_regs); 1706bfcc09ddSBjoern A. Zeeb } 1707bfcc09ddSBjoern A. Zeeb 1708bfcc09ddSBjoern A. Zeeb static void * 1709bfcc09ddSBjoern A. Zeeb iwl_dump_ini_err_table_fill_header(struct iwl_fw_runtime *fwrt, 1710bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1711bfcc09ddSBjoern A. Zeeb void *data) 1712bfcc09ddSBjoern A. Zeeb { 1713bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1714bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_err_table_dump *dump = data; 1715bfcc09ddSBjoern A. Zeeb 1716bfcc09ddSBjoern A. Zeeb dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER); 1717bfcc09ddSBjoern A. Zeeb dump->version = reg->err_table.version; 1718bfcc09ddSBjoern A. Zeeb 1719bfcc09ddSBjoern A. Zeeb return dump->data; 1720bfcc09ddSBjoern A. Zeeb } 1721bfcc09ddSBjoern A. Zeeb 1722bfcc09ddSBjoern A. Zeeb static void * 1723bfcc09ddSBjoern A. Zeeb iwl_dump_ini_special_mem_fill_header(struct iwl_fw_runtime *fwrt, 1724bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1725bfcc09ddSBjoern A. Zeeb void *data) 1726bfcc09ddSBjoern A. Zeeb { 1727bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1728bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_special_device_memory *dump = data; 1729bfcc09ddSBjoern A. Zeeb 1730bfcc09ddSBjoern A. Zeeb dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER); 1731bfcc09ddSBjoern A. Zeeb dump->type = reg->special_mem.type; 1732bfcc09ddSBjoern A. Zeeb dump->version = reg->special_mem.version; 1733bfcc09ddSBjoern A. Zeeb 1734bfcc09ddSBjoern A. Zeeb return dump->data; 1735bfcc09ddSBjoern A. Zeeb } 1736bfcc09ddSBjoern A. Zeeb 1737bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt, 1738bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1739bfcc09ddSBjoern A. Zeeb { 1740bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1741bfcc09ddSBjoern A. Zeeb 1742bfcc09ddSBjoern A. Zeeb return iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs); 1743bfcc09ddSBjoern A. Zeeb } 1744bfcc09ddSBjoern A. Zeeb 1745bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt, 1746bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1747bfcc09ddSBjoern A. Zeeb { 1748bfcc09ddSBjoern A. Zeeb if (fwrt->trans->trans_cfg->gen2) { 1749bfcc09ddSBjoern A. Zeeb if (fwrt->trans->init_dram.paging_cnt) 1750bfcc09ddSBjoern A. Zeeb return fwrt->trans->init_dram.paging_cnt - 1; 1751bfcc09ddSBjoern A. Zeeb else 1752bfcc09ddSBjoern A. Zeeb return 0; 1753bfcc09ddSBjoern A. Zeeb } 1754bfcc09ddSBjoern A. Zeeb 1755bfcc09ddSBjoern A. Zeeb return fwrt->num_of_paging_blk; 1756bfcc09ddSBjoern A. Zeeb } 1757bfcc09ddSBjoern A. Zeeb 1758bfcc09ddSBjoern A. Zeeb static u32 1759bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mon_dram_ranges(struct iwl_fw_runtime *fwrt, 1760bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1761bfcc09ddSBjoern A. Zeeb { 1762bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1763bfcc09ddSBjoern A. Zeeb struct iwl_fw_mon *fw_mon; 1764bfcc09ddSBjoern A. Zeeb u32 ranges = 0, alloc_id = le32_to_cpu(reg->dram_alloc_id); 1765bfcc09ddSBjoern A. Zeeb int i; 1766bfcc09ddSBjoern A. Zeeb 1767bfcc09ddSBjoern A. Zeeb fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id]; 1768bfcc09ddSBjoern A. Zeeb 1769bfcc09ddSBjoern A. Zeeb for (i = 0; i < fw_mon->num_frags; i++) { 1770bfcc09ddSBjoern A. Zeeb if (!fw_mon->frags[i].size) 1771bfcc09ddSBjoern A. Zeeb break; 1772bfcc09ddSBjoern A. Zeeb 1773bfcc09ddSBjoern A. Zeeb ranges++; 1774bfcc09ddSBjoern A. Zeeb } 1775bfcc09ddSBjoern A. Zeeb 1776bfcc09ddSBjoern A. Zeeb return ranges; 1777bfcc09ddSBjoern A. Zeeb } 1778bfcc09ddSBjoern A. Zeeb 1779bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_txf_ranges(struct iwl_fw_runtime *fwrt, 1780bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1781bfcc09ddSBjoern A. Zeeb { 1782bfcc09ddSBjoern A. Zeeb u32 num_of_fifos = 0; 1783bfcc09ddSBjoern A. Zeeb 1784bfcc09ddSBjoern A. Zeeb while (iwl_ini_txf_iter(fwrt, reg_data, num_of_fifos)) 1785bfcc09ddSBjoern A. Zeeb num_of_fifos++; 1786bfcc09ddSBjoern A. Zeeb 1787bfcc09ddSBjoern A. Zeeb return num_of_fifos; 1788bfcc09ddSBjoern A. Zeeb } 1789bfcc09ddSBjoern A. Zeeb 1790bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_single_range(struct iwl_fw_runtime *fwrt, 1791bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1792bfcc09ddSBjoern A. Zeeb { 1793bfcc09ddSBjoern A. Zeeb return 1; 1794bfcc09ddSBjoern A. Zeeb } 1795bfcc09ddSBjoern A. Zeeb 1796bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt, 1797bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1798bfcc09ddSBjoern A. Zeeb { 1799bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1800bfcc09ddSBjoern A. Zeeb u32 size = le32_to_cpu(reg->dev_addr.size); 1801bfcc09ddSBjoern A. Zeeb u32 ranges = iwl_dump_ini_mem_ranges(fwrt, reg_data); 1802bfcc09ddSBjoern A. Zeeb 1803bfcc09ddSBjoern A. Zeeb if (!size || !ranges) 1804bfcc09ddSBjoern A. Zeeb return 0; 1805bfcc09ddSBjoern A. Zeeb 1806bfcc09ddSBjoern A. Zeeb return sizeof(struct iwl_fw_ini_error_dump) + ranges * 1807bfcc09ddSBjoern A. Zeeb (size + sizeof(struct iwl_fw_ini_error_dump_range)); 1808bfcc09ddSBjoern A. Zeeb } 1809bfcc09ddSBjoern A. Zeeb 1810bfcc09ddSBjoern A. Zeeb static u32 1811bfcc09ddSBjoern A. Zeeb iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt, 1812bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1813bfcc09ddSBjoern A. Zeeb { 1814bfcc09ddSBjoern A. Zeeb int i; 1815bfcc09ddSBjoern A. Zeeb u32 range_header_len = sizeof(struct iwl_fw_ini_error_dump_range); 1816bfcc09ddSBjoern A. Zeeb u32 size = sizeof(struct iwl_fw_ini_error_dump); 1817bfcc09ddSBjoern A. Zeeb 1818bfcc09ddSBjoern A. Zeeb /* start from 1 to skip CSS section */ 1819bfcc09ddSBjoern A. Zeeb for (i = 1; i <= iwl_dump_ini_paging_ranges(fwrt, reg_data); i++) { 1820bfcc09ddSBjoern A. Zeeb size += range_header_len; 1821bfcc09ddSBjoern A. Zeeb if (fwrt->trans->trans_cfg->gen2) 1822bfcc09ddSBjoern A. Zeeb size += fwrt->trans->init_dram.paging[i].size; 1823bfcc09ddSBjoern A. Zeeb else 1824bfcc09ddSBjoern A. Zeeb size += fwrt->fw_paging_db[i].fw_paging_size; 1825bfcc09ddSBjoern A. Zeeb } 1826bfcc09ddSBjoern A. Zeeb 1827bfcc09ddSBjoern A. Zeeb return size; 1828bfcc09ddSBjoern A. Zeeb } 1829bfcc09ddSBjoern A. Zeeb 1830bfcc09ddSBjoern A. Zeeb static u32 1831bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mon_dram_get_size(struct iwl_fw_runtime *fwrt, 1832bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1833bfcc09ddSBjoern A. Zeeb { 1834bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1835bfcc09ddSBjoern A. Zeeb struct iwl_fw_mon *fw_mon; 1836bfcc09ddSBjoern A. Zeeb u32 size = 0, alloc_id = le32_to_cpu(reg->dram_alloc_id); 1837bfcc09ddSBjoern A. Zeeb int i; 1838bfcc09ddSBjoern A. Zeeb 1839bfcc09ddSBjoern A. Zeeb fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id]; 1840bfcc09ddSBjoern A. Zeeb 1841bfcc09ddSBjoern A. Zeeb for (i = 0; i < fw_mon->num_frags; i++) { 1842bfcc09ddSBjoern A. Zeeb struct iwl_dram_data *frag = &fw_mon->frags[i]; 1843bfcc09ddSBjoern A. Zeeb 1844bfcc09ddSBjoern A. Zeeb if (!frag->size) 1845bfcc09ddSBjoern A. Zeeb break; 1846bfcc09ddSBjoern A. Zeeb 1847bfcc09ddSBjoern A. Zeeb size += sizeof(struct iwl_fw_ini_error_dump_range) + frag->size; 1848bfcc09ddSBjoern A. Zeeb } 1849bfcc09ddSBjoern A. Zeeb 1850bfcc09ddSBjoern A. Zeeb if (size) 1851bfcc09ddSBjoern A. Zeeb size += sizeof(struct iwl_fw_ini_monitor_dump); 1852bfcc09ddSBjoern A. Zeeb 1853bfcc09ddSBjoern A. Zeeb return size; 1854bfcc09ddSBjoern A. Zeeb } 1855bfcc09ddSBjoern A. Zeeb 1856bfcc09ddSBjoern A. Zeeb static u32 1857bfcc09ddSBjoern A. Zeeb iwl_dump_ini_mon_smem_get_size(struct iwl_fw_runtime *fwrt, 1858bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1859bfcc09ddSBjoern A. Zeeb { 1860bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1861bfcc09ddSBjoern A. Zeeb u32 size; 1862bfcc09ddSBjoern A. Zeeb 1863bfcc09ddSBjoern A. Zeeb size = le32_to_cpu(reg->internal_buffer.size); 1864bfcc09ddSBjoern A. Zeeb if (!size) 1865bfcc09ddSBjoern A. Zeeb return 0; 1866bfcc09ddSBjoern A. Zeeb 1867bfcc09ddSBjoern A. Zeeb size += sizeof(struct iwl_fw_ini_monitor_dump) + 1868bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_ini_error_dump_range); 1869bfcc09ddSBjoern A. Zeeb 1870bfcc09ddSBjoern A. Zeeb return size; 1871bfcc09ddSBjoern A. Zeeb } 1872bfcc09ddSBjoern A. Zeeb 1873bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_txf_get_size(struct iwl_fw_runtime *fwrt, 1874bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1875bfcc09ddSBjoern A. Zeeb { 1876bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1877bfcc09ddSBjoern A. Zeeb struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data; 1878bfcc09ddSBjoern A. Zeeb u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs); 1879bfcc09ddSBjoern A. Zeeb u32 size = 0; 1880bfcc09ddSBjoern A. Zeeb u32 fifo_hdr = sizeof(struct iwl_fw_ini_error_dump_range) + 1881bfcc09ddSBjoern A. Zeeb registers_num * 1882bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_ini_error_dump_register); 1883bfcc09ddSBjoern A. Zeeb 1884bfcc09ddSBjoern A. Zeeb while (iwl_ini_txf_iter(fwrt, reg_data, size)) { 1885bfcc09ddSBjoern A. Zeeb size += fifo_hdr; 1886bfcc09ddSBjoern A. Zeeb if (!reg->fifos.hdr_only) 1887bfcc09ddSBjoern A. Zeeb size += iter->fifo_size; 1888bfcc09ddSBjoern A. Zeeb } 1889bfcc09ddSBjoern A. Zeeb 1890bfcc09ddSBjoern A. Zeeb if (!size) 1891bfcc09ddSBjoern A. Zeeb return 0; 1892bfcc09ddSBjoern A. Zeeb 1893bfcc09ddSBjoern A. Zeeb return size + sizeof(struct iwl_fw_ini_error_dump); 1894bfcc09ddSBjoern A. Zeeb } 1895bfcc09ddSBjoern A. Zeeb 1896bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_rxf_get_size(struct iwl_fw_runtime *fwrt, 1897bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1898bfcc09ddSBjoern A. Zeeb { 1899bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1900bfcc09ddSBjoern A. Zeeb struct iwl_ini_rxf_data rx_data; 1901bfcc09ddSBjoern A. Zeeb u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs); 1902bfcc09ddSBjoern A. Zeeb u32 size = sizeof(struct iwl_fw_ini_error_dump) + 1903bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_ini_error_dump_range) + 1904bfcc09ddSBjoern A. Zeeb registers_num * sizeof(struct iwl_fw_ini_error_dump_register); 1905bfcc09ddSBjoern A. Zeeb 1906bfcc09ddSBjoern A. Zeeb if (reg->fifos.hdr_only) 1907bfcc09ddSBjoern A. Zeeb return size; 1908bfcc09ddSBjoern A. Zeeb 1909bfcc09ddSBjoern A. Zeeb iwl_ini_get_rxf_data(fwrt, reg_data, &rx_data); 1910bfcc09ddSBjoern A. Zeeb size += rx_data.size; 1911bfcc09ddSBjoern A. Zeeb 1912bfcc09ddSBjoern A. Zeeb return size; 1913bfcc09ddSBjoern A. Zeeb } 1914bfcc09ddSBjoern A. Zeeb 1915bfcc09ddSBjoern A. Zeeb static u32 1916bfcc09ddSBjoern A. Zeeb iwl_dump_ini_err_table_get_size(struct iwl_fw_runtime *fwrt, 1917bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1918bfcc09ddSBjoern A. Zeeb { 1919bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1920bfcc09ddSBjoern A. Zeeb u32 size = le32_to_cpu(reg->err_table.size); 1921bfcc09ddSBjoern A. Zeeb 1922bfcc09ddSBjoern A. Zeeb if (size) 1923bfcc09ddSBjoern A. Zeeb size += sizeof(struct iwl_fw_ini_err_table_dump) + 1924bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_ini_error_dump_range); 1925bfcc09ddSBjoern A. Zeeb 1926bfcc09ddSBjoern A. Zeeb return size; 1927bfcc09ddSBjoern A. Zeeb } 1928bfcc09ddSBjoern A. Zeeb 1929bfcc09ddSBjoern A. Zeeb static u32 1930bfcc09ddSBjoern A. Zeeb iwl_dump_ini_special_mem_get_size(struct iwl_fw_runtime *fwrt, 1931bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1932bfcc09ddSBjoern A. Zeeb { 1933bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1934bfcc09ddSBjoern A. Zeeb u32 size = le32_to_cpu(reg->special_mem.size); 1935bfcc09ddSBjoern A. Zeeb 1936bfcc09ddSBjoern A. Zeeb if (size) 1937bfcc09ddSBjoern A. Zeeb size += sizeof(struct iwl_fw_ini_special_device_memory) + 1938bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_ini_error_dump_range); 1939bfcc09ddSBjoern A. Zeeb 1940bfcc09ddSBjoern A. Zeeb return size; 1941bfcc09ddSBjoern A. Zeeb } 1942bfcc09ddSBjoern A. Zeeb 1943bfcc09ddSBjoern A. Zeeb static u32 1944bfcc09ddSBjoern A. Zeeb iwl_dump_ini_fw_pkt_get_size(struct iwl_fw_runtime *fwrt, 1945bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data) 1946bfcc09ddSBjoern A. Zeeb { 1947bfcc09ddSBjoern A. Zeeb u32 size = 0; 1948bfcc09ddSBjoern A. Zeeb 1949bfcc09ddSBjoern A. Zeeb if (!reg_data->dump_data->fw_pkt) 1950bfcc09ddSBjoern A. Zeeb return 0; 1951bfcc09ddSBjoern A. Zeeb 1952bfcc09ddSBjoern A. Zeeb size += iwl_rx_packet_payload_len(reg_data->dump_data->fw_pkt); 1953bfcc09ddSBjoern A. Zeeb if (size) 1954bfcc09ddSBjoern A. Zeeb size += sizeof(struct iwl_fw_ini_error_dump) + 1955bfcc09ddSBjoern A. Zeeb sizeof(struct iwl_fw_ini_error_dump_range); 1956bfcc09ddSBjoern A. Zeeb 1957bfcc09ddSBjoern A. Zeeb return size; 1958bfcc09ddSBjoern A. Zeeb } 1959bfcc09ddSBjoern A. Zeeb 1960bfcc09ddSBjoern A. Zeeb /** 1961bfcc09ddSBjoern A. Zeeb * struct iwl_dump_ini_mem_ops - ini memory dump operations 1962bfcc09ddSBjoern A. Zeeb * @get_num_of_ranges: returns the number of memory ranges in the region. 1963bfcc09ddSBjoern A. Zeeb * @get_size: returns the total size of the region. 1964bfcc09ddSBjoern A. Zeeb * @fill_mem_hdr: fills region type specific headers and returns pointer to 1965bfcc09ddSBjoern A. Zeeb * the first range or NULL if failed to fill headers. 1966bfcc09ddSBjoern A. Zeeb * @fill_range: copies a given memory range into the dump. 1967bfcc09ddSBjoern A. Zeeb * Returns the size of the range or negative error value otherwise. 1968bfcc09ddSBjoern A. Zeeb */ 1969bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_mem_ops { 1970bfcc09ddSBjoern A. Zeeb u32 (*get_num_of_ranges)(struct iwl_fw_runtime *fwrt, 1971bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data); 1972bfcc09ddSBjoern A. Zeeb u32 (*get_size)(struct iwl_fw_runtime *fwrt, 1973bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data); 1974bfcc09ddSBjoern A. Zeeb void *(*fill_mem_hdr)(struct iwl_fw_runtime *fwrt, 1975bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1976bfcc09ddSBjoern A. Zeeb void *data); 1977bfcc09ddSBjoern A. Zeeb int (*fill_range)(struct iwl_fw_runtime *fwrt, 1978bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1979bfcc09ddSBjoern A. Zeeb void *range, int idx); 1980bfcc09ddSBjoern A. Zeeb }; 1981bfcc09ddSBjoern A. Zeeb 1982bfcc09ddSBjoern A. Zeeb /** 1983bfcc09ddSBjoern A. Zeeb * iwl_dump_ini_mem 1984bfcc09ddSBjoern A. Zeeb * 1985bfcc09ddSBjoern A. Zeeb * Creates a dump tlv and copy a memory region into it. 1986bfcc09ddSBjoern A. Zeeb * Returns the size of the current dump tlv or 0 if failed 1987bfcc09ddSBjoern A. Zeeb * 1988bfcc09ddSBjoern A. Zeeb * @fwrt: fw runtime struct 1989bfcc09ddSBjoern A. Zeeb * @list: list to add the dump tlv to 1990bfcc09ddSBjoern A. Zeeb * @reg_data: memory region 1991bfcc09ddSBjoern A. Zeeb * @ops: memory dump operations 1992bfcc09ddSBjoern A. Zeeb */ 1993bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, 1994bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data *reg_data, 1995bfcc09ddSBjoern A. Zeeb const struct iwl_dump_ini_mem_ops *ops) 1996bfcc09ddSBjoern A. Zeeb { 1997bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data; 1998bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_entry *entry; 1999bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data *tlv; 2000bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_error_dump_header *header; 2001bfcc09ddSBjoern A. Zeeb u32 type = le32_to_cpu(reg->type), id = le32_to_cpu(reg->id); 2002bfcc09ddSBjoern A. Zeeb u32 num_of_ranges, i, size; 2003bfcc09ddSBjoern A. Zeeb void *range; 2004bfcc09ddSBjoern A. Zeeb 2005bfcc09ddSBjoern A. Zeeb /* 2006bfcc09ddSBjoern A. Zeeb * The higher part of the ID in version 2 is irrelevant for 2007bfcc09ddSBjoern A. Zeeb * us, so mask it out. 2008bfcc09ddSBjoern A. Zeeb */ 2009bfcc09ddSBjoern A. Zeeb if (le32_to_cpu(reg->hdr.version) == 2) 2010bfcc09ddSBjoern A. Zeeb id &= IWL_FW_INI_REGION_V2_MASK; 2011bfcc09ddSBjoern A. Zeeb 2012bfcc09ddSBjoern A. Zeeb if (!ops->get_num_of_ranges || !ops->get_size || !ops->fill_mem_hdr || 2013bfcc09ddSBjoern A. Zeeb !ops->fill_range) 2014bfcc09ddSBjoern A. Zeeb return 0; 2015bfcc09ddSBjoern A. Zeeb 2016bfcc09ddSBjoern A. Zeeb size = ops->get_size(fwrt, reg_data); 2017bfcc09ddSBjoern A. Zeeb if (!size) 2018bfcc09ddSBjoern A. Zeeb return 0; 2019bfcc09ddSBjoern A. Zeeb 2020bfcc09ddSBjoern A. Zeeb entry = vzalloc(sizeof(*entry) + sizeof(*tlv) + size); 2021bfcc09ddSBjoern A. Zeeb if (!entry) 2022bfcc09ddSBjoern A. Zeeb return 0; 2023bfcc09ddSBjoern A. Zeeb 2024bfcc09ddSBjoern A. Zeeb entry->size = sizeof(*tlv) + size; 2025bfcc09ddSBjoern A. Zeeb 2026bfcc09ddSBjoern A. Zeeb tlv = (void *)entry->data; 2027bfcc09ddSBjoern A. Zeeb tlv->type = reg->type; 2028bfcc09ddSBjoern A. Zeeb tlv->len = cpu_to_le32(size); 2029bfcc09ddSBjoern A. Zeeb 2030bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(fwrt, "WRT: Collecting region: id=%d, type=%d\n", id, 2031bfcc09ddSBjoern A. Zeeb type); 2032bfcc09ddSBjoern A. Zeeb 2033bfcc09ddSBjoern A. Zeeb num_of_ranges = ops->get_num_of_ranges(fwrt, reg_data); 2034bfcc09ddSBjoern A. Zeeb 2035bfcc09ddSBjoern A. Zeeb header = (void *)tlv->data; 2036bfcc09ddSBjoern A. Zeeb header->region_id = cpu_to_le32(id); 2037bfcc09ddSBjoern A. Zeeb header->num_of_ranges = cpu_to_le32(num_of_ranges); 2038bfcc09ddSBjoern A. Zeeb header->name_len = cpu_to_le32(IWL_FW_INI_MAX_NAME); 2039bfcc09ddSBjoern A. Zeeb memcpy(header->name, reg->name, IWL_FW_INI_MAX_NAME); 2040bfcc09ddSBjoern A. Zeeb 2041bfcc09ddSBjoern A. Zeeb range = ops->fill_mem_hdr(fwrt, reg_data, header); 2042bfcc09ddSBjoern A. Zeeb if (!range) { 2043bfcc09ddSBjoern A. Zeeb IWL_ERR(fwrt, 2044bfcc09ddSBjoern A. Zeeb "WRT: Failed to fill region header: id=%d, type=%d\n", 2045bfcc09ddSBjoern A. Zeeb id, type); 2046bfcc09ddSBjoern A. Zeeb goto out_err; 2047bfcc09ddSBjoern A. Zeeb } 2048bfcc09ddSBjoern A. Zeeb 2049bfcc09ddSBjoern A. Zeeb for (i = 0; i < num_of_ranges; i++) { 2050bfcc09ddSBjoern A. Zeeb int range_size = ops->fill_range(fwrt, reg_data, range, i); 2051bfcc09ddSBjoern A. Zeeb 2052bfcc09ddSBjoern A. Zeeb if (range_size < 0) { 2053bfcc09ddSBjoern A. Zeeb IWL_ERR(fwrt, 2054bfcc09ddSBjoern A. Zeeb "WRT: Failed to dump region: id=%d, type=%d\n", 2055bfcc09ddSBjoern A. Zeeb id, type); 2056bfcc09ddSBjoern A. Zeeb goto out_err; 2057bfcc09ddSBjoern A. Zeeb } 2058bfcc09ddSBjoern A. Zeeb range = (u8 *)range + range_size; 2059bfcc09ddSBjoern A. Zeeb } 2060bfcc09ddSBjoern A. Zeeb 2061bfcc09ddSBjoern A. Zeeb list_add_tail(&entry->list, list); 2062bfcc09ddSBjoern A. Zeeb 2063bfcc09ddSBjoern A. Zeeb return entry->size; 2064bfcc09ddSBjoern A. Zeeb 2065bfcc09ddSBjoern A. Zeeb out_err: 2066bfcc09ddSBjoern A. Zeeb vfree(entry); 2067bfcc09ddSBjoern A. Zeeb 2068bfcc09ddSBjoern A. Zeeb return 0; 2069bfcc09ddSBjoern A. Zeeb } 2070bfcc09ddSBjoern A. Zeeb 2071bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, 2072bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_trigger_tlv *trigger, 2073bfcc09ddSBjoern A. Zeeb struct list_head *list) 2074bfcc09ddSBjoern A. Zeeb { 2075bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_entry *entry; 2076bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_data *tlv; 2077bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_info *dump; 2078bfcc09ddSBjoern A. Zeeb struct iwl_dbg_tlv_node *node; 2079bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_cfg_name *cfg_name; 2080bfcc09ddSBjoern A. Zeeb u32 size = sizeof(*tlv) + sizeof(*dump); 2081bfcc09ddSBjoern A. Zeeb u32 num_of_cfg_names = 0; 2082bfcc09ddSBjoern A. Zeeb u32 hw_type; 2083bfcc09ddSBjoern A. Zeeb 2084bfcc09ddSBjoern A. Zeeb list_for_each_entry(node, &fwrt->trans->dbg.debug_info_tlv_list, list) { 2085bfcc09ddSBjoern A. Zeeb size += sizeof(*cfg_name); 2086bfcc09ddSBjoern A. Zeeb num_of_cfg_names++; 2087bfcc09ddSBjoern A. Zeeb } 2088bfcc09ddSBjoern A. Zeeb 2089bfcc09ddSBjoern A. Zeeb entry = vzalloc(sizeof(*entry) + size); 2090bfcc09ddSBjoern A. Zeeb if (!entry) 2091bfcc09ddSBjoern A. Zeeb return 0; 2092bfcc09ddSBjoern A. Zeeb 2093bfcc09ddSBjoern A. Zeeb entry->size = size; 2094bfcc09ddSBjoern A. Zeeb 2095bfcc09ddSBjoern A. Zeeb tlv = (void *)entry->data; 2096bfcc09ddSBjoern A. Zeeb tlv->type = cpu_to_le32(IWL_INI_DUMP_INFO_TYPE); 2097bfcc09ddSBjoern A. Zeeb tlv->len = cpu_to_le32(size - sizeof(*tlv)); 2098bfcc09ddSBjoern A. Zeeb 2099bfcc09ddSBjoern A. Zeeb dump = (void *)tlv->data; 2100bfcc09ddSBjoern A. Zeeb 2101bfcc09ddSBjoern A. Zeeb dump->version = cpu_to_le32(IWL_INI_DUMP_VER); 2102bfcc09ddSBjoern A. Zeeb dump->time_point = trigger->time_point; 2103bfcc09ddSBjoern A. Zeeb dump->trigger_reason = trigger->trigger_reason; 2104bfcc09ddSBjoern A. Zeeb dump->external_cfg_state = 2105bfcc09ddSBjoern A. Zeeb cpu_to_le32(fwrt->trans->dbg.external_ini_cfg); 2106bfcc09ddSBjoern A. Zeeb 2107bfcc09ddSBjoern A. Zeeb dump->ver_type = cpu_to_le32(fwrt->dump.fw_ver.type); 2108bfcc09ddSBjoern A. Zeeb dump->ver_subtype = cpu_to_le32(fwrt->dump.fw_ver.subtype); 2109bfcc09ddSBjoern A. Zeeb 2110bfcc09ddSBjoern A. Zeeb dump->hw_step = cpu_to_le32(CSR_HW_REV_STEP(fwrt->trans->hw_rev)); 2111bfcc09ddSBjoern A. Zeeb 2112bfcc09ddSBjoern A. Zeeb /* 2113bfcc09ddSBjoern A. Zeeb * Several HWs all have type == 0x42, so we'll override this value 2114bfcc09ddSBjoern A. Zeeb * according to the detected HW 2115bfcc09ddSBjoern A. Zeeb */ 2116bfcc09ddSBjoern A. Zeeb hw_type = CSR_HW_REV_TYPE(fwrt->trans->hw_rev); 2117bfcc09ddSBjoern A. Zeeb if (hw_type == IWL_AX210_HW_TYPE) { 2118bfcc09ddSBjoern A. Zeeb u32 prph_val = iwl_read_prph(fwrt->trans, WFPM_OTP_CFG1_ADDR_GEN2); 2119bfcc09ddSBjoern A. Zeeb u32 is_jacket = !!(prph_val & WFPM_OTP_CFG1_IS_JACKET_BIT); 2120bfcc09ddSBjoern A. Zeeb u32 is_cdb = !!(prph_val & WFPM_OTP_CFG1_IS_CDB_BIT); 2121bfcc09ddSBjoern A. Zeeb u32 masked_bits = is_jacket | (is_cdb << 1); 2122bfcc09ddSBjoern A. Zeeb 2123bfcc09ddSBjoern A. Zeeb /* 2124bfcc09ddSBjoern A. Zeeb * The HW type depends on certain bits in this case, so add 2125bfcc09ddSBjoern A. Zeeb * these bits to the HW type. We won't have collisions since we 2126bfcc09ddSBjoern A. Zeeb * add these bits after the highest possible bit in the mask. 2127bfcc09ddSBjoern A. Zeeb */ 2128bfcc09ddSBjoern A. Zeeb hw_type |= masked_bits << IWL_AX210_HW_TYPE_ADDITION_SHIFT; 2129bfcc09ddSBjoern A. Zeeb } 2130bfcc09ddSBjoern A. Zeeb dump->hw_type = cpu_to_le32(hw_type); 2131bfcc09ddSBjoern A. Zeeb 2132bfcc09ddSBjoern A. Zeeb dump->rf_id_flavor = 2133bfcc09ddSBjoern A. Zeeb cpu_to_le32(CSR_HW_RFID_FLAVOR(fwrt->trans->hw_rf_id)); 2134bfcc09ddSBjoern A. Zeeb dump->rf_id_dash = cpu_to_le32(CSR_HW_RFID_DASH(fwrt->trans->hw_rf_id)); 2135bfcc09ddSBjoern A. Zeeb dump->rf_id_step = cpu_to_le32(CSR_HW_RFID_STEP(fwrt->trans->hw_rf_id)); 2136bfcc09ddSBjoern A. Zeeb dump->rf_id_type = cpu_to_le32(CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)); 2137bfcc09ddSBjoern A. Zeeb 2138bfcc09ddSBjoern A. Zeeb dump->lmac_major = cpu_to_le32(fwrt->dump.fw_ver.lmac_major); 2139bfcc09ddSBjoern A. Zeeb dump->lmac_minor = cpu_to_le32(fwrt->dump.fw_ver.lmac_minor); 2140bfcc09ddSBjoern A. Zeeb dump->umac_major = cpu_to_le32(fwrt->dump.fw_ver.umac_major); 2141bfcc09ddSBjoern A. Zeeb dump->umac_minor = cpu_to_le32(fwrt->dump.fw_ver.umac_minor); 2142bfcc09ddSBjoern A. Zeeb 2143bfcc09ddSBjoern A. Zeeb dump->fw_mon_mode = cpu_to_le32(fwrt->trans->dbg.ini_dest); 2144bfcc09ddSBjoern A. Zeeb dump->regions_mask = trigger->regions_mask & 2145bfcc09ddSBjoern A. Zeeb ~cpu_to_le64(fwrt->trans->dbg.unsupported_region_msk); 2146bfcc09ddSBjoern A. Zeeb 2147bfcc09ddSBjoern A. Zeeb dump->build_tag_len = cpu_to_le32(sizeof(dump->build_tag)); 2148bfcc09ddSBjoern A. Zeeb memcpy(dump->build_tag, fwrt->fw->human_readable, 2149bfcc09ddSBjoern A. Zeeb sizeof(dump->build_tag)); 2150bfcc09ddSBjoern A. Zeeb 2151bfcc09ddSBjoern A. Zeeb cfg_name = dump->cfg_names; 2152bfcc09ddSBjoern A. Zeeb dump->num_of_cfg_names = cpu_to_le32(num_of_cfg_names); 2153bfcc09ddSBjoern A. Zeeb list_for_each_entry(node, &fwrt->trans->dbg.debug_info_tlv_list, list) { 2154bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_debug_info_tlv *debug_info = 2155bfcc09ddSBjoern A. Zeeb (void *)node->tlv.data; 2156bfcc09ddSBjoern A. Zeeb 2157bfcc09ddSBjoern A. Zeeb cfg_name->image_type = debug_info->image_type; 2158bfcc09ddSBjoern A. Zeeb cfg_name->cfg_name_len = 2159bfcc09ddSBjoern A. Zeeb cpu_to_le32(IWL_FW_INI_MAX_CFG_NAME); 2160bfcc09ddSBjoern A. Zeeb memcpy(cfg_name->cfg_name, debug_info->debug_cfg_name, 2161bfcc09ddSBjoern A. Zeeb sizeof(cfg_name->cfg_name)); 2162bfcc09ddSBjoern A. Zeeb cfg_name++; 2163bfcc09ddSBjoern A. Zeeb } 2164bfcc09ddSBjoern A. Zeeb 2165bfcc09ddSBjoern A. Zeeb /* add dump info TLV to the beginning of the list since it needs to be 2166bfcc09ddSBjoern A. Zeeb * the first TLV in the dump 2167bfcc09ddSBjoern A. Zeeb */ 2168bfcc09ddSBjoern A. Zeeb list_add(&entry->list, list); 2169bfcc09ddSBjoern A. Zeeb 2170bfcc09ddSBjoern A. Zeeb return entry->size; 2171bfcc09ddSBjoern A. Zeeb } 2172bfcc09ddSBjoern A. Zeeb 2173bfcc09ddSBjoern A. Zeeb static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { 2174bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_INVALID] = {}, 2175bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_INTERNAL_BUFFER] = { 2176bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_single_range, 2177bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mon_smem_get_size, 2178bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mon_smem_fill_header, 2179bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_mon_smem_iter, 2180bfcc09ddSBjoern A. Zeeb }, 2181bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_DRAM_BUFFER] = { 2182bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_mon_dram_ranges, 2183bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mon_dram_get_size, 2184bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mon_dram_fill_header, 2185bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_mon_dram_iter, 2186bfcc09ddSBjoern A. Zeeb }, 2187bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_TXF] = { 2188bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_txf_ranges, 2189bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_txf_get_size, 2190bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2191bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_txf_iter, 2192bfcc09ddSBjoern A. Zeeb }, 2193bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_RXF] = { 2194bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_single_range, 2195bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_rxf_get_size, 2196bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2197bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_rxf_iter, 2198bfcc09ddSBjoern A. Zeeb }, 2199bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_LMAC_ERROR_TABLE] = { 2200bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_single_range, 2201bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_err_table_get_size, 2202bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_err_table_fill_header, 2203bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_err_table_iter, 2204bfcc09ddSBjoern A. Zeeb }, 2205bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_UMAC_ERROR_TABLE] = { 2206bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_single_range, 2207bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_err_table_get_size, 2208bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_err_table_fill_header, 2209bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_err_table_iter, 2210bfcc09ddSBjoern A. Zeeb }, 2211bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_RSP_OR_NOTIF] = { 2212bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_single_range, 2213bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_fw_pkt_get_size, 2214bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2215bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_fw_pkt_iter, 2216bfcc09ddSBjoern A. Zeeb }, 2217bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_DEVICE_MEMORY] = { 2218bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_mem_ranges, 2219bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mem_get_size, 2220bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2221bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_dev_mem_iter, 2222bfcc09ddSBjoern A. Zeeb }, 2223bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_PERIPHERY_MAC] = { 2224bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_mem_ranges, 2225bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mem_get_size, 2226bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2227bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_prph_mac_iter, 2228bfcc09ddSBjoern A. Zeeb }, 2229bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_PERIPHERY_PHY] = { 2230bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_mem_ranges, 2231bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mem_get_size, 2232bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2233bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_prph_phy_iter, 2234bfcc09ddSBjoern A. Zeeb }, 2235bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_PERIPHERY_AUX] = {}, 2236bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_PAGING] = { 2237bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2238bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_paging_ranges, 2239bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_paging_get_size, 2240bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_paging_iter, 2241bfcc09ddSBjoern A. Zeeb }, 2242bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_CSR] = { 2243bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_mem_ranges, 2244bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mem_get_size, 2245bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2246bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_csr_iter, 2247bfcc09ddSBjoern A. Zeeb }, 2248bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_DRAM_IMR] = {}, 2249bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_PCI_IOSF_CONFIG] = { 2250bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_mem_ranges, 2251bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mem_get_size, 2252bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2253bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_config_iter, 2254bfcc09ddSBjoern A. Zeeb }, 2255bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_SPECIAL_DEVICE_MEMORY] = { 2256bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_single_range, 2257bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_special_mem_get_size, 2258bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_special_mem_fill_header, 2259bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_special_mem_iter, 2260bfcc09ddSBjoern A. Zeeb }, 2261bfcc09ddSBjoern A. Zeeb [IWL_FW_INI_REGION_DBGI_SRAM] = { 2262bfcc09ddSBjoern A. Zeeb .get_num_of_ranges = iwl_dump_ini_mem_ranges, 2263bfcc09ddSBjoern A. Zeeb .get_size = iwl_dump_ini_mem_get_size, 2264bfcc09ddSBjoern A. Zeeb .fill_mem_hdr = iwl_dump_ini_mem_fill_header, 2265bfcc09ddSBjoern A. Zeeb .fill_range = iwl_dump_ini_dbgi_sram_iter, 2266bfcc09ddSBjoern A. Zeeb }, 2267bfcc09ddSBjoern A. Zeeb }; 2268bfcc09ddSBjoern A. Zeeb 2269bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, 2270bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *dump_data, 2271bfcc09ddSBjoern A. Zeeb struct list_head *list) 2272bfcc09ddSBjoern A. Zeeb { 2273bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig; 2274bfcc09ddSBjoern A. Zeeb enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point); 2275bfcc09ddSBjoern A. Zeeb struct iwl_dump_ini_region_data reg_data = { 2276bfcc09ddSBjoern A. Zeeb .dump_data = dump_data, 2277bfcc09ddSBjoern A. Zeeb }; 2278bfcc09ddSBjoern A. Zeeb int i; 2279bfcc09ddSBjoern A. Zeeb u32 size = 0; 2280bfcc09ddSBjoern A. Zeeb u64 regions_mask = le64_to_cpu(trigger->regions_mask) & 2281bfcc09ddSBjoern A. Zeeb ~(fwrt->trans->dbg.unsupported_region_msk); 2282bfcc09ddSBjoern A. Zeeb 2283bfcc09ddSBjoern A. Zeeb BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask)); 2284bfcc09ddSBjoern A. Zeeb BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) < 2285bfcc09ddSBjoern A. Zeeb ARRAY_SIZE(fwrt->trans->dbg.active_regions)); 2286bfcc09ddSBjoern A. Zeeb 2287bfcc09ddSBjoern A. Zeeb for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) { 2288bfcc09ddSBjoern A. Zeeb u32 reg_type; 2289bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_region_tlv *reg; 2290bfcc09ddSBjoern A. Zeeb 2291bfcc09ddSBjoern A. Zeeb if (!(BIT_ULL(i) & regions_mask)) 2292bfcc09ddSBjoern A. Zeeb continue; 2293bfcc09ddSBjoern A. Zeeb 2294bfcc09ddSBjoern A. Zeeb reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i]; 2295bfcc09ddSBjoern A. Zeeb if (!reg_data.reg_tlv) { 2296bfcc09ddSBjoern A. Zeeb IWL_WARN(fwrt, 2297bfcc09ddSBjoern A. Zeeb "WRT: Unassigned region id %d, skipping\n", i); 2298bfcc09ddSBjoern A. Zeeb continue; 2299bfcc09ddSBjoern A. Zeeb } 2300bfcc09ddSBjoern A. Zeeb 2301bfcc09ddSBjoern A. Zeeb reg = (void *)reg_data.reg_tlv->data; 2302bfcc09ddSBjoern A. Zeeb reg_type = le32_to_cpu(reg->type); 2303bfcc09ddSBjoern A. Zeeb if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops)) 2304bfcc09ddSBjoern A. Zeeb continue; 2305bfcc09ddSBjoern A. Zeeb 2306bfcc09ddSBjoern A. Zeeb if (reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY && 2307bfcc09ddSBjoern A. Zeeb tp_id != IWL_FW_INI_TIME_POINT_FW_ASSERT) { 2308bfcc09ddSBjoern A. Zeeb IWL_WARN(fwrt, 2309bfcc09ddSBjoern A. Zeeb "WRT: trying to collect phy prph at time point: %d, skipping\n", 2310bfcc09ddSBjoern A. Zeeb tp_id); 2311bfcc09ddSBjoern A. Zeeb continue; 2312bfcc09ddSBjoern A. Zeeb } 2313bfcc09ddSBjoern A. Zeeb 2314bfcc09ddSBjoern A. Zeeb size += iwl_dump_ini_mem(fwrt, list, ®_data, 2315bfcc09ddSBjoern A. Zeeb &iwl_dump_ini_region_ops[reg_type]); 2316bfcc09ddSBjoern A. Zeeb } 2317bfcc09ddSBjoern A. Zeeb 2318bfcc09ddSBjoern A. Zeeb if (size) 2319bfcc09ddSBjoern A. Zeeb size += iwl_dump_ini_info(fwrt, trigger, list); 2320bfcc09ddSBjoern A. Zeeb 2321bfcc09ddSBjoern A. Zeeb return size; 2322bfcc09ddSBjoern A. Zeeb } 2323bfcc09ddSBjoern A. Zeeb 2324bfcc09ddSBjoern A. Zeeb static bool iwl_fw_ini_trigger_on(struct iwl_fw_runtime *fwrt, 2325bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_trigger_tlv *trig) 2326bfcc09ddSBjoern A. Zeeb { 2327bfcc09ddSBjoern A. Zeeb enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trig->time_point); 2328bfcc09ddSBjoern A. Zeeb u32 usec = le32_to_cpu(trig->ignore_consec); 2329bfcc09ddSBjoern A. Zeeb 2330bfcc09ddSBjoern A. Zeeb if (!iwl_trans_dbg_ini_valid(fwrt->trans) || 2331bfcc09ddSBjoern A. Zeeb tp_id == IWL_FW_INI_TIME_POINT_INVALID || 2332bfcc09ddSBjoern A. Zeeb tp_id >= IWL_FW_INI_TIME_POINT_NUM || 2333bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_no_trig_window(fwrt, tp_id, usec)) 2334bfcc09ddSBjoern A. Zeeb return false; 2335bfcc09ddSBjoern A. Zeeb 2336bfcc09ddSBjoern A. Zeeb return true; 2337bfcc09ddSBjoern A. Zeeb } 2338bfcc09ddSBjoern A. Zeeb 2339bfcc09ddSBjoern A. Zeeb static u32 iwl_dump_ini_file_gen(struct iwl_fw_runtime *fwrt, 2340bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *dump_data, 2341bfcc09ddSBjoern A. Zeeb struct list_head *list) 2342bfcc09ddSBjoern A. Zeeb { 2343bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig; 2344bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_entry *entry; 2345bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_file_hdr *hdr; 2346bfcc09ddSBjoern A. Zeeb u32 size; 2347bfcc09ddSBjoern A. Zeeb 2348bfcc09ddSBjoern A. Zeeb if (!trigger || !iwl_fw_ini_trigger_on(fwrt, trigger) || 2349bfcc09ddSBjoern A. Zeeb !le64_to_cpu(trigger->regions_mask)) 2350bfcc09ddSBjoern A. Zeeb return 0; 2351bfcc09ddSBjoern A. Zeeb 2352bfcc09ddSBjoern A. Zeeb entry = vzalloc(sizeof(*entry) + sizeof(*hdr)); 2353bfcc09ddSBjoern A. Zeeb if (!entry) 2354bfcc09ddSBjoern A. Zeeb return 0; 2355bfcc09ddSBjoern A. Zeeb 2356bfcc09ddSBjoern A. Zeeb entry->size = sizeof(*hdr); 2357bfcc09ddSBjoern A. Zeeb 2358bfcc09ddSBjoern A. Zeeb size = iwl_dump_ini_trigger(fwrt, dump_data, list); 2359bfcc09ddSBjoern A. Zeeb if (!size) { 2360bfcc09ddSBjoern A. Zeeb vfree(entry); 2361bfcc09ddSBjoern A. Zeeb return 0; 2362bfcc09ddSBjoern A. Zeeb } 2363bfcc09ddSBjoern A. Zeeb 2364bfcc09ddSBjoern A. Zeeb hdr = (void *)entry->data; 2365bfcc09ddSBjoern A. Zeeb hdr->barker = cpu_to_le32(IWL_FW_INI_ERROR_DUMP_BARKER); 2366bfcc09ddSBjoern A. Zeeb hdr->file_len = cpu_to_le32(size + entry->size); 2367bfcc09ddSBjoern A. Zeeb 2368bfcc09ddSBjoern A. Zeeb list_add(&entry->list, list); 2369bfcc09ddSBjoern A. Zeeb 2370bfcc09ddSBjoern A. Zeeb return le32_to_cpu(hdr->file_len); 2371bfcc09ddSBjoern A. Zeeb } 2372bfcc09ddSBjoern A. Zeeb 2373bfcc09ddSBjoern A. Zeeb static inline void iwl_fw_free_dump_desc(struct iwl_fw_runtime *fwrt, 2374bfcc09ddSBjoern A. Zeeb const struct iwl_fw_dump_desc *desc) 2375bfcc09ddSBjoern A. Zeeb { 2376bfcc09ddSBjoern A. Zeeb if (desc && desc != &iwl_dump_desc_assert) 2377bfcc09ddSBjoern A. Zeeb kfree(desc); 2378bfcc09ddSBjoern A. Zeeb 2379bfcc09ddSBjoern A. Zeeb fwrt->dump.lmac_err_id[0] = 0; 2380bfcc09ddSBjoern A. Zeeb if (fwrt->smem_cfg.num_lmacs > 1) 2381bfcc09ddSBjoern A. Zeeb fwrt->dump.lmac_err_id[1] = 0; 2382bfcc09ddSBjoern A. Zeeb fwrt->dump.umac_err_id = 0; 2383bfcc09ddSBjoern A. Zeeb } 2384bfcc09ddSBjoern A. Zeeb 2385bfcc09ddSBjoern A. Zeeb static void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt, 2386bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *dump_data) 2387bfcc09ddSBjoern A. Zeeb { 2388bfcc09ddSBjoern A. Zeeb struct iwl_fw_dump_ptrs fw_error_dump = {}; 2389bfcc09ddSBjoern A. Zeeb struct iwl_fw_error_dump_file *dump_file; 2390bfcc09ddSBjoern A. Zeeb struct scatterlist *sg_dump_data; 2391bfcc09ddSBjoern A. Zeeb u32 file_len; 2392bfcc09ddSBjoern A. Zeeb u32 dump_mask = fwrt->fw->dbg.dump_mask; 2393bfcc09ddSBjoern A. Zeeb 2394bfcc09ddSBjoern A. Zeeb dump_file = iwl_fw_error_dump_file(fwrt, &fw_error_dump, dump_data); 2395bfcc09ddSBjoern A. Zeeb if (!dump_file) 2396bfcc09ddSBjoern A. Zeeb return; 2397bfcc09ddSBjoern A. Zeeb 2398bfcc09ddSBjoern A. Zeeb if (dump_data->monitor_only) 2399bfcc09ddSBjoern A. Zeeb dump_mask &= BIT(IWL_FW_ERROR_DUMP_FW_MONITOR); 2400bfcc09ddSBjoern A. Zeeb 2401bfcc09ddSBjoern A. Zeeb fw_error_dump.trans_ptr = iwl_trans_dump_data(fwrt->trans, dump_mask, 2402bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops, 2403bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ctx); 2404bfcc09ddSBjoern A. Zeeb file_len = le32_to_cpu(dump_file->file_len); 2405bfcc09ddSBjoern A. Zeeb fw_error_dump.fwrt_len = file_len; 2406bfcc09ddSBjoern A. Zeeb 2407bfcc09ddSBjoern A. Zeeb if (fw_error_dump.trans_ptr) { 2408bfcc09ddSBjoern A. Zeeb file_len += fw_error_dump.trans_ptr->len; 2409bfcc09ddSBjoern A. Zeeb dump_file->file_len = cpu_to_le32(file_len); 2410bfcc09ddSBjoern A. Zeeb } 2411bfcc09ddSBjoern A. Zeeb 2412bfcc09ddSBjoern A. Zeeb sg_dump_data = alloc_sgtable(file_len); 2413bfcc09ddSBjoern A. Zeeb if (sg_dump_data) { 2414bfcc09ddSBjoern A. Zeeb sg_pcopy_from_buffer(sg_dump_data, 2415bfcc09ddSBjoern A. Zeeb sg_nents(sg_dump_data), 2416bfcc09ddSBjoern A. Zeeb fw_error_dump.fwrt_ptr, 2417bfcc09ddSBjoern A. Zeeb fw_error_dump.fwrt_len, 0); 2418bfcc09ddSBjoern A. Zeeb if (fw_error_dump.trans_ptr) 2419bfcc09ddSBjoern A. Zeeb sg_pcopy_from_buffer(sg_dump_data, 2420bfcc09ddSBjoern A. Zeeb sg_nents(sg_dump_data), 2421bfcc09ddSBjoern A. Zeeb fw_error_dump.trans_ptr->data, 2422bfcc09ddSBjoern A. Zeeb fw_error_dump.trans_ptr->len, 2423bfcc09ddSBjoern A. Zeeb fw_error_dump.fwrt_len); 2424bfcc09ddSBjoern A. Zeeb dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len, 2425bfcc09ddSBjoern A. Zeeb GFP_KERNEL); 2426bfcc09ddSBjoern A. Zeeb } 2427bfcc09ddSBjoern A. Zeeb vfree(fw_error_dump.fwrt_ptr); 2428bfcc09ddSBjoern A. Zeeb vfree(fw_error_dump.trans_ptr); 2429bfcc09ddSBjoern A. Zeeb } 2430bfcc09ddSBjoern A. Zeeb 2431bfcc09ddSBjoern A. Zeeb static void iwl_dump_ini_list_free(struct list_head *list) 2432bfcc09ddSBjoern A. Zeeb { 2433bfcc09ddSBjoern A. Zeeb while (!list_empty(list)) { 2434bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_entry *entry = 2435bfcc09ddSBjoern A. Zeeb list_entry(list->next, typeof(*entry), list); 2436bfcc09ddSBjoern A. Zeeb 2437bfcc09ddSBjoern A. Zeeb list_del(&entry->list); 2438bfcc09ddSBjoern A. Zeeb vfree(entry); 2439bfcc09ddSBjoern A. Zeeb } 2440bfcc09ddSBjoern A. Zeeb } 2441bfcc09ddSBjoern A. Zeeb 2442bfcc09ddSBjoern A. Zeeb static void iwl_fw_error_dump_data_free(struct iwl_fwrt_dump_data *dump_data) 2443bfcc09ddSBjoern A. Zeeb { 2444bfcc09ddSBjoern A. Zeeb dump_data->trig = NULL; 2445bfcc09ddSBjoern A. Zeeb kfree(dump_data->fw_pkt); 2446bfcc09ddSBjoern A. Zeeb dump_data->fw_pkt = NULL; 2447bfcc09ddSBjoern A. Zeeb } 2448bfcc09ddSBjoern A. Zeeb 2449bfcc09ddSBjoern A. Zeeb static void iwl_fw_error_ini_dump(struct iwl_fw_runtime *fwrt, 2450bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *dump_data) 2451bfcc09ddSBjoern A. Zeeb { 2452bfcc09ddSBjoern A. Zeeb #if defined(__linux__) 2453bfcc09ddSBjoern A. Zeeb struct list_head dump_list = LIST_HEAD_INIT(dump_list); 2454bfcc09ddSBjoern A. Zeeb #elif defined(__FreeBSD__) 2455bfcc09ddSBjoern A. Zeeb struct list_head dump_list = LINUX_LIST_HEAD_INIT(dump_list); 2456bfcc09ddSBjoern A. Zeeb #endif 2457bfcc09ddSBjoern A. Zeeb struct scatterlist *sg_dump_data; 2458bfcc09ddSBjoern A. Zeeb u32 file_len = iwl_dump_ini_file_gen(fwrt, dump_data, &dump_list); 2459bfcc09ddSBjoern A. Zeeb 2460bfcc09ddSBjoern A. Zeeb if (!file_len) 2461bfcc09ddSBjoern A. Zeeb return; 2462bfcc09ddSBjoern A. Zeeb 2463bfcc09ddSBjoern A. Zeeb sg_dump_data = alloc_sgtable(file_len); 2464bfcc09ddSBjoern A. Zeeb if (sg_dump_data) { 2465bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_dump_entry *entry; 2466bfcc09ddSBjoern A. Zeeb int sg_entries = sg_nents(sg_dump_data); 2467bfcc09ddSBjoern A. Zeeb u32 offs = 0; 2468bfcc09ddSBjoern A. Zeeb 2469bfcc09ddSBjoern A. Zeeb list_for_each_entry(entry, &dump_list, list) { 2470bfcc09ddSBjoern A. Zeeb sg_pcopy_from_buffer(sg_dump_data, sg_entries, 2471bfcc09ddSBjoern A. Zeeb entry->data, entry->size, offs); 2472bfcc09ddSBjoern A. Zeeb offs += entry->size; 2473bfcc09ddSBjoern A. Zeeb } 2474bfcc09ddSBjoern A. Zeeb dev_coredumpsg(fwrt->trans->dev, sg_dump_data, file_len, 2475bfcc09ddSBjoern A. Zeeb GFP_KERNEL); 2476bfcc09ddSBjoern A. Zeeb } 2477bfcc09ddSBjoern A. Zeeb iwl_dump_ini_list_free(&dump_list); 2478bfcc09ddSBjoern A. Zeeb } 2479bfcc09ddSBjoern A. Zeeb 2480bfcc09ddSBjoern A. Zeeb const struct iwl_fw_dump_desc iwl_dump_desc_assert = { 2481bfcc09ddSBjoern A. Zeeb .trig_desc = { 2482bfcc09ddSBjoern A. Zeeb .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), 2483bfcc09ddSBjoern A. Zeeb }, 2484bfcc09ddSBjoern A. Zeeb }; 2485bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_dump_desc_assert); 2486bfcc09ddSBjoern A. Zeeb 2487bfcc09ddSBjoern A. Zeeb int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, 2488bfcc09ddSBjoern A. Zeeb const struct iwl_fw_dump_desc *desc, 2489bfcc09ddSBjoern A. Zeeb bool monitor_only, 2490bfcc09ddSBjoern A. Zeeb unsigned int delay) 2491bfcc09ddSBjoern A. Zeeb { 2492bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_wk_data *wk_data; 2493bfcc09ddSBjoern A. Zeeb unsigned long idx; 2494bfcc09ddSBjoern A. Zeeb 2495bfcc09ddSBjoern A. Zeeb if (iwl_trans_dbg_ini_valid(fwrt->trans)) { 2496bfcc09ddSBjoern A. Zeeb iwl_fw_free_dump_desc(fwrt, desc); 2497bfcc09ddSBjoern A. Zeeb return 0; 2498bfcc09ddSBjoern A. Zeeb } 2499bfcc09ddSBjoern A. Zeeb 2500bfcc09ddSBjoern A. Zeeb /* 2501bfcc09ddSBjoern A. Zeeb * Check there is an available worker. 2502bfcc09ddSBjoern A. Zeeb * ffz return value is undefined if no zero exists, 2503bfcc09ddSBjoern A. Zeeb * so check against ~0UL first. 2504bfcc09ddSBjoern A. Zeeb */ 2505bfcc09ddSBjoern A. Zeeb if (fwrt->dump.active_wks == ~0UL) 2506bfcc09ddSBjoern A. Zeeb return -EBUSY; 2507bfcc09ddSBjoern A. Zeeb 2508bfcc09ddSBjoern A. Zeeb idx = ffz(fwrt->dump.active_wks); 2509bfcc09ddSBjoern A. Zeeb 2510bfcc09ddSBjoern A. Zeeb if (idx >= IWL_FW_RUNTIME_DUMP_WK_NUM || 2511bfcc09ddSBjoern A. Zeeb test_and_set_bit(fwrt->dump.wks[idx].idx, &fwrt->dump.active_wks)) 2512bfcc09ddSBjoern A. Zeeb return -EBUSY; 2513bfcc09ddSBjoern A. Zeeb 2514bfcc09ddSBjoern A. Zeeb wk_data = &fwrt->dump.wks[idx]; 2515bfcc09ddSBjoern A. Zeeb 2516bfcc09ddSBjoern A. Zeeb if (WARN_ON(wk_data->dump_data.desc)) 2517bfcc09ddSBjoern A. Zeeb iwl_fw_free_dump_desc(fwrt, wk_data->dump_data.desc); 2518bfcc09ddSBjoern A. Zeeb 2519bfcc09ddSBjoern A. Zeeb wk_data->dump_data.desc = desc; 2520bfcc09ddSBjoern A. Zeeb wk_data->dump_data.monitor_only = monitor_only; 2521bfcc09ddSBjoern A. Zeeb 2522bfcc09ddSBjoern A. Zeeb IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n", 2523bfcc09ddSBjoern A. Zeeb le32_to_cpu(desc->trig_desc.type)); 2524bfcc09ddSBjoern A. Zeeb 2525bfcc09ddSBjoern A. Zeeb schedule_delayed_work(&wk_data->wk, usecs_to_jiffies(delay)); 2526bfcc09ddSBjoern A. Zeeb 2527bfcc09ddSBjoern A. Zeeb return 0; 2528bfcc09ddSBjoern A. Zeeb } 2529bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc); 2530bfcc09ddSBjoern A. Zeeb 2531bfcc09ddSBjoern A. Zeeb int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt, 2532bfcc09ddSBjoern A. Zeeb enum iwl_fw_dbg_trigger trig_type) 2533bfcc09ddSBjoern A. Zeeb { 2534bfcc09ddSBjoern A. Zeeb if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) 2535bfcc09ddSBjoern A. Zeeb return -EIO; 2536bfcc09ddSBjoern A. Zeeb 2537bfcc09ddSBjoern A. Zeeb if (iwl_trans_dbg_ini_valid(fwrt->trans)) { 2538bfcc09ddSBjoern A. Zeeb if (trig_type != FW_DBG_TRIGGER_ALIVE_TIMEOUT && 2539bfcc09ddSBjoern A. Zeeb trig_type != FW_DBG_TRIGGER_DRIVER) 2540bfcc09ddSBjoern A. Zeeb return -EIO; 2541bfcc09ddSBjoern A. Zeeb 2542bfcc09ddSBjoern A. Zeeb iwl_dbg_tlv_time_point(fwrt, 2543bfcc09ddSBjoern A. Zeeb IWL_FW_INI_TIME_POINT_HOST_ALIVE_TIMEOUT, 2544bfcc09ddSBjoern A. Zeeb NULL); 2545bfcc09ddSBjoern A. Zeeb } else { 2546bfcc09ddSBjoern A. Zeeb struct iwl_fw_dump_desc *iwl_dump_error_desc; 2547bfcc09ddSBjoern A. Zeeb int ret; 2548bfcc09ddSBjoern A. Zeeb 2549bfcc09ddSBjoern A. Zeeb iwl_dump_error_desc = 2550bfcc09ddSBjoern A. Zeeb kmalloc(sizeof(*iwl_dump_error_desc), GFP_KERNEL); 2551bfcc09ddSBjoern A. Zeeb 2552bfcc09ddSBjoern A. Zeeb if (!iwl_dump_error_desc) 2553bfcc09ddSBjoern A. Zeeb return -ENOMEM; 2554bfcc09ddSBjoern A. Zeeb 2555bfcc09ddSBjoern A. Zeeb iwl_dump_error_desc->trig_desc.type = cpu_to_le32(trig_type); 2556bfcc09ddSBjoern A. Zeeb iwl_dump_error_desc->len = 0; 2557bfcc09ddSBjoern A. Zeeb 2558bfcc09ddSBjoern A. Zeeb ret = iwl_fw_dbg_collect_desc(fwrt, iwl_dump_error_desc, 2559bfcc09ddSBjoern A. Zeeb false, 0); 2560bfcc09ddSBjoern A. Zeeb if (ret) { 2561bfcc09ddSBjoern A. Zeeb kfree(iwl_dump_error_desc); 2562bfcc09ddSBjoern A. Zeeb return ret; 2563bfcc09ddSBjoern A. Zeeb } 2564bfcc09ddSBjoern A. Zeeb } 2565bfcc09ddSBjoern A. Zeeb 2566bfcc09ddSBjoern A. Zeeb iwl_trans_sync_nmi(fwrt->trans); 2567bfcc09ddSBjoern A. Zeeb 2568bfcc09ddSBjoern A. Zeeb return 0; 2569bfcc09ddSBjoern A. Zeeb } 2570bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_dbg_error_collect); 2571bfcc09ddSBjoern A. Zeeb 2572bfcc09ddSBjoern A. Zeeb int iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, 2573bfcc09ddSBjoern A. Zeeb enum iwl_fw_dbg_trigger trig, 2574bfcc09ddSBjoern A. Zeeb const char *str, size_t len, 2575bfcc09ddSBjoern A. Zeeb struct iwl_fw_dbg_trigger_tlv *trigger) 2576bfcc09ddSBjoern A. Zeeb { 2577bfcc09ddSBjoern A. Zeeb struct iwl_fw_dump_desc *desc; 2578bfcc09ddSBjoern A. Zeeb unsigned int delay = 0; 2579bfcc09ddSBjoern A. Zeeb bool monitor_only = false; 2580bfcc09ddSBjoern A. Zeeb 2581bfcc09ddSBjoern A. Zeeb if (trigger) { 2582bfcc09ddSBjoern A. Zeeb u16 occurrences = le16_to_cpu(trigger->occurrences) - 1; 2583bfcc09ddSBjoern A. Zeeb 2584bfcc09ddSBjoern A. Zeeb if (!le16_to_cpu(trigger->occurrences)) 2585bfcc09ddSBjoern A. Zeeb return 0; 2586bfcc09ddSBjoern A. Zeeb 2587bfcc09ddSBjoern A. Zeeb if (trigger->flags & IWL_FW_DBG_FORCE_RESTART) { 2588bfcc09ddSBjoern A. Zeeb IWL_WARN(fwrt, "Force restart: trigger %d fired.\n", 2589bfcc09ddSBjoern A. Zeeb trig); 2590bfcc09ddSBjoern A. Zeeb iwl_force_nmi(fwrt->trans); 2591bfcc09ddSBjoern A. Zeeb return 0; 2592bfcc09ddSBjoern A. Zeeb } 2593bfcc09ddSBjoern A. Zeeb 2594bfcc09ddSBjoern A. Zeeb trigger->occurrences = cpu_to_le16(occurrences); 2595bfcc09ddSBjoern A. Zeeb monitor_only = trigger->mode & IWL_FW_DBG_TRIGGER_MONITOR_ONLY; 2596bfcc09ddSBjoern A. Zeeb 2597bfcc09ddSBjoern A. Zeeb /* convert msec to usec */ 2598bfcc09ddSBjoern A. Zeeb delay = le32_to_cpu(trigger->stop_delay) * USEC_PER_MSEC; 2599bfcc09ddSBjoern A. Zeeb } 2600bfcc09ddSBjoern A. Zeeb 2601bfcc09ddSBjoern A. Zeeb desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC); 2602bfcc09ddSBjoern A. Zeeb if (!desc) 2603bfcc09ddSBjoern A. Zeeb return -ENOMEM; 2604bfcc09ddSBjoern A. Zeeb 2605bfcc09ddSBjoern A. Zeeb 2606bfcc09ddSBjoern A. Zeeb desc->len = len; 2607bfcc09ddSBjoern A. Zeeb desc->trig_desc.type = cpu_to_le32(trig); 2608bfcc09ddSBjoern A. Zeeb memcpy(desc->trig_desc.data, str, len); 2609bfcc09ddSBjoern A. Zeeb 2610bfcc09ddSBjoern A. Zeeb return iwl_fw_dbg_collect_desc(fwrt, desc, monitor_only, delay); 2611bfcc09ddSBjoern A. Zeeb } 2612bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect); 2613bfcc09ddSBjoern A. Zeeb 2614bfcc09ddSBjoern A. Zeeb int iwl_fw_dbg_collect_trig(struct iwl_fw_runtime *fwrt, 2615bfcc09ddSBjoern A. Zeeb struct iwl_fw_dbg_trigger_tlv *trigger, 2616bfcc09ddSBjoern A. Zeeb const char *fmt, ...) 2617bfcc09ddSBjoern A. Zeeb { 2618bfcc09ddSBjoern A. Zeeb int ret, len = 0; 2619bfcc09ddSBjoern A. Zeeb char buf[64]; 2620bfcc09ddSBjoern A. Zeeb 2621bfcc09ddSBjoern A. Zeeb if (iwl_trans_dbg_ini_valid(fwrt->trans)) 2622bfcc09ddSBjoern A. Zeeb return 0; 2623bfcc09ddSBjoern A. Zeeb 2624bfcc09ddSBjoern A. Zeeb if (fmt) { 2625bfcc09ddSBjoern A. Zeeb va_list ap; 2626bfcc09ddSBjoern A. Zeeb 2627bfcc09ddSBjoern A. Zeeb buf[sizeof(buf) - 1] = '\0'; 2628bfcc09ddSBjoern A. Zeeb 2629bfcc09ddSBjoern A. Zeeb va_start(ap, fmt); 2630bfcc09ddSBjoern A. Zeeb vsnprintf(buf, sizeof(buf), fmt, ap); 2631bfcc09ddSBjoern A. Zeeb va_end(ap); 2632bfcc09ddSBjoern A. Zeeb 2633bfcc09ddSBjoern A. Zeeb /* check for truncation */ 2634bfcc09ddSBjoern A. Zeeb if (WARN_ON_ONCE(buf[sizeof(buf) - 1])) 2635bfcc09ddSBjoern A. Zeeb buf[sizeof(buf) - 1] = '\0'; 2636bfcc09ddSBjoern A. Zeeb 2637bfcc09ddSBjoern A. Zeeb len = strlen(buf) + 1; 2638bfcc09ddSBjoern A. Zeeb } 2639bfcc09ddSBjoern A. Zeeb 2640bfcc09ddSBjoern A. Zeeb ret = iwl_fw_dbg_collect(fwrt, le32_to_cpu(trigger->id), buf, len, 2641bfcc09ddSBjoern A. Zeeb trigger); 2642bfcc09ddSBjoern A. Zeeb 2643bfcc09ddSBjoern A. Zeeb if (ret) 2644bfcc09ddSBjoern A. Zeeb return ret; 2645bfcc09ddSBjoern A. Zeeb 2646bfcc09ddSBjoern A. Zeeb return 0; 2647bfcc09ddSBjoern A. Zeeb } 2648bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_trig); 2649bfcc09ddSBjoern A. Zeeb 2650bfcc09ddSBjoern A. Zeeb int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id) 2651bfcc09ddSBjoern A. Zeeb { 2652bfcc09ddSBjoern A. Zeeb u8 *ptr; 2653bfcc09ddSBjoern A. Zeeb int ret; 2654bfcc09ddSBjoern A. Zeeb int i; 2655bfcc09ddSBjoern A. Zeeb 2656bfcc09ddSBjoern A. Zeeb if (WARN_ONCE(conf_id >= ARRAY_SIZE(fwrt->fw->dbg.conf_tlv), 2657bfcc09ddSBjoern A. Zeeb "Invalid configuration %d\n", conf_id)) 2658bfcc09ddSBjoern A. Zeeb return -EINVAL; 2659bfcc09ddSBjoern A. Zeeb 2660bfcc09ddSBjoern A. Zeeb /* EARLY START - firmware's configuration is hard coded */ 2661bfcc09ddSBjoern A. Zeeb if ((!fwrt->fw->dbg.conf_tlv[conf_id] || 2662bfcc09ddSBjoern A. Zeeb !fwrt->fw->dbg.conf_tlv[conf_id]->num_of_hcmds) && 2663bfcc09ddSBjoern A. Zeeb conf_id == FW_DBG_START_FROM_ALIVE) 2664bfcc09ddSBjoern A. Zeeb return 0; 2665bfcc09ddSBjoern A. Zeeb 2666bfcc09ddSBjoern A. Zeeb if (!fwrt->fw->dbg.conf_tlv[conf_id]) 2667bfcc09ddSBjoern A. Zeeb return -EINVAL; 2668bfcc09ddSBjoern A. Zeeb 2669bfcc09ddSBjoern A. Zeeb if (fwrt->dump.conf != FW_DBG_INVALID) 2670bfcc09ddSBjoern A. Zeeb IWL_INFO(fwrt, "FW already configured (%d) - re-configuring\n", 2671bfcc09ddSBjoern A. Zeeb fwrt->dump.conf); 2672bfcc09ddSBjoern A. Zeeb 2673bfcc09ddSBjoern A. Zeeb /* Send all HCMDs for configuring the FW debug */ 2674bfcc09ddSBjoern A. Zeeb ptr = (void *)&fwrt->fw->dbg.conf_tlv[conf_id]->hcmd; 2675bfcc09ddSBjoern A. Zeeb for (i = 0; i < fwrt->fw->dbg.conf_tlv[conf_id]->num_of_hcmds; i++) { 2676bfcc09ddSBjoern A. Zeeb struct iwl_fw_dbg_conf_hcmd *cmd = (void *)ptr; 2677bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 2678bfcc09ddSBjoern A. Zeeb .id = cmd->id, 2679bfcc09ddSBjoern A. Zeeb .len = { le16_to_cpu(cmd->len), }, 2680bfcc09ddSBjoern A. Zeeb .data = { cmd->data, }, 2681bfcc09ddSBjoern A. Zeeb }; 2682bfcc09ddSBjoern A. Zeeb 2683bfcc09ddSBjoern A. Zeeb ret = iwl_trans_send_cmd(fwrt->trans, &hcmd); 2684bfcc09ddSBjoern A. Zeeb if (ret) 2685bfcc09ddSBjoern A. Zeeb return ret; 2686bfcc09ddSBjoern A. Zeeb 2687bfcc09ddSBjoern A. Zeeb ptr += sizeof(*cmd); 2688bfcc09ddSBjoern A. Zeeb ptr += le16_to_cpu(cmd->len); 2689bfcc09ddSBjoern A. Zeeb } 2690bfcc09ddSBjoern A. Zeeb 2691bfcc09ddSBjoern A. Zeeb fwrt->dump.conf = conf_id; 2692bfcc09ddSBjoern A. Zeeb 2693bfcc09ddSBjoern A. Zeeb return 0; 2694bfcc09ddSBjoern A. Zeeb } 2695bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf); 2696bfcc09ddSBjoern A. Zeeb 2697bfcc09ddSBjoern A. Zeeb /* this function assumes dump_start was called beforehand and dump_end will be 2698bfcc09ddSBjoern A. Zeeb * called afterwards 2699bfcc09ddSBjoern A. Zeeb */ 2700bfcc09ddSBjoern A. Zeeb static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) 2701bfcc09ddSBjoern A. Zeeb { 2702bfcc09ddSBjoern A. Zeeb struct iwl_fw_dbg_params params = {0}; 2703bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *dump_data = 2704bfcc09ddSBjoern A. Zeeb &fwrt->dump.wks[wk_idx].dump_data; 2705bfcc09ddSBjoern A. Zeeb 2706bfcc09ddSBjoern A. Zeeb if (!test_bit(wk_idx, &fwrt->dump.active_wks)) 2707bfcc09ddSBjoern A. Zeeb return; 2708bfcc09ddSBjoern A. Zeeb 2709bfcc09ddSBjoern A. Zeeb if (!test_bit(STATUS_DEVICE_ENABLED, &fwrt->trans->status)) { 2710bfcc09ddSBjoern A. Zeeb IWL_ERR(fwrt, "Device is not enabled - cannot dump error\n"); 2711bfcc09ddSBjoern A. Zeeb goto out; 2712bfcc09ddSBjoern A. Zeeb } 2713bfcc09ddSBjoern A. Zeeb 2714bfcc09ddSBjoern A. Zeeb /* there's no point in fw dump if the bus is dead */ 2715bfcc09ddSBjoern A. Zeeb if (test_bit(STATUS_TRANS_DEAD, &fwrt->trans->status)) { 2716bfcc09ddSBjoern A. Zeeb IWL_ERR(fwrt, "Skip fw error dump since bus is dead\n"); 2717bfcc09ddSBjoern A. Zeeb goto out; 2718bfcc09ddSBjoern A. Zeeb } 2719bfcc09ddSBjoern A. Zeeb 2720bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, true); 2721bfcc09ddSBjoern A. Zeeb 2722bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection start\n"); 2723bfcc09ddSBjoern A. Zeeb if (iwl_trans_dbg_ini_valid(fwrt->trans)) 2724bfcc09ddSBjoern A. Zeeb iwl_fw_error_ini_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data); 2725bfcc09ddSBjoern A. Zeeb else 2726bfcc09ddSBjoern A. Zeeb iwl_fw_error_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data); 2727bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection done\n"); 2728bfcc09ddSBjoern A. Zeeb 2729bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, false); 2730bfcc09ddSBjoern A. Zeeb 2731bfcc09ddSBjoern A. Zeeb out: 2732bfcc09ddSBjoern A. Zeeb if (iwl_trans_dbg_ini_valid(fwrt->trans)) { 2733bfcc09ddSBjoern A. Zeeb iwl_fw_error_dump_data_free(dump_data); 2734bfcc09ddSBjoern A. Zeeb } else { 2735bfcc09ddSBjoern A. Zeeb iwl_fw_free_dump_desc(fwrt, dump_data->desc); 2736bfcc09ddSBjoern A. Zeeb dump_data->desc = NULL; 2737bfcc09ddSBjoern A. Zeeb } 2738bfcc09ddSBjoern A. Zeeb 2739bfcc09ddSBjoern A. Zeeb clear_bit(wk_idx, &fwrt->dump.active_wks); 2740bfcc09ddSBjoern A. Zeeb } 2741bfcc09ddSBjoern A. Zeeb 2742bfcc09ddSBjoern A. Zeeb int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, 2743bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_dump_data *dump_data, 2744bfcc09ddSBjoern A. Zeeb bool sync) 2745bfcc09ddSBjoern A. Zeeb { 2746bfcc09ddSBjoern A. Zeeb struct iwl_fw_ini_trigger_tlv *trig = dump_data->trig; 2747bfcc09ddSBjoern A. Zeeb enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trig->time_point); 2748bfcc09ddSBjoern A. Zeeb u32 occur, delay; 2749bfcc09ddSBjoern A. Zeeb unsigned long idx; 2750bfcc09ddSBjoern A. Zeeb 2751bfcc09ddSBjoern A. Zeeb if (!iwl_fw_ini_trigger_on(fwrt, trig)) { 2752bfcc09ddSBjoern A. Zeeb IWL_WARN(fwrt, "WRT: Trigger %d is not active, aborting dump\n", 2753bfcc09ddSBjoern A. Zeeb tp_id); 2754bfcc09ddSBjoern A. Zeeb return -EINVAL; 2755bfcc09ddSBjoern A. Zeeb } 2756bfcc09ddSBjoern A. Zeeb 2757bfcc09ddSBjoern A. Zeeb delay = le32_to_cpu(trig->dump_delay); 2758bfcc09ddSBjoern A. Zeeb occur = le32_to_cpu(trig->occurrences); 2759bfcc09ddSBjoern A. Zeeb if (!occur) 2760bfcc09ddSBjoern A. Zeeb return 0; 2761bfcc09ddSBjoern A. Zeeb 2762bfcc09ddSBjoern A. Zeeb trig->occurrences = cpu_to_le32(--occur); 2763bfcc09ddSBjoern A. Zeeb 2764bfcc09ddSBjoern A. Zeeb /* Check there is an available worker. 2765bfcc09ddSBjoern A. Zeeb * ffz return value is undefined if no zero exists, 2766bfcc09ddSBjoern A. Zeeb * so check against ~0UL first. 2767bfcc09ddSBjoern A. Zeeb */ 2768bfcc09ddSBjoern A. Zeeb if (fwrt->dump.active_wks == ~0UL) 2769bfcc09ddSBjoern A. Zeeb return -EBUSY; 2770bfcc09ddSBjoern A. Zeeb 2771bfcc09ddSBjoern A. Zeeb idx = ffz(fwrt->dump.active_wks); 2772bfcc09ddSBjoern A. Zeeb 2773bfcc09ddSBjoern A. Zeeb if (idx >= IWL_FW_RUNTIME_DUMP_WK_NUM || 2774bfcc09ddSBjoern A. Zeeb test_and_set_bit(fwrt->dump.wks[idx].idx, &fwrt->dump.active_wks)) 2775bfcc09ddSBjoern A. Zeeb return -EBUSY; 2776bfcc09ddSBjoern A. Zeeb 2777bfcc09ddSBjoern A. Zeeb fwrt->dump.wks[idx].dump_data = *dump_data; 2778bfcc09ddSBjoern A. Zeeb 2779bfcc09ddSBjoern A. Zeeb if (sync) 2780bfcc09ddSBjoern A. Zeeb delay = 0; 2781bfcc09ddSBjoern A. Zeeb 2782bfcc09ddSBjoern A. Zeeb IWL_WARN(fwrt, 2783bfcc09ddSBjoern A. Zeeb "WRT: Collecting data: ini trigger %d fired (delay=%dms).\n", 2784bfcc09ddSBjoern A. Zeeb tp_id, (u32)(delay / USEC_PER_MSEC)); 2785bfcc09ddSBjoern A. Zeeb 2786bfcc09ddSBjoern A. Zeeb schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); 2787bfcc09ddSBjoern A. Zeeb 2788bfcc09ddSBjoern A. Zeeb if (sync) 2789bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_collect_sync(fwrt, idx); 2790bfcc09ddSBjoern A. Zeeb 2791bfcc09ddSBjoern A. Zeeb return 0; 2792bfcc09ddSBjoern A. Zeeb } 2793bfcc09ddSBjoern A. Zeeb 2794bfcc09ddSBjoern A. Zeeb void iwl_fw_error_dump_wk(struct work_struct *work) 2795bfcc09ddSBjoern A. Zeeb { 2796bfcc09ddSBjoern A. Zeeb struct iwl_fwrt_wk_data *wks = 2797bfcc09ddSBjoern A. Zeeb container_of(work, typeof(*wks), wk.work); 2798bfcc09ddSBjoern A. Zeeb struct iwl_fw_runtime *fwrt = 2799bfcc09ddSBjoern A. Zeeb container_of(wks, typeof(*fwrt), dump.wks[wks->idx]); 2800bfcc09ddSBjoern A. Zeeb 2801bfcc09ddSBjoern A. Zeeb /* assumes the op mode mutex is locked in dump_start since 2802bfcc09ddSBjoern A. Zeeb * iwl_fw_dbg_collect_sync can't run in parallel 2803bfcc09ddSBjoern A. Zeeb */ 2804bfcc09ddSBjoern A. Zeeb if (fwrt->ops && fwrt->ops->dump_start && 2805bfcc09ddSBjoern A. Zeeb fwrt->ops->dump_start(fwrt->ops_ctx)) 2806bfcc09ddSBjoern A. Zeeb return; 2807bfcc09ddSBjoern A. Zeeb 2808bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_collect_sync(fwrt, wks->idx); 2809bfcc09ddSBjoern A. Zeeb 2810bfcc09ddSBjoern A. Zeeb if (fwrt->ops && fwrt->ops->dump_end) 2811bfcc09ddSBjoern A. Zeeb fwrt->ops->dump_end(fwrt->ops_ctx); 2812bfcc09ddSBjoern A. Zeeb } 2813bfcc09ddSBjoern A. Zeeb 2814bfcc09ddSBjoern A. Zeeb void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt) 2815bfcc09ddSBjoern A. Zeeb { 2816bfcc09ddSBjoern A. Zeeb const struct iwl_cfg *cfg = fwrt->trans->cfg; 2817bfcc09ddSBjoern A. Zeeb 2818bfcc09ddSBjoern A. Zeeb if (!iwl_fw_dbg_is_d3_debug_enabled(fwrt)) 2819bfcc09ddSBjoern A. Zeeb return; 2820bfcc09ddSBjoern A. Zeeb 2821bfcc09ddSBjoern A. Zeeb if (!fwrt->dump.d3_debug_data) { 2822bfcc09ddSBjoern A. Zeeb fwrt->dump.d3_debug_data = kmalloc(cfg->d3_debug_data_length, 2823bfcc09ddSBjoern A. Zeeb GFP_KERNEL); 2824bfcc09ddSBjoern A. Zeeb if (!fwrt->dump.d3_debug_data) { 2825bfcc09ddSBjoern A. Zeeb IWL_ERR(fwrt, 2826bfcc09ddSBjoern A. Zeeb "failed to allocate memory for D3 debug data\n"); 2827bfcc09ddSBjoern A. Zeeb return; 2828bfcc09ddSBjoern A. Zeeb } 2829bfcc09ddSBjoern A. Zeeb } 2830bfcc09ddSBjoern A. Zeeb 2831bfcc09ddSBjoern A. Zeeb /* if the buffer holds previous debug data it is overwritten */ 2832bfcc09ddSBjoern A. Zeeb iwl_trans_read_mem_bytes(fwrt->trans, cfg->d3_debug_data_base_addr, 2833bfcc09ddSBjoern A. Zeeb fwrt->dump.d3_debug_data, 2834bfcc09ddSBjoern A. Zeeb cfg->d3_debug_data_length); 2835bfcc09ddSBjoern A. Zeeb 2836bfcc09ddSBjoern A. Zeeb if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) 2837bfcc09ddSBjoern A. Zeeb fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, 2838bfcc09ddSBjoern A. Zeeb cfg->d3_debug_data_base_addr, 2839bfcc09ddSBjoern A. Zeeb fwrt->dump.d3_debug_data, 2840bfcc09ddSBjoern A. Zeeb cfg->d3_debug_data_length); 2841bfcc09ddSBjoern A. Zeeb } 2842bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data); 2843bfcc09ddSBjoern A. Zeeb 2844bfcc09ddSBjoern A. Zeeb void iwl_fw_dbg_stop_sync(struct iwl_fw_runtime *fwrt) 2845bfcc09ddSBjoern A. Zeeb { 2846bfcc09ddSBjoern A. Zeeb int i; 2847bfcc09ddSBjoern A. Zeeb 2848bfcc09ddSBjoern A. Zeeb iwl_dbg_tlv_del_timers(fwrt->trans); 2849bfcc09ddSBjoern A. Zeeb for (i = 0; i < IWL_FW_RUNTIME_DUMP_WK_NUM; i++) 2850bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_collect_sync(fwrt, i); 2851bfcc09ddSBjoern A. Zeeb 2852bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_stop_restart_recording(fwrt, NULL, true); 2853bfcc09ddSBjoern A. Zeeb } 2854bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_dbg_stop_sync); 2855bfcc09ddSBjoern A. Zeeb 2856bfcc09ddSBjoern A. Zeeb static int iwl_fw_dbg_suspend_resume_hcmd(struct iwl_trans *trans, bool suspend) 2857bfcc09ddSBjoern A. Zeeb { 2858bfcc09ddSBjoern A. Zeeb struct iwl_dbg_suspend_resume_cmd cmd = { 2859bfcc09ddSBjoern A. Zeeb .operation = suspend ? 2860bfcc09ddSBjoern A. Zeeb cpu_to_le32(DBGC_SUSPEND_CMD) : 2861bfcc09ddSBjoern A. Zeeb cpu_to_le32(DBGC_RESUME_CMD), 2862bfcc09ddSBjoern A. Zeeb }; 2863bfcc09ddSBjoern A. Zeeb struct iwl_host_cmd hcmd = { 2864bfcc09ddSBjoern A. Zeeb .id = WIDE_ID(DEBUG_GROUP, DBGC_SUSPEND_RESUME), 2865bfcc09ddSBjoern A. Zeeb .data[0] = &cmd, 2866bfcc09ddSBjoern A. Zeeb .len[0] = sizeof(cmd), 2867bfcc09ddSBjoern A. Zeeb }; 2868bfcc09ddSBjoern A. Zeeb 2869bfcc09ddSBjoern A. Zeeb return iwl_trans_send_cmd(trans, &hcmd); 2870bfcc09ddSBjoern A. Zeeb } 2871bfcc09ddSBjoern A. Zeeb 2872bfcc09ddSBjoern A. Zeeb static void iwl_fw_dbg_stop_recording(struct iwl_trans *trans, 2873bfcc09ddSBjoern A. Zeeb struct iwl_fw_dbg_params *params) 2874bfcc09ddSBjoern A. Zeeb { 2875bfcc09ddSBjoern A. Zeeb if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { 2876bfcc09ddSBjoern A. Zeeb iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); 2877bfcc09ddSBjoern A. Zeeb return; 2878bfcc09ddSBjoern A. Zeeb } 2879bfcc09ddSBjoern A. Zeeb 2880bfcc09ddSBjoern A. Zeeb if (params) { 2881bfcc09ddSBjoern A. Zeeb params->in_sample = iwl_read_umac_prph(trans, DBGC_IN_SAMPLE); 2882bfcc09ddSBjoern A. Zeeb params->out_ctrl = iwl_read_umac_prph(trans, DBGC_OUT_CTRL); 2883bfcc09ddSBjoern A. Zeeb } 2884bfcc09ddSBjoern A. Zeeb 2885bfcc09ddSBjoern A. Zeeb iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, 0); 2886bfcc09ddSBjoern A. Zeeb /* wait for the DBGC to finish writing the internal buffer to DRAM to 2887bfcc09ddSBjoern A. Zeeb * avoid halting the HW while writing 2888bfcc09ddSBjoern A. Zeeb */ 2889bfcc09ddSBjoern A. Zeeb usleep_range(700, 1000); 2890bfcc09ddSBjoern A. Zeeb iwl_write_umac_prph(trans, DBGC_OUT_CTRL, 0); 2891bfcc09ddSBjoern A. Zeeb } 2892bfcc09ddSBjoern A. Zeeb 2893bfcc09ddSBjoern A. Zeeb static int iwl_fw_dbg_restart_recording(struct iwl_trans *trans, 2894bfcc09ddSBjoern A. Zeeb struct iwl_fw_dbg_params *params) 2895bfcc09ddSBjoern A. Zeeb { 2896bfcc09ddSBjoern A. Zeeb if (!params) 2897bfcc09ddSBjoern A. Zeeb return -EIO; 2898bfcc09ddSBjoern A. Zeeb 2899bfcc09ddSBjoern A. Zeeb if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { 2900bfcc09ddSBjoern A. Zeeb iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); 2901bfcc09ddSBjoern A. Zeeb iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); 2902bfcc09ddSBjoern A. Zeeb iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); 2903bfcc09ddSBjoern A. Zeeb } else { 2904bfcc09ddSBjoern A. Zeeb iwl_write_umac_prph(trans, DBGC_IN_SAMPLE, params->in_sample); 2905bfcc09ddSBjoern A. Zeeb iwl_write_umac_prph(trans, DBGC_OUT_CTRL, params->out_ctrl); 2906bfcc09ddSBjoern A. Zeeb } 2907bfcc09ddSBjoern A. Zeeb 2908bfcc09ddSBjoern A. Zeeb return 0; 2909bfcc09ddSBjoern A. Zeeb } 2910bfcc09ddSBjoern A. Zeeb 2911bfcc09ddSBjoern A. Zeeb void iwl_fw_dbg_stop_restart_recording(struct iwl_fw_runtime *fwrt, 2912bfcc09ddSBjoern A. Zeeb struct iwl_fw_dbg_params *params, 2913bfcc09ddSBjoern A. Zeeb bool stop) 2914bfcc09ddSBjoern A. Zeeb { 2915bfcc09ddSBjoern A. Zeeb int ret __maybe_unused = 0; 2916bfcc09ddSBjoern A. Zeeb 2917bfcc09ddSBjoern A. Zeeb if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) 2918bfcc09ddSBjoern A. Zeeb return; 2919bfcc09ddSBjoern A. Zeeb 2920bfcc09ddSBjoern A. Zeeb if (fw_has_capa(&fwrt->fw->ucode_capa, 2921bfcc09ddSBjoern A. Zeeb IWL_UCODE_TLV_CAPA_DBG_SUSPEND_RESUME_CMD_SUPP)) 2922bfcc09ddSBjoern A. Zeeb ret = iwl_fw_dbg_suspend_resume_hcmd(fwrt->trans, stop); 2923bfcc09ddSBjoern A. Zeeb else if (stop) 2924bfcc09ddSBjoern A. Zeeb iwl_fw_dbg_stop_recording(fwrt->trans, params); 2925bfcc09ddSBjoern A. Zeeb else 2926bfcc09ddSBjoern A. Zeeb ret = iwl_fw_dbg_restart_recording(fwrt->trans, params); 2927bfcc09ddSBjoern A. Zeeb #ifdef CONFIG_IWLWIFI_DEBUGFS 2928bfcc09ddSBjoern A. Zeeb if (!ret) { 2929bfcc09ddSBjoern A. Zeeb if (stop) 2930bfcc09ddSBjoern A. Zeeb fwrt->trans->dbg.rec_on = false; 2931bfcc09ddSBjoern A. Zeeb else 2932bfcc09ddSBjoern A. Zeeb iwl_fw_set_dbg_rec_on(fwrt); 2933bfcc09ddSBjoern A. Zeeb } 2934bfcc09ddSBjoern A. Zeeb #endif 2935bfcc09ddSBjoern A. Zeeb } 2936bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_fw_dbg_stop_restart_recording); 2937