// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
#include <linux/devcoredump.h>
#include "iwl-drv.h"
#include "runtime.h"
#include "dbg.h"
#include "debugfs.h"
#include "iwl-io.h"
#include "iwl-prph.h"
#include "iwl-csr.h"
#include "iwl-fh.h"
/**
* struct iwl_fw_dump_ptrs - set of pointers needed for the fw-error-dump
*
* @fwrt_ptr: pointer to the buffer coming from fwrt
* @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the
* transport's data.
* @fwrt_len: length of the valid data in fwrt_ptr
*/
struct iwl_fw_dump_ptrs {
struct iwl_trans_dump_data *trans_ptr;
void *fwrt_ptr;
u32 fwrt_len;
};
#define RADIO_REG_MAX_READ 0x2ad
static void iwl_read_radio_regs(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data)
{
u8 *pos = (void *)(*dump_data)->data;
int i;
IWL_DEBUG_INFO(fwrt, "WRT radio registers dump\n" );
if (!iwl_trans_grab_nic_access(fwrt->trans))
return ;
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG);
(*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ);
for (i = 0; i < RADIO_REG_MAX_READ; i++) {
u32 rd_cmd = RADIO_RSP_RD_CMD;
rd_cmd |= i << RADIO_RSP_ADDR_POS;
iwl_write_prph_no_grab(fwrt->trans, RSP_RADIO_CMD, rd_cmd);
*pos = (u8)iwl_read_prph_no_grab(fwrt->trans, RSP_RADIO_RDDAT);
pos++;
}
*dump_data = iwl_fw_error_next_data(*dump_data);
iwl_trans_release_nic_access(fwrt->trans);
}
static void iwl_fwrt_dump_rxf(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data,
int size, u32 offset, int fifo_num)
{
struct iwl_fw_error_dump_fifo *fifo_hdr;
u32 *fifo_data;
u32 fifo_len;
int i;
fifo_hdr = (void *)(*dump_data)->data;
fifo_data = (void *)fifo_hdr->data;
fifo_len = size;
/* No need to try to read the data if the length is 0 */
if (fifo_len == 0)
return ;
/* Add a TLV for the RXF */
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
(*dump_data)->len = cpu_to_le32(fifo_len + sizeof (*fifo_hdr));
fifo_hdr->fifo_num = cpu_to_le32(fifo_num);
fifo_hdr->available_bytes =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
RXF_RD_D_SPACE + offset));
fifo_hdr->wr_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
RXF_RD_WR_PTR + offset));
fifo_hdr->rd_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
RXF_RD_RD_PTR + offset));
fifo_hdr->fence_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
RXF_RD_FENCE_PTR + offset));
fifo_hdr->fence_mode =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
RXF_SET_FENCE_MODE + offset));
/* Lock fence */
iwl_trans_write_prph(fwrt->trans, RXF_SET_FENCE_MODE + offset, 0x1);
/* Set fence pointer to the same place like WR pointer */
iwl_trans_write_prph(fwrt->trans, RXF_LD_WR2FENCE + offset, 0x1);
/* Set fence offset */
iwl_trans_write_prph(fwrt->trans,
RXF_LD_FENCE_OFFSET_ADDR + offset, 0x0);
/* Read FIFO */
fifo_len /= sizeof (u32); /* Size in DWORDS */
for (i = 0; i < fifo_len; i++)
fifo_data[i] = iwl_trans_read_prph(fwrt->trans,
RXF_FIFO_RD_FENCE_INC +
offset);
*dump_data = iwl_fw_error_next_data(*dump_data);
}
static void iwl_fwrt_dump_txf(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data,
int size, u32 offset, int fifo_num)
{
struct iwl_fw_error_dump_fifo *fifo_hdr;
u32 *fifo_data;
u32 fifo_len;
int i;
fifo_hdr = (void *)(*dump_data)->data;
fifo_data = (void *)fifo_hdr->data;
fifo_len = size;
/* No need to try to read the data if the length is 0 */
if (fifo_len == 0)
return ;
/* Add a TLV for the FIFO */
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF);
(*dump_data)->len = cpu_to_le32(fifo_len + sizeof (*fifo_hdr));
fifo_hdr->fifo_num = cpu_to_le32(fifo_num);
fifo_hdr->available_bytes =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_FIFO_ITEM_CNT + offset));
fifo_hdr->wr_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_WR_PTR + offset));
fifo_hdr->rd_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_RD_PTR + offset));
fifo_hdr->fence_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_FENCE_PTR + offset));
fifo_hdr->fence_mode =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_LOCK_FENCE + offset));
/* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
iwl_trans_write_prph(fwrt->trans, TXF_READ_MODIFY_ADDR + offset,
TXF_WR_PTR + offset);
/* Dummy-read to advance the read pointer to the head */
iwl_trans_read_prph(fwrt->trans, TXF_READ_MODIFY_DATA + offset);
/* Read FIFO */
for (i = 0; i < fifo_len / sizeof (u32); i++)
fifo_data[i] = iwl_trans_read_prph(fwrt->trans,
TXF_READ_MODIFY_DATA +
offset);
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf)
fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx,
fifo_data, fifo_len);
*dump_data = iwl_fw_error_next_data(*dump_data);
}
static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data)
{
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
IWL_DEBUG_INFO(fwrt, "WRT RX FIFO dump\n" );
if (!iwl_trans_grab_nic_access(fwrt->trans))
return ;
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF)) {
/* Pull RXF1 */
iwl_fwrt_dump_rxf(fwrt, dump_data,
cfg->lmac[0].rxfifo1_size, 0, 0);
/* Pull RXF2 */
iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size,
RXF_DIFF_FROM_PREV +
fwrt->trans->mac_cfg->umac_prph_offset, 1);
/* Pull LMAC2 RXF1 */
if (fwrt->smem_cfg.num_lmacs > 1)
iwl_fwrt_dump_rxf(fwrt, dump_data,
cfg->lmac[1].rxfifo1_size,
LMAC2_PRPH_OFFSET, 2);
}
iwl_trans_release_nic_access(fwrt->trans);
}
static void iwl_fw_dump_txf(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data)
{
struct iwl_fw_error_dump_fifo *fifo_hdr;
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
u32 *fifo_data;
u32 fifo_len;
int i, j;
IWL_DEBUG_INFO(fwrt, "WRT TX FIFO dump\n" );
if (!iwl_trans_grab_nic_access(fwrt->trans))
return ;
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF)) {
/* Pull TXF data from LMAC1 */
for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries; i++) {
/* Mark the number of TXF we're pulling now */
iwl_trans_write_prph(fwrt->trans, TXF_LARC_NUM, i);
iwl_fwrt_dump_txf(fwrt, dump_data,
cfg->lmac[0].txfifo_size[i], 0, i);
}
/* Pull TXF data from LMAC2 */
if (fwrt->smem_cfg.num_lmacs > 1) {
for (i = 0; i < fwrt->smem_cfg.num_txfifo_entries;
i++) {
/* Mark the number of TXF we're pulling now */
iwl_trans_write_prph(fwrt->trans,
TXF_LARC_NUM +
LMAC2_PRPH_OFFSET, i);
iwl_fwrt_dump_txf(fwrt, dump_data,
cfg->lmac[1].txfifo_size[i],
LMAC2_PRPH_OFFSET,
i + cfg->num_txfifo_entries);
}
}
}
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)) {
/* Pull UMAC internal TXF data from all TXFs */
for (i = 0;
i < ARRAY_SIZE(fwrt->smem_cfg.internal_txfifo_size);
i++) {
fifo_hdr = (void *)(*dump_data)->data;
fifo_data = (void *)fifo_hdr->data;
fifo_len = fwrt->smem_cfg.internal_txfifo_size[i];
/* No need to try to read the data if the length is 0 */
if (fifo_len == 0)
continue ;
/* Add a TLV for the internal FIFOs */
(*dump_data)->type =
cpu_to_le32(IWL_FW_ERROR_DUMP_INTERNAL_TXF);
(*dump_data)->len =
cpu_to_le32(fifo_len + sizeof (*fifo_hdr));
fifo_hdr->fifo_num = cpu_to_le32(i);
/* Mark the number of TXF we're pulling now */
iwl_trans_write_prph(fwrt->trans, TXF_CPU2_NUM, i +
fwrt->smem_cfg.num_txfifo_entries);
fifo_hdr->available_bytes =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_CPU2_FIFO_ITEM_CNT));
fifo_hdr->wr_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_CPU2_WR_PTR));
fifo_hdr->rd_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_CPU2_RD_PTR));
fifo_hdr->fence_ptr =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_CPU2_FENCE_PTR));
fifo_hdr->fence_mode =
cpu_to_le32(iwl_trans_read_prph(fwrt->trans,
TXF_CPU2_LOCK_FENCE));
/* Set TXF_CPU2_READ_MODIFY_ADDR to TXF_CPU2_WR_PTR */
iwl_trans_write_prph(fwrt->trans,
TXF_CPU2_READ_MODIFY_ADDR,
TXF_CPU2_WR_PTR);
/* Dummy-read to advance the read pointer to head */
iwl_trans_read_prph(fwrt->trans,
TXF_CPU2_READ_MODIFY_DATA);
/* Read FIFO */
fifo_len /= sizeof (u32); /* Size in DWORDS */
for (j = 0; j < fifo_len; j++)
fifo_data[j] =
iwl_trans_read_prph(fwrt->trans,
TXF_CPU2_READ_MODIFY_DATA);
*dump_data = iwl_fw_error_next_data(*dump_data);
}
}
iwl_trans_release_nic_access(fwrt->trans);
}
struct iwl_prph_range {
u32 start, end;
};
static const struct iwl_prph_range iwl_prph_dump_addr_comm[] = {
{ .start = 0x00a00000, .end = 0x00a00000 },
{ .start = 0x00a0000c, .end = 0x00a00024 },
{ .start = 0x00a0002c, .end = 0x00a0003c },
{ .start = 0x00a00410, .end = 0x00a00418 },
{ .start = 0x00a00420, .end = 0x00a00420 },
{ .start = 0x00a00428, .end = 0x00a00428 },
{ .start = 0x00a00430, .end = 0x00a0043c },
{ .start = 0x00a00444, .end = 0x00a00444 },
{ .start = 0x00a004c0, .end = 0x00a004cc },
{ .start = 0x00a004d8, .end = 0x00a004d8 },
{ .start = 0x00a004e0, .end = 0x00a004f0 },
{ .start = 0x00a00840, .end = 0x00a00840 },
{ .start = 0x00a00850, .end = 0x00a00858 },
{ .start = 0x00a01004, .end = 0x00a01008 },
{ .start = 0x00a01010, .end = 0x00a01010 },
{ .start = 0x00a01018, .end = 0x00a01018 },
{ .start = 0x00a01024, .end = 0x00a01024 },
{ .start = 0x00a0102c, .end = 0x00a01034 },
{ .start = 0x00a0103c, .end = 0x00a01040 },
{ .start = 0x00a01048, .end = 0x00a01094 },
{ .start = 0x00a01c00, .end = 0x00a01c20 },
{ .start = 0x00a01c58, .end = 0x00a01c58 },
{ .start = 0x00a01c7c, .end = 0x00a01c7c },
{ .start = 0x00a01c28, .end = 0x00a01c54 },
{ .start = 0x00a01c5c, .end = 0x00a01c5c },
{ .start = 0x00a01c60, .end = 0x00a01cdc },
{ .start = 0x00a01ce0, .end = 0x00a01d0c },
{ .start = 0x00a01d18, .end = 0x00a01d20 },
{ .start = 0x00a01d2c, .end = 0x00a01d30 },
{ .start = 0x00a01d40, .end = 0x00a01d5c },
{ .start = 0x00a01d80, .end = 0x00a01d80 },
{ .start = 0x00a01d98, .end = 0x00a01d9c },
{ .start = 0x00a01da8, .end = 0x00a01da8 },
{ .start = 0x00a01db8, .end = 0x00a01df4 },
{ .start = 0x00a01dc0, .end = 0x00a01dfc },
{ .start = 0x00a01e00, .end = 0x00a01e2c },
{ .start = 0x00a01e40, .end = 0x00a01e60 },
{ .start = 0x00a01e68, .end = 0x00a01e6c },
{ .start = 0x00a01e74, .end = 0x00a01e74 },
{ .start = 0x00a01e84, .end = 0x00a01e90 },
{ .start = 0x00a01e9c, .end = 0x00a01ec4 },
{ .start = 0x00a01ed0, .end = 0x00a01ee0 },
{ .start = 0x00a01f00, .end = 0x00a01f1c },
{ .start = 0x00a01f44, .end = 0x00a01ffc },
{ .start = 0x00a02000, .end = 0x00a02048 },
{ .start = 0x00a02068, .end = 0x00a020f0 },
{ .start = 0x00a02100, .end = 0x00a02118 },
{ .start = 0x00a02140, .end = 0x00a0214c },
{ .start = 0x00a02168, .end = 0x00a0218c },
{ .start = 0x00a021c0, .end = 0x00a021c0 },
{ .start = 0x00a02400, .end = 0x00a02410 },
{ .start = 0x00a02418, .end = 0x00a02420 },
{ .start = 0x00a02428, .end = 0x00a0242c },
{ .start = 0x00a02434, .end = 0x00a02434 },
{ .start = 0x00a02440, .end = 0x00a02460 },
{ .start = 0x00a02468, .end = 0x00a024b0 },
{ .start = 0x00a024c8, .end = 0x00a024cc },
{ .start = 0x00a02500, .end = 0x00a02504 },
{ .start = 0x00a0250c, .end = 0x00a02510 },
{ .start = 0x00a02540, .end = 0x00a02554 },
{ .start = 0x00a02580, .end = 0x00a025f4 },
{ .start = 0x00a02600, .end = 0x00a0260c },
{ .start = 0x00a02648, .end = 0x00a02650 },
{ .start = 0x00a02680, .end = 0x00a02680 },
{ .start = 0x00a026c0, .end = 0x00a026d0 },
{ .start = 0x00a02700, .end = 0x00a0270c },
{ .start = 0x00a02804, .end = 0x00a02804 },
{ .start = 0x00a02818, .end = 0x00a0281c },
{ .start = 0x00a02c00, .end = 0x00a02db4 },
{ .start = 0x00a02df4, .end = 0x00a02fb0 },
{ .start = 0x00a03000, .end = 0x00a03014 },
{ .start = 0x00a0301c, .end = 0x00a0302c },
{ .start = 0x00a03034, .end = 0x00a03038 },
{ .start = 0x00a03040, .end = 0x00a03048 },
{ .start = 0x00a03060, .end = 0x00a03068 },
{ .start = 0x00a03070, .end = 0x00a03074 },
{ .start = 0x00a0307c, .end = 0x00a0307c },
{ .start = 0x00a03080, .end = 0x00a03084 },
{ .start = 0x00a0308c, .end = 0x00a03090 },
{ .start = 0x00a03098, .end = 0x00a03098 },
{ .start = 0x00a030a0, .end = 0x00a030a0 },
{ .start = 0x00a030a8, .end = 0x00a030b4 },
{ .start = 0x00a030bc, .end = 0x00a030bc },
{ .start = 0x00a030c0, .end = 0x00a0312c },
{ .start = 0x00a03c00, .end = 0x00a03c5c },
{ .start = 0x00a04400, .end = 0x00a04454 },
{ .start = 0x00a04460, .end = 0x00a04474 },
{ .start = 0x00a044c0, .end = 0x00a044ec },
{ .start = 0x00a04500, .end = 0x00a04504 },
{ .start = 0x00a04510, .end = 0x00a04538 },
{ .start = 0x00a04540, .end = 0x00a04548 },
{ .start = 0x00a04560, .end = 0x00a0457c },
{ .start = 0x00a04590, .end = 0x00a04598 },
{ .start = 0x00a045c0, .end = 0x00a045f4 },
};
static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {
{ .start = 0x00a05c00, .end = 0x00a05c18 },
{ .start = 0x00a05400, .end = 0x00a056e8 },
{ .start = 0x00a08000, .end = 0x00a098bc },
{ .start = 0x00a02400, .end = 0x00a02758 },
{ .start = 0x00a04764, .end = 0x00a0476c },
{ .start = 0x00a04770, .end = 0x00a04774 },
{ .start = 0x00a04620, .end = 0x00a04624 },
};
static const struct iwl_prph_range iwl_prph_dump_addr_22000[] = {
{ .start = 0x00a00000, .end = 0x00a00000 },
{ .start = 0x00a0000c, .end = 0x00a00024 },
{ .start = 0x00a0002c, .end = 0x00a00034 },
{ .start = 0x00a0003c, .end = 0x00a0003c },
{ .start = 0x00a00410, .end = 0x00a00418 },
{ .start = 0x00a00420, .end = 0x00a00420 },
{ .start = 0x00a00428, .end = 0x00a00428 },
{ .start = 0x00a00430, .end = 0x00a0043c },
{ .start = 0x00a00444, .end = 0x00a00444 },
{ .start = 0x00a00840, .end = 0x00a00840 },
{ .start = 0x00a00850, .end = 0x00a00858 },
{ .start = 0x00a01004, .end = 0x00a01008 },
{ .start = 0x00a01010, .end = 0x00a01010 },
{ .start = 0x00a01018, .end = 0x00a01018 },
{ .start = 0x00a01024, .end = 0x00a01024 },
{ .start = 0x00a0102c, .end = 0x00a01034 },
{ .start = 0x00a0103c, .end = 0x00a01040 },
{ .start = 0x00a01048, .end = 0x00a01050 },
{ .start = 0x00a01058, .end = 0x00a01058 },
{ .start = 0x00a01060, .end = 0x00a01070 },
{ .start = 0x00a0108c, .end = 0x00a0108c },
{ .start = 0x00a01c20, .end = 0x00a01c28 },
{ .start = 0x00a01d10, .end = 0x00a01d10 },
{ .start = 0x00a01e28, .end = 0x00a01e2c },
{ .start = 0x00a01e60, .end = 0x00a01e60 },
{ .start = 0x00a01e80, .end = 0x00a01e80 },
{ .start = 0x00a01ea0, .end = 0x00a01ea0 },
{ .start = 0x00a02000, .end = 0x00a0201c },
{ .start = 0x00a02024, .end = 0x00a02024 },
{ .start = 0x00a02040, .end = 0x00a02048 },
{ .start = 0x00a020c0, .end = 0x00a020e0 },
{ .start = 0x00a02400, .end = 0x00a02404 },
{ .start = 0x00a0240c, .end = 0x00a02414 },
{ .start = 0x00a0241c, .end = 0x00a0243c },
{ .start = 0x00a02448, .end = 0x00a024bc },
{ .start = 0x00a024c4, .end = 0x00a024cc },
{ .start = 0x00a02508, .end = 0x00a02508 },
{ .start = 0x00a02510, .end = 0x00a02514 },
{ .start = 0x00a0251c, .end = 0x00a0251c },
{ .start = 0x00a0252c, .end = 0x00a0255c },
{ .start = 0x00a02564, .end = 0x00a025a0 },
{ .start = 0x00a025a8, .end = 0x00a025b4 },
{ .start = 0x00a025c0, .end = 0x00a025c0 },
{ .start = 0x00a025e8, .end = 0x00a025f4 },
{ .start = 0x00a02c08, .end = 0x00a02c18 },
{ .start = 0x00a02c2c, .end = 0x00a02c38 },
{ .start = 0x00a02c68, .end = 0x00a02c78 },
{ .start = 0x00a03000, .end = 0x00a03000 },
{ .start = 0x00a03010, .end = 0x00a03014 },
{ .start = 0x00a0301c, .end = 0x00a0302c },
{ .start = 0x00a03034, .end = 0x00a03038 },
{ .start = 0x00a03040, .end = 0x00a03044 },
{ .start = 0x00a03060, .end = 0x00a03068 },
{ .start = 0x00a03070, .end = 0x00a03070 },
{ .start = 0x00a0307c, .end = 0x00a03084 },
{ .start = 0x00a0308c, .end = 0x00a03090 },
{ .start = 0x00a03098, .end = 0x00a03098 },
{ .start = 0x00a030a0, .end = 0x00a030a0 },
{ .start = 0x00a030a8, .end = 0x00a030b4 },
{ .start = 0x00a030bc, .end = 0x00a030c0 },
{ .start = 0x00a030c8, .end = 0x00a030f4 },
{ .start = 0x00a03100, .end = 0x00a0312c },
{ .start = 0x00a03c00, .end = 0x00a03c5c },
{ .start = 0x00a04400, .end = 0x00a04454 },
{ .start = 0x00a04460, .end = 0x00a04474 },
{ .start = 0x00a044c0, .end = 0x00a044ec },
{ .start = 0x00a04500, .end = 0x00a04504 },
{ .start = 0x00a04510, .end = 0x00a04538 },
{ .start = 0x00a04540, .end = 0x00a04548 },
{ .start = 0x00a04560, .end = 0x00a04560 },
{ .start = 0x00a04570, .end = 0x00a0457c },
{ .start = 0x00a04590, .end = 0x00a04590 },
{ .start = 0x00a04598, .end = 0x00a04598 },
{ .start = 0x00a045c0, .end = 0x00a045f4 },
{ .start = 0x00a05c18, .end = 0x00a05c1c },
{ .start = 0x00a0c000, .end = 0x00a0c018 },
{ .start = 0x00a0c020, .end = 0x00a0c028 },
{ .start = 0x00a0c038, .end = 0x00a0c094 },
{ .start = 0x00a0c0c0, .end = 0x00a0c104 },
{ .start = 0x00a0c10c, .end = 0x00a0c118 },
{ .start = 0x00a0c150, .end = 0x00a0c174 },
{ .start = 0x00a0c17c, .end = 0x00a0c188 },
{ .start = 0x00a0c190, .end = 0x00a0c198 },
{ .start = 0x00a0c1a0, .end = 0x00a0c1a8 },
{ .start = 0x00a0c1b0, .end = 0x00a0c1b8 },
};
static const struct iwl_prph_range iwl_prph_dump_addr_ax210[] = {
{ .start = 0x00d03c00, .end = 0x00d03c64 },
{ .start = 0x00d05c18, .end = 0x00d05c1c },
{ .start = 0x00d0c000, .end = 0x00d0c174 },
};
static void iwl_read_prph_block(struct iwl_trans *trans, u32 start,
u32 len_bytes, __le32 *data)
{
u32 i;
for (i = 0; i < len_bytes; i += 4)
*data++ = cpu_to_le32(iwl_read_prph_no_grab(trans, start + i));
}
static void iwl_dump_prph(struct iwl_fw_runtime *fwrt,
const struct iwl_prph_range *iwl_prph_dump_addr,
u32 range_len, void *ptr)
{
struct iwl_fw_error_dump_prph *prph;
struct iwl_trans *trans = fwrt->trans;
struct iwl_fw_error_dump_data **data =
(struct iwl_fw_error_dump_data **)ptr;
u32 i;
if (!data)
return ;
IWL_DEBUG_INFO(trans, "WRT PRPH dump\n" );
if (!iwl_trans_grab_nic_access(trans))
return ;
for (i = 0; i < range_len; i++) {
/* The range includes both boundaries */
int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
iwl_prph_dump_addr[i].start + 4;
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH);
(*data)->len = cpu_to_le32(sizeof (*prph) +
num_bytes_in_chunk);
prph = (void *)(*data)->data;
prph->prph_start = cpu_to_le32(iwl_prph_dump_addr[i].start);
iwl_read_prph_block(trans, iwl_prph_dump_addr[i].start,
/* our range is inclusive, hence + 4 */
iwl_prph_dump_addr[i].end -
iwl_prph_dump_addr[i].start + 4,
(void *)prph->data);
*data = iwl_fw_error_next_data(*data);
}
iwl_trans_release_nic_access(trans);
}
/*
* alloc_sgtable - allocates (chained) scatterlist in the given size,
* fills it with pages and returns it
* @size: the size (in bytes) of the table
*/
static struct scatterlist *alloc_sgtable(ssize_t size)
{
struct scatterlist *result = NULL, *prev;
int nents, i, n_prev;
nents = DIV_ROUND_UP(size, PAGE_SIZE);
#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof (*result))
/*
* We need an additional entry for table chaining,
* this ensures the loop can finish i.e. we can
* fit at least two entries per page (obviously,
* many more really fit.)
*/
BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2);
while (nents > 0) {
struct scatterlist *new , *iter;
int n_fill, n_alloc;
if (nents <= N_ENTRIES_PER_PAGE) {
/* last needed table */
n_fill = nents;
n_alloc = nents;
nents = 0;
} else {
/* fill a page with entries */
n_alloc = N_ENTRIES_PER_PAGE;
/* reserve one for chaining */
n_fill = n_alloc - 1;
nents -= n_fill;
}
new = kcalloc(n_alloc, sizeof (*new ), GFP_KERNEL);
if (!new ) {
if (result)
_devcd_free_sgtable(result);
return NULL;
}
sg_init_table(new , n_alloc);
if (!result)
result = new ;
else
sg_chain(prev, n_prev, new );
prev = new ;
n_prev = n_alloc;
for_each_sg(new , iter, n_fill, i) {
struct page *new_page = alloc_page(GFP_KERNEL);
if (!new_page) {
_devcd_free_sgtable(result);
return NULL;
}
sg_set_page(iter, new_page, PAGE_SIZE, 0);
}
}
return result;
}
static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt,
const struct iwl_prph_range *iwl_prph_dump_addr,
u32 range_len, void *ptr)
{
u32 *prph_len = (u32 *)ptr;
int i, num_bytes_in_chunk;
if (!prph_len)
return ;
for (i = 0; i < range_len; i++) {
/* The range includes both boundaries */
num_bytes_in_chunk =
iwl_prph_dump_addr[i].end -
iwl_prph_dump_addr[i].start + 4;
*prph_len += sizeof (struct iwl_fw_error_dump_data) +
sizeof (struct iwl_fw_error_dump_prph) +
num_bytes_in_chunk;
}
}
static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr,
void (*handler)(struct iwl_fw_runtime *,
const struct iwl_prph_range *,
u32, void *))
{
u32 range_len;
if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210);
handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr);
} else if (fwrt->trans->mac_cfg->device_family >=
IWL_DEVICE_FAMILY_22000) {
range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000);
handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr);
} else {
range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm);
handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr);
if (fwrt->trans->mac_cfg->mq_rx_supported) {
range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000);
handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr);
}
}
}
static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **dump_data,
u32 len, u32 ofs, u32 type)
{
struct iwl_fw_error_dump_mem *dump_mem;
if (!len)
return ;
(*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
(*dump_data)->len = cpu_to_le32(len + sizeof (*dump_mem));
dump_mem = (void *)(*dump_data)->data;
dump_mem->type = cpu_to_le32(type);
dump_mem->offset = cpu_to_le32(ofs);
iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len);
*dump_data = iwl_fw_error_next_data(*dump_data);
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, ofs,
dump_mem->data, len);
IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n" , dump_mem->type);
}
#define ADD_LEN(len, item_len, const_len) \
do {size_t item = item_len; len += (!!item) * const_len + item; } \
while (0)
static int iwl_fw_rxf_len(struct iwl_fw_runtime *fwrt,
struct iwl_fwrt_shared_mem_cfg *mem_cfg)
{
size_t hdr_len = sizeof (struct iwl_fw_error_dump_data) +
sizeof (struct iwl_fw_error_dump_fifo);
u32 fifo_len = 0;
int i;
if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RXF))
return 0;
/* Count RXF2 size */
ADD_LEN(fifo_len, mem_cfg->rxfifo2_size, hdr_len);
/* Count RXF1 sizes */
if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC))
mem_cfg->num_lmacs = MAX_NUM_LMAC;
for (i = 0; i < mem_cfg->num_lmacs; i++)
ADD_LEN(fifo_len, mem_cfg->lmac[i].rxfifo1_size, hdr_len);
return fifo_len;
}
static int iwl_fw_txf_len(struct iwl_fw_runtime *fwrt,
struct iwl_fwrt_shared_mem_cfg *mem_cfg)
{
size_t hdr_len = sizeof (struct iwl_fw_error_dump_data) +
sizeof (struct iwl_fw_error_dump_fifo);
u32 fifo_len = 0;
int i;
if (!iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_TXF))
goto dump_internal_txf;
/* Count TXF sizes */
if (WARN_ON(mem_cfg->num_lmacs > MAX_NUM_LMAC))
mem_cfg->num_lmacs = MAX_NUM_LMAC;
for (i = 0; i < mem_cfg->num_lmacs; i++) {
int j;
for (j = 0; j < mem_cfg->num_txfifo_entries; j++)
ADD_LEN(fifo_len, mem_cfg->lmac[i].txfifo_size[j],
hdr_len);
}
dump_internal_txf:
if (!(iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_INTERNAL_TXF) &&
fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG)))
goto out;
for (i = 0; i < ARRAY_SIZE(mem_cfg->internal_txfifo_size); i++)
ADD_LEN(fifo_len, mem_cfg->internal_txfifo_size[i], hdr_len);
out:
return fifo_len;
}
static void iwl_dump_paging(struct iwl_fw_runtime *fwrt,
struct iwl_fw_error_dump_data **data)
{
int i;
IWL_DEBUG_INFO(fwrt, "WRT paging dump\n" );
for (i = 1; i < fwrt->num_of_paging_blk + 1; i++) {
struct iwl_fw_error_dump_paging *paging;
struct page *pages =
fwrt->fw_paging_db[i].fw_paging_block;
dma_addr_t addr = fwrt->fw_paging_db[i].fw_paging_phys;
(*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING);
(*data)->len = cpu_to_le32(sizeof (*paging) +
PAGING_BLOCK_SIZE);
paging = (void *)(*data)->data;
paging->index = cpu_to_le32(i);
dma_sync_single_for_cpu(fwrt->trans->dev, addr,
PAGING_BLOCK_SIZE,
DMA_BIDIRECTIONAL);
memcpy(paging->data, page_address(pages),
PAGING_BLOCK_SIZE);
dma_sync_single_for_device(fwrt->trans->dev, addr,
PAGING_BLOCK_SIZE,
DMA_BIDIRECTIONAL);
(*data) = iwl_fw_error_next_data(*data);
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx,
fwrt->fw_paging_db[i].fw_offs,
paging->data,
PAGING_BLOCK_SIZE);
}
}
static struct iwl_fw_error_dump_file *
iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt,
struct iwl_fw_dump_ptrs *fw_error_dump,
struct iwl_fwrt_dump_data *data)
{
struct iwl_fw_error_dump_file *dump_file;
struct iwl_fw_error_dump_data *dump_data;
struct iwl_fw_error_dump_info *dump_info;
struct iwl_fw_error_dump_smem_cfg *dump_smem_cfg;
struct iwl_fw_error_dump_trigger_desc *dump_trig;
u32 sram_len, sram_ofs;
const struct iwl_fw_dbg_mem_seg_tlv *fw_mem = fwrt->fw->dbg.mem_tlv;
struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg;
u32 file_len, fifo_len = 0, prph_len = 0, radio_len = 0;
u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->mac_cfg->base->smem_len;
u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ?
0 : fwrt->trans->cfg->dccm2_len;
int i;
/* SRAM - include stack CCM if driver knows the values for it */
if (!fwrt->trans->cfg->dccm_offset ||
!fwrt->trans->cfg->dccm_len) {
const struct fw_img *img;
if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX)
return NULL;
img = &fwrt->fw->img[fwrt->cur_fw_img];
sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
} else {
sram_ofs = fwrt->trans->cfg->dccm_offset;
sram_len = fwrt->trans->cfg->dccm_len;
}
/* reading RXF/TXF sizes */
if (test_bit(STATUS_FW_ERROR, &fwrt->trans->status)) {
fifo_len = iwl_fw_rxf_len(fwrt, mem_cfg);
fifo_len += iwl_fw_txf_len(fwrt, mem_cfg);
/* Make room for PRPH registers */
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PRPH))
iwl_fw_prph_handler(fwrt, &prph_len,
iwl_fw_get_prph_len);
if (fwrt->trans->mac_cfg->device_family ==
IWL_DEVICE_FAMILY_7000 &&
iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG))
radio_len = sizeof (*dump_data) + RADIO_REG_MAX_READ;
}
file_len = sizeof (*dump_file) + fifo_len + prph_len + radio_len;
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO))
file_len += sizeof (*dump_data) + sizeof (*dump_info);
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG))
file_len += sizeof (*dump_data) + sizeof (*dump_smem_cfg);
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
size_t hdr_len = sizeof (*dump_data) +
sizeof (struct iwl_fw_error_dump_mem);
/* Dump SRAM only if no mem_tlvs */
if (!fwrt->fw->dbg.n_mem_tlv)
ADD_LEN(file_len, sram_len, hdr_len);
/* Make room for all mem types that exist */
ADD_LEN(file_len, smem_len, hdr_len);
ADD_LEN(file_len, sram2_len, hdr_len);
for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++)
ADD_LEN(file_len, le32_to_cpu(fw_mem[i].len), hdr_len);
}
/* Make room for fw's virtual image pages, if it exists */
if (iwl_fw_dbg_is_paging_enabled(fwrt))
file_len += fwrt->num_of_paging_blk *
(sizeof (*dump_data) +
sizeof (struct iwl_fw_error_dump_paging) +
PAGING_BLOCK_SIZE);
if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) {
file_len += sizeof (*dump_data) +
fwrt->trans->mac_cfg->base->d3_debug_data_length * 2;
}
/* If we only want a monitor dump, reset the file length */
if (data->monitor_only) {
file_len = sizeof (*dump_file) + sizeof (*dump_data) * 2 +
sizeof (*dump_info) + sizeof (*dump_smem_cfg);
}
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
data->desc)
file_len += sizeof (*dump_data) + sizeof (*dump_trig) +
data->desc->len;
dump_file = vzalloc(file_len);
if (!dump_file)
return NULL;
fw_error_dump->fwrt_ptr = dump_file;
dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
dump_data = (void *)dump_file->data;
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_DEV_FW_INFO)) {
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
dump_data->len = cpu_to_le32(sizeof (*dump_info));
dump_info = (void *)dump_data->data;
dump_info->hw_type =
cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev));
dump_info->hw_step =
cpu_to_le32(fwrt->trans->info.hw_rev_step);
memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable,
sizeof (dump_info->fw_human_readable));
strscpy_pad(dump_info->dev_human_readable,
fwrt->trans->info.name,
sizeof (dump_info->dev_human_readable));
strscpy_pad(dump_info->bus_human_readable, fwrt->dev->bus->name,
sizeof (dump_info->bus_human_readable));
dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs;
dump_info->lmac_err_id[0] =
cpu_to_le32(fwrt->dump.lmac_err_id[0]);
if (fwrt->smem_cfg.num_lmacs > 1)
dump_info->lmac_err_id[1] =
cpu_to_le32(fwrt->dump.lmac_err_id[1]);
dump_info->umac_err_id = cpu_to_le32(fwrt->dump.umac_err_id);
dump_data = iwl_fw_error_next_data(dump_data);
}
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM_CFG)) {
/* Dump shared memory configuration */
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_CFG);
dump_data->len = cpu_to_le32(sizeof (*dump_smem_cfg));
dump_smem_cfg = (void *)dump_data->data;
dump_smem_cfg->num_lmacs = cpu_to_le32(mem_cfg->num_lmacs);
dump_smem_cfg->num_txfifo_entries =
cpu_to_le32(mem_cfg->num_txfifo_entries);
for (i = 0; i < MAX_NUM_LMAC; i++) {
int j;
u32 *txf_size = mem_cfg->lmac[i].txfifo_size;
for (j = 0; j < TX_FIFO_MAX_NUM; j++)
dump_smem_cfg->lmac[i].txfifo_size[j] =
cpu_to_le32(txf_size[j]);
dump_smem_cfg->lmac[i].rxfifo1_size =
cpu_to_le32(mem_cfg->lmac[i].rxfifo1_size);
}
dump_smem_cfg->rxfifo2_size =
cpu_to_le32(mem_cfg->rxfifo2_size);
dump_smem_cfg->internal_txfifo_addr =
cpu_to_le32(mem_cfg->internal_txfifo_addr);
for (i = 0; i < TX_FIFO_INTERNAL_MAX_NUM; i++) {
dump_smem_cfg->internal_txfifo_size[i] =
cpu_to_le32(mem_cfg->internal_txfifo_size[i]);
}
dump_data = iwl_fw_error_next_data(dump_data);
}
/* We only dump the FIFOs if the FW is in error state */
if (fifo_len) {
iwl_fw_dump_rxf(fwrt, &dump_data);
iwl_fw_dump_txf(fwrt, &dump_data);
}
if (radio_len)
iwl_read_radio_regs(fwrt, &dump_data);
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_ERROR_INFO) &&
data->desc) {
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
dump_data->len = cpu_to_le32(sizeof (*dump_trig) +
data->desc->len);
dump_trig = (void *)dump_data->data;
memcpy(dump_trig, &data->desc->trig_desc,
sizeof (*dump_trig) + data->desc->len);
dump_data = iwl_fw_error_next_data(dump_data);
}
/* In case we only want monitor dump, skip to dump trasport data */
if (data->monitor_only)
goto out;
if (iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_MEM)) {
const struct iwl_fw_dbg_mem_seg_tlv *fw_dbg_mem =
fwrt->fw->dbg.mem_tlv;
if (!fwrt->fw->dbg.n_mem_tlv)
iwl_fw_dump_mem(fwrt, &dump_data, sram_len, sram_ofs,
IWL_FW_ERROR_DUMP_MEM_SRAM);
for (i = 0; i < fwrt->fw->dbg.n_mem_tlv; i++) {
u32 len = le32_to_cpu(fw_dbg_mem[i].len);
u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs);
iwl_fw_dump_mem(fwrt, &dump_data, len, ofs,
le32_to_cpu(fw_dbg_mem[i].data_type));
}
iwl_fw_dump_mem(fwrt, &dump_data, smem_len,
fwrt->trans->mac_cfg->base->smem_offset,
IWL_FW_ERROR_DUMP_MEM_SMEM);
iwl_fw_dump_mem(fwrt, &dump_data, sram2_len,
fwrt->trans->cfg->dccm2_offset,
IWL_FW_ERROR_DUMP_MEM_SRAM);
}
if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) {
u32 addr = fwrt->trans->mac_cfg->base->d3_debug_data_base_addr;
size_t data_size = fwrt->trans->mac_cfg->base->d3_debug_data_length;
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA);
dump_data->len = cpu_to_le32(data_size * 2);
memcpy(dump_data->data, fwrt->dump.d3_debug_data, data_size);
kfree(fwrt->dump.d3_debug_data);
fwrt->dump.d3_debug_data = NULL;
iwl_trans_read_mem_bytes(fwrt->trans, addr,
dump_data->data + data_size,
data_size);
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem)
fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, addr,
dump_data->data + data_size,
data_size);
dump_data = iwl_fw_error_next_data(dump_data);
}
/* Dump fw's virtual image */
if (iwl_fw_dbg_is_paging_enabled(fwrt))
iwl_dump_paging(fwrt, &dump_data);
if (prph_len)
iwl_fw_prph_handler(fwrt, &dump_data, iwl_dump_prph);
out:
dump_file->file_len = cpu_to_le32(file_len);
return dump_file;
}
/**
* struct iwl_dump_ini_region_data - region data
* @reg_tlv: region TLV
* @dump_data: dump data
*/
struct iwl_dump_ini_region_data {
struct iwl_ucode_tlv *reg_tlv;
struct iwl_fwrt_dump_data *dump_data;
};
static int iwl_dump_ini_prph_mac_iter_common(struct iwl_fw_runtime *fwrt,
void *range_ptr, u32 addr,
__le32 size)
{
struct iwl_fw_ini_error_dump_range *range = range_ptr;
__le32 *val = range->data;
int i;
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = size;
for (i = 0; i < le32_to_cpu(size); i += 4)
*val++ = cpu_to_le32(iwl_read_prph(fwrt->trans, addr + i));
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int
iwl_dump_ini_prph_mac_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
u32 addr = le32_to_cpu(reg->addrs[idx]) +
le32_to_cpu(reg->dev_addr.offset);
return iwl_dump_ini_prph_mac_iter_common(fwrt, range_ptr, addr,
reg->dev_addr.size);
}
static int
iwl_dump_ini_prph_mac_block_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs;
u32 addr = le32_to_cpu(reg->dev_addr_range.offset) +
le32_to_cpu(pairs[idx].addr);
return iwl_dump_ini_prph_mac_iter_common(fwrt, range_ptr, addr,
pairs[idx].size);
}
static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt,
void *range_ptr, u32 addr,
__le32 size, __le32 offset)
{
struct iwl_fw_ini_error_dump_range *range = range_ptr;
__le32 *val = range->data;
u32 indirect_wr_addr = WMAL_INDRCT_RD_CMD1;
u32 indirect_rd_addr = WMAL_MRSPF_1;
u32 prph_val;
u32 dphy_state;
u32 dphy_addr;
u32 prph_stts;
int i;
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = size;
if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
indirect_wr_addr = WMAL_INDRCT_CMD1;
indirect_wr_addr += le32_to_cpu(offset);
indirect_rd_addr += le32_to_cpu(offset);
if (!iwl_trans_grab_nic_access(fwrt->trans))
return -EBUSY;
dphy_addr = (offset) ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW;
dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr);
for (i = 0; i < le32_to_cpu(size); i += 4) {
if (dphy_state == HBUS_TIMEOUT ||
(dphy_state & WFPM_PS_CTL_RW_PHYRF_PD_FSM_CURSTATE_MSK) !=
WFPM_PHYRF_STATE_ON) {
*val++ = cpu_to_le32(WFPM_DPHY_OFF);
continue ;
}
iwl_write_prph_no_grab(fwrt->trans, indirect_wr_addr,
WMAL_INDRCT_CMD(addr + i));
if (fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF1 &&
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_JF2 &&
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR1 &&
fwrt->trans->info.hw_rf_id != IWL_CFG_RF_TYPE_HR2) {
udelay(2);
prph_stts = iwl_read_prph_no_grab(fwrt->trans,
WMAL_MRSPF_STTS);
/* Abort dump if status is 0xA5A5A5A2 or FIFO1 empty */
if (prph_stts == WMAL_TIMEOUT_VAL ||
!WMAL_MRSPF_STTS_IS_FIFO1_NOT_EMPTY(prph_stts))
break ;
}
prph_val = iwl_read_prph_no_grab(fwrt->trans,
indirect_rd_addr);
*val++ = cpu_to_le32(prph_val);
}
iwl_trans_release_nic_access(fwrt->trans);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int
iwl_dump_ini_prph_phy_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
u32 addr = le32_to_cpu(reg->addrs[idx]);
return iwl_dump_ini_prph_phy_iter_common(fwrt, range_ptr, addr,
reg->dev_addr.size,
reg->dev_addr.offset);
}
static int
iwl_dump_ini_prph_phy_block_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs;
u32 addr = le32_to_cpu(pairs[idx].addr);
return iwl_dump_ini_prph_phy_iter_common(fwrt, range_ptr, addr,
pairs[idx].size,
reg->dev_addr_range.offset);
}
static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
__le32 *val = range->data;
u32 addr = le32_to_cpu(reg->addrs[idx]) +
le32_to_cpu(reg->dev_addr.offset);
int i;
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = reg->dev_addr.size;
for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4)
*val++ = cpu_to_le32(iwl_trans_read32(fwrt->trans, addr + i));
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int iwl_dump_ini_config_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_trans *trans = fwrt->trans;
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
__le32 *val = range->data;
u32 addr = le32_to_cpu(reg->addrs[idx]) +
le32_to_cpu(reg->dev_addr.offset);
int i;
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = reg->dev_addr.size;
for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) {
int ret;
u32 tmp;
ret = iwl_trans_read_config32(trans, addr + i, &tmp);
if (ret < 0)
return ret;
*val++ = cpu_to_le32(tmp);
}
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
u32 addr = le32_to_cpu(reg->addrs[idx]) +
le32_to_cpu(reg->dev_addr.offset);
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = reg->dev_addr.size;
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
le32_to_cpu(reg->dev_addr.size));
if (reg->sub_type == IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_HW_SMEM &&
fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf)
fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx,
range->data,
le32_to_cpu(reg->dev_addr.size));
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int _iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt,
void *range_ptr, u32 range_len, int idx)
{
struct page *page = fwrt->fw_paging_db[idx].fw_paging_block;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
dma_addr_t addr = fwrt->fw_paging_db[idx].fw_paging_phys;
u32 page_size = fwrt->fw_paging_db[idx].fw_paging_size;
range->page_num = cpu_to_le32(idx);
range->range_data_size = cpu_to_le32(page_size);
dma_sync_single_for_cpu(fwrt->trans->dev, addr, page_size,
DMA_BIDIRECTIONAL);
memcpy(range->data, page_address(page), page_size);
dma_sync_single_for_device(fwrt->trans->dev, addr, page_size,
DMA_BIDIRECTIONAL);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_error_dump_range *range;
u32 page_size;
/* all paged index start from 1 to skip CSS section */
idx++;
if (!fwrt->trans->mac_cfg->gen2)
return _iwl_dump_ini_paging_iter(fwrt, range_ptr, range_len, idx);
range = range_ptr;
page_size = fwrt->trans->init_dram.paging[idx].size;
range->page_num = cpu_to_le32(idx);
range->range_data_size = cpu_to_le32(page_size);
memcpy(range->data, fwrt->trans->init_dram.paging[idx].block,
page_size);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int
iwl_dump_ini_mon_dram_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
struct iwl_dram_data *frag;
u32 alloc_id = le32_to_cpu(reg->dram_alloc_id);
frag = &fwrt->trans->dbg.fw_mon_ini[alloc_id].frags[idx];
range->dram_base_addr = cpu_to_le64(frag->physical);
range->range_data_size = cpu_to_le32(frag->size);
memcpy(range->data, frag->block, frag->size);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int iwl_dump_ini_mon_smem_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
u32 addr = le32_to_cpu(reg->internal_buffer.base_addr);
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = reg->internal_buffer.size;
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
le32_to_cpu(reg->internal_buffer.size));
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static bool iwl_ini_txf_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;
struct iwl_fwrt_shared_mem_cfg *cfg = &fwrt->smem_cfg;
int txf_num = cfg->num_txfifo_entries;
int int_txf_num = ARRAY_SIZE(cfg->internal_txfifo_size);
u32 lmac_bitmap = le32_to_cpu(reg->fifos.fid[0]);
if (!idx) {
if (le32_to_cpu(reg->fifos.offset) && cfg->num_lmacs == 1) {
IWL_ERR(fwrt, "WRT: Invalid lmac offset 0x%x\n" ,
le32_to_cpu(reg->fifos.offset));
return false ;
}
iter->internal_txf = 0;
iter->fifo_size = 0;
iter->fifo = -1;
if (le32_to_cpu(reg->fifos.offset))
iter->lmac = 1;
else
iter->lmac = 0;
}
if (!iter->internal_txf) {
for (iter->fifo++; iter->fifo < txf_num; iter->fifo++) {
iter->fifo_size =
cfg->lmac[iter->lmac].txfifo_size[iter->fifo];
if (iter->fifo_size && (lmac_bitmap & BIT(iter->fifo)))
return true ;
}
iter->fifo--;
}
iter->internal_txf = 1;
if (!fw_has_capa(&fwrt->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG))
return false ;
for (iter->fifo++; iter->fifo < int_txf_num + txf_num; iter->fifo++) {
iter->fifo_size =
cfg->internal_txfifo_size[iter->fifo - txf_num];
if (iter->fifo_size && (lmac_bitmap & BIT(iter->fifo)))
return true ;
}
return false ;
}
static int iwl_dump_ini_txf_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
struct iwl_txf_iter_data *iter = &fwrt->dump.txf_iter_data;
struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data;
u32 offs = le32_to_cpu(reg->fifos.offset), addr;
u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
u32 registers_size = registers_num * sizeof (*reg_dump);
__le32 *data;
int i;
if (!iwl_ini_txf_iter(fwrt, reg_data, idx))
return -EIO;
if (!iwl_trans_grab_nic_access(fwrt->trans))
return -EBUSY;
range->fifo_hdr.fifo_num = cpu_to_le32(iter->fifo);
range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num);
range->range_data_size = cpu_to_le32(iter->fifo_size + registers_size);
iwl_write_prph_no_grab(fwrt->trans, TXF_LARC_NUM + offs, iter->fifo);
/*
* read txf registers. for each register, write to the dump the
* register address and its value
*/
for (i = 0; i < registers_num; i++) {
addr = le32_to_cpu(reg->addrs[i]) + offs;
reg_dump->addr = cpu_to_le32(addr);
reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans,
addr));
reg_dump++;
}
if (reg->fifos.hdr_only) {
range->range_data_size = cpu_to_le32(registers_size);
goto out;
}
/* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
iwl_write_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_ADDR + offs,
TXF_WR_PTR + offs);
/* Dummy-read to advance the read pointer to the head */
iwl_read_prph_no_grab(fwrt->trans, TXF_READ_MODIFY_DATA + offs);
/* Read FIFO */
addr = TXF_READ_MODIFY_DATA + offs;
data = (void *)reg_dump;
for (i = 0; i < iter->fifo_size; i += sizeof (*data))
*data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr));
if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_txf)
fwrt->sanitize_ops->frob_txf(fwrt->sanitize_ctx,
reg_dump, iter->fifo_size);
out:
iwl_trans_release_nic_access(fwrt->trans);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int
iwl_dump_ini_prph_snps_dphyip_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
__le32 *val = range->data;
__le32 offset = reg->dev_addr.offset;
u32 indirect_rd_wr_addr = DPHYIP_INDIRECT;
u32 addr = le32_to_cpu(reg->addrs[idx]);
u32 dphy_state, dphy_addr, prph_val;
int i;
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = reg->dev_addr.size;
if (!iwl_trans_grab_nic_access(fwrt->trans))
return -EBUSY;
indirect_rd_wr_addr += le32_to_cpu(offset);
dphy_addr = offset ? WFPM_LMAC2_PS_CTL_RW : WFPM_LMAC1_PS_CTL_RW;
dphy_state = iwl_read_umac_prph_no_grab(fwrt->trans, dphy_addr);
for (i = 0; i < le32_to_cpu(reg->dev_addr.size); i += 4) {
if (dphy_state == HBUS_TIMEOUT ||
(dphy_state & WFPM_PS_CTL_RW_PHYRF_PD_FSM_CURSTATE_MSK) !=
WFPM_PHYRF_STATE_ON) {
*val++ = cpu_to_le32(WFPM_DPHY_OFF);
continue ;
}
iwl_write_prph_no_grab(fwrt->trans, indirect_rd_wr_addr,
addr + i);
/* wait a bit for value to be ready in register */
udelay(1);
prph_val = iwl_read_prph_no_grab(fwrt->trans,
indirect_rd_wr_addr);
*val++ = cpu_to_le32((prph_val & DPHYIP_INDIRECT_RD_MSK) >>
DPHYIP_INDIRECT_RD_SHIFT);
}
iwl_trans_release_nic_access(fwrt->trans);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
struct iwl_ini_rxf_data {
u32 fifo_num;
u32 size;
u32 offset;
};
static void iwl_ini_get_rxf_data(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
struct iwl_ini_rxf_data *data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
u32 fid1 = le32_to_cpu(reg->fifos.fid[0]);
u32 fid2 = le32_to_cpu(reg->fifos.fid[1]);
u8 fifo_idx;
if (!data)
return ;
memset(data, 0, sizeof (*data));
/* make sure only one bit is set in only one fid */
if (WARN_ONCE(hweight_long(fid1) + hweight_long(fid2) != 1,
"fid1=%x, fid2=%x\n" , fid1, fid2))
return ;
if (fid1) {
fifo_idx = ffs(fid1) - 1;
if (WARN_ONCE(fifo_idx >= MAX_NUM_LMAC, "fifo_idx=%d\n" ,
fifo_idx))
return ;
data->size = fwrt->smem_cfg.lmac[fifo_idx].rxfifo1_size;
data->fifo_num = fifo_idx;
} else {
u8 max_idx;
fifo_idx = ffs(fid2) - 1;
if (iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP,
SHARED_MEM_CFG_CMD, 0) <= 3)
max_idx = 0;
else
max_idx = 1;
if (WARN_ONCE(fifo_idx > max_idx,
"invalid umac fifo idx %d" , fifo_idx))
return ;
/* use bit 31 to distinguish between umac and lmac rxf while
* parsing the dump
*/
data->fifo_num = fifo_idx | IWL_RXF_UMAC_BIT;
switch (fifo_idx) {
case 0:
data->size = fwrt->smem_cfg.rxfifo2_size;
data->offset = iwl_umac_prph(fwrt->trans,
RXF_DIFF_FROM_PREV);
break ;
case 1:
data->size = fwrt->smem_cfg.rxfifo2_control_size;
data->offset = iwl_umac_prph(fwrt->trans,
RXF2C_DIFF_FROM_PREV);
break ;
}
}
}
static int iwl_dump_ini_rxf_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
struct iwl_ini_rxf_data rxf_data;
struct iwl_fw_ini_error_dump_register *reg_dump = (void *)range->data;
u32 offs = le32_to_cpu(reg->fifos.offset), addr;
u32 registers_num = iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
u32 registers_size = registers_num * sizeof (*reg_dump);
__le32 *data;
int i;
iwl_ini_get_rxf_data(fwrt, reg_data, &rxf_data);
if (!rxf_data.size)
return -EIO;
if (!iwl_trans_grab_nic_access(fwrt->trans))
return -EBUSY;
range->fifo_hdr.fifo_num = cpu_to_le32(rxf_data.fifo_num);
range->fifo_hdr.num_of_registers = cpu_to_le32(registers_num);
range->range_data_size = cpu_to_le32(rxf_data.size + registers_size);
/*
* read rxf registers. for each register, write to the dump the
* register address and its value
*/
for (i = 0; i < registers_num; i++) {
addr = le32_to_cpu(reg->addrs[i]) + offs;
reg_dump->addr = cpu_to_le32(addr);
reg_dump->data = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans,
addr));
reg_dump++;
}
if (reg->fifos.hdr_only) {
range->range_data_size = cpu_to_le32(registers_size);
goto out;
}
offs = rxf_data.offset;
/* Lock fence */
iwl_write_prph_no_grab(fwrt->trans, RXF_SET_FENCE_MODE + offs, 0x1);
/* Set fence pointer to the same place like WR pointer */
iwl_write_prph_no_grab(fwrt->trans, RXF_LD_WR2FENCE + offs, 0x1);
/* Set fence offset */
iwl_write_prph_no_grab(fwrt->trans, RXF_LD_FENCE_OFFSET_ADDR + offs,
0x0);
/* Read FIFO */
addr = RXF_FIFO_RD_FENCE_INC + offs;
data = (void *)reg_dump;
for (i = 0; i < rxf_data.size; i += sizeof (*data))
*data++ = cpu_to_le32(iwl_read_prph_no_grab(fwrt->trans, addr));
out:
iwl_trans_release_nic_access(fwrt->trans);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int
iwl_dump_ini_err_table_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_region_err_table *err_table = ®->err_table;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
u32 addr = le32_to_cpu(err_table->base_addr) +
le32_to_cpu(err_table->offset);
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = err_table->size;
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
le32_to_cpu(err_table->size));
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int
iwl_dump_ini_special_mem_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_region_special_device_memory *special_mem =
®->special_mem;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
u32 addr = le32_to_cpu(special_mem->base_addr) +
le32_to_cpu(special_mem->offset);
range->internal_base_addr = cpu_to_le32(addr);
range->range_data_size = special_mem->size;
iwl_trans_read_mem_bytes(fwrt->trans, addr, range->data,
le32_to_cpu(special_mem->size));
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int
iwl_dump_ini_dbgi_sram_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_error_dump_range *range = range_ptr;
__le32 *val = range->data;
u32 prph_data;
int i;
if (!iwl_trans_grab_nic_access(fwrt->trans))
return -EBUSY;
range->range_data_size = reg->dev_addr.size;
for (i = 0; i < (le32_to_cpu(reg->dev_addr.size) / 4); i++) {
prph_data = iwl_read_prph_no_grab(fwrt->trans, (i % 2) ?
DBGI_SRAM_TARGET_ACCESS_RDATA_MSB :
DBGI_SRAM_TARGET_ACCESS_RDATA_LSB);
if (iwl_trans_is_hw_error_value(prph_data)) {
iwl_trans_release_nic_access(fwrt->trans);
return -EBUSY;
}
*val++ = cpu_to_le32(prph_data);
}
iwl_trans_release_nic_access(fwrt->trans);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int iwl_dump_ini_fw_pkt_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
struct iwl_fw_ini_error_dump_range *range = range_ptr;
struct iwl_rx_packet *pkt = reg_data->dump_data->fw_pkt;
u32 pkt_len;
if (!pkt)
return -EIO;
pkt_len = iwl_rx_packet_payload_len(pkt);
memcpy(&range->fw_pkt_hdr, &pkt->hdr, sizeof (range->fw_pkt_hdr));
range->range_data_size = cpu_to_le32(pkt_len);
memcpy(range->data, pkt->data, pkt_len);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static int iwl_dump_ini_imr_iter(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *range_ptr, u32 range_len, int idx)
{
/* read the IMR memory and DMA it to SRAM */
struct iwl_fw_ini_error_dump_range *range = range_ptr;
u64 imr_curr_addr = fwrt->trans->dbg.imr_data.imr_curr_addr;
u32 imr_rem_bytes = fwrt->trans->dbg.imr_data.imr2sram_remainbyte;
u32 sram_addr = fwrt->trans->dbg.imr_data.sram_addr;
u32 sram_size = fwrt->trans->dbg.imr_data.sram_size;
u32 size_to_dump = (imr_rem_bytes > sram_size) ? sram_size : imr_rem_bytes;
range->range_data_size = cpu_to_le32(size_to_dump);
if (iwl_trans_write_imr_mem(fwrt->trans, sram_addr,
imr_curr_addr, size_to_dump)) {
IWL_ERR(fwrt, "WRT_DEBUG: IMR Memory transfer failed\n" );
return -1;
}
fwrt->trans->dbg.imr_data.imr_curr_addr = imr_curr_addr + size_to_dump;
fwrt->trans->dbg.imr_data.imr2sram_remainbyte -= size_to_dump;
iwl_trans_read_mem_bytes(fwrt->trans, sram_addr, range->data,
size_to_dump);
return sizeof (*range) + le32_to_cpu(range->range_data_size);
}
static void *
iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *data, u32 data_len)
{
struct iwl_fw_ini_error_dump *dump = data;
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
return dump->data;
}
/**
* mask_apply_and_normalize - applies mask on val and normalize the result
*
* @val: value
* @mask: mask to apply and to normalize with
*
* The normalization is based on the first set bit in the mask
*
* Returns: the extracted value
*/
static u32 mask_apply_and_normalize(u32 val, u32 mask)
{
return (val & mask) >> (ffs(mask) - 1);
}
static __le32 iwl_get_mon_reg(struct iwl_fw_runtime *fwrt, u32 alloc_id,
const struct iwl_fw_mon_reg *reg_info)
{
u32 val, offs;
/* The header addresses of DBGCi is calculate as follows:
* DBGC1 address + (0x100 * i)
*/
offs = (alloc_id - IWL_FW_INI_ALLOCATION_ID_DBGC1) * 0x100;
if (!reg_info || !reg_info->addr || !reg_info->mask)
return 0;
val = iwl_read_prph_no_grab(fwrt->trans, reg_info->addr + offs);
return cpu_to_le32(mask_apply_and_normalize(val, reg_info->mask));
}
static void *
iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id,
struct iwl_fw_ini_monitor_dump *data,
const struct iwl_fw_mon_regs *addrs)
{
if (!iwl_trans_grab_nic_access(fwrt->trans)) {
IWL_ERR(fwrt, "Failed to get monitor header\n" );
return NULL;
}
data->write_ptr = iwl_get_mon_reg(fwrt, alloc_id,
&addrs->write_ptr);
if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) {
u32 wrt_ptr = le32_to_cpu(data->write_ptr);
data->write_ptr = cpu_to_le32(wrt_ptr >> 2);
}
data->cycle_cnt = iwl_get_mon_reg(fwrt, alloc_id,
&addrs->cycle_cnt);
data->cur_frag = iwl_get_mon_reg(fwrt, alloc_id,
&addrs->cur_frag);
iwl_trans_release_nic_access(fwrt->trans);
data->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
return data->data;
}
static void *
iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *data, u32 data_len)
{
struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
u32 alloc_id = le32_to_cpu(reg->dram_alloc_id);
return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,
&fwrt->trans->mac_cfg->base->mon_dram_regs);
}
static void *
iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *data, u32 data_len)
{
struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
u32 alloc_id = le32_to_cpu(reg->internal_buffer.alloc_id);
return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump,
&fwrt->trans->mac_cfg->base->mon_smem_regs);
}
static void *
iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *data, u32 data_len)
{
struct iwl_fw_ini_monitor_dump *mon_dump = (void *)data;
return iwl_dump_ini_mon_fill_header(fwrt,
/* no offset calculation later */
IWL_FW_INI_ALLOCATION_ID_DBGC1,
mon_dump,
&fwrt->trans->mac_cfg->base->mon_dbgi_regs);
}
static void *
iwl_dump_ini_err_table_fill_header(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *data, u32 data_len)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_err_table_dump *dump = data;
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
dump->version = reg->err_table.version;
return dump->data;
}
static void *
iwl_dump_ini_special_mem_fill_header(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *data, u32 data_len)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_special_device_memory *dump = data;
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
dump->type = reg->special_mem.type;
dump->version = reg->special_mem.version;
return dump->data;
}
static void *
iwl_dump_ini_imr_fill_header(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data,
void *data, u32 data_len)
{
struct iwl_fw_ini_error_dump *dump = data;
dump->header.version = cpu_to_le32(IWL_INI_DUMP_VER);
return dump->data;
}
static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
return iwl_tlv_array_len(reg_data->reg_tlv, reg, addrs);
}
static u32
iwl_dump_ini_mem_block_ranges(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
size_t size = sizeof (struct iwl_fw_ini_addr_size);
return iwl_tlv_array_len_with_size(reg_data->reg_tlv, reg, size);
}
static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
if (fwrt->trans->mac_cfg->gen2) {
if (fwrt->trans->init_dram.paging_cnt)
return fwrt->trans->init_dram.paging_cnt - 1;
else
return 0;
}
return fwrt->num_of_paging_blk;
}
static u32
iwl_dump_ini_mon_dram_ranges(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_mon *fw_mon;
u32 ranges = 0, alloc_id = le32_to_cpu(reg->dram_alloc_id);
int i;
fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
for (i = 0; i < fw_mon->num_frags; i++) {
if (!fw_mon->frags[i].size)
break ;
ranges++;
}
return ranges;
}
static u32 iwl_dump_ini_txf_ranges(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
u32 num_of_fifos = 0;
while (iwl_ini_txf_iter(fwrt, reg_data, num_of_fifos))
num_of_fifos++;
return num_of_fifos;
}
static u32 iwl_dump_ini_single_range(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
return 1;
}
static u32 iwl_dump_ini_imr_ranges(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
/* range is total number of pages need to copied from
*IMR memory to SRAM and later from SRAM to DRAM
*/
u32 imr_enable = fwrt->trans->dbg.imr_data.imr_enable;
u32 imr_size = fwrt->trans->dbg.imr_data.imr_size;
u32 sram_size = fwrt->trans->dbg.imr_data.sram_size;
if (imr_enable == 0 || imr_size == 0 || sram_size == 0) {
IWL_DEBUG_INFO(fwrt,
"WRT: Invalid imr data enable: %d, imr_size: %d, sram_size: %d\n" ,
imr_enable, imr_size, sram_size);
return 0;
}
return ((imr_size % sram_size) ? (imr_size / sram_size + 1) : (imr_size / sram_size));
}
static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
u32 size = le32_to_cpu(reg->dev_addr.size);
u32 ranges = iwl_dump_ini_mem_ranges(fwrt, reg_data);
if (!size || !ranges)
return 0;
return sizeof (struct iwl_fw_ini_error_dump) + ranges *
(size + sizeof (struct iwl_fw_ini_error_dump_range));
}
static u32
iwl_dump_ini_mem_block_get_size(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_ini_addr_size *pairs = (void *)reg->addrs;
u32 ranges = iwl_dump_ini_mem_block_ranges(fwrt, reg_data);
u32 size = sizeof (struct iwl_fw_ini_error_dump);
int range;
if (!ranges)
return 0;
for (range = 0; range < ranges; range++)
size += le32_to_cpu(pairs[range].size);
return size + ranges * sizeof (struct iwl_fw_ini_error_dump_range);
}
static u32
iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
int i;
u32 range_header_len = sizeof (struct iwl_fw_ini_error_dump_range);
u32 size = sizeof (struct iwl_fw_ini_error_dump);
/* start from 1 to skip CSS section */
for (i = 1; i <= iwl_dump_ini_paging_ranges(fwrt, reg_data); i++) {
size += range_header_len;
if (fwrt->trans->mac_cfg->gen2)
size += fwrt->trans->init_dram.paging[i].size;
else
size += fwrt->fw_paging_db[i].fw_paging_size;
}
return size;
}
static u32
iwl_dump_ini_mon_dram_get_size(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
struct iwl_fw_mon *fw_mon;
u32 size = 0, alloc_id = le32_to_cpu(reg->dram_alloc_id);
int i;
fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
for (i = 0; i < fw_mon->num_frags; i++) {
struct iwl_dram_data *frag = &fw_mon->frags[i];
if (!frag->size)
break ;
size += sizeof (struct iwl_fw_ini_error_dump_range) + frag->size;
}
if (size)
size += sizeof (struct iwl_fw_ini_monitor_dump);
return size;
}
static u32
iwl_dump_ini_mon_smem_get_size(struct iwl_fw_runtime *fwrt,
struct iwl_dump_ini_region_data *reg_data)
{
struct iwl_fw_ini_region_tlv *reg = (void *)reg_data->reg_tlv->data;
u32 size;
size = le32_to_cpu(reg->internal_buffer.size);
if (!size)
return 0;
size += sizeof (struct iwl_fw_ini_monitor_dump) +
sizeof (struct iwl_fw_ini_error_dump_range);
return size;
}
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=97 H=95 G=95
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.32Bemerkung:
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland