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