Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  dbg.c   Sprache: C

 
// 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






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge