iwlwifi: mvm: add rxf and txf to dump data
authorLiad Kaufman <liad.kaufman@intel.com>
Mon, 15 Dec 2014 15:54:16 +0000 (17:54 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Thu, 22 Jan 2015 15:54:05 +0000 (17:54 +0200)
When the FW is in error status - try to read the RXF and
TXF (all of them) and add them to the dump data.

This shouldn't happen in non-error statuses, as we don't
want to stop the RXF/TXF while they are running.

Signed-off-by: Liad Kaufman <liad.kaufman@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/mvm/fw-api.h
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c

index ec115bded88a824b7fc4ded21249dd4a9a448aaf..919a2548a92c5dca064258c437cdb8e59ade42db 100644 (file)
@@ -134,6 +134,27 @@ struct iwl_fw_error_dump_txcmd {
        u8 data[];
 } __packed;
 
+/**
+ * struct iwl_fw_error_dump_fifo - RX/TX FIFO data
+ * @fifo_num: number of FIFO (starting from 0)
+ * @available_bytes: num of bytes available in FIFO (may be less than FIFO size)
+ * @wr_ptr: position of write pointer
+ * @rd_ptr: position of read pointer
+ * @fence_ptr: position of fence pointer
+ * @fence_mode: the current mode of the fence (before locking) -
+ *     0=follow RD pointer ; 1 = freeze
+ * @data: all of the FIFO's data
+ */
+struct iwl_fw_error_dump_fifo {
+       __le32 fifo_num;
+       __le32 available_bytes;
+       __le32 wr_ptr;
+       __le32 rd_ptr;
+       __le32 fence_ptr;
+       __le32 fence_mode;
+       u8 data[];
+} __packed;
+
 enum iwl_fw_error_dump_family {
        IWL_FW_ERROR_DUMP_FAMILY_7 = 7,
        IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
index 83ab4239082c969dd61b9fc3415c4b745ebcb55c..387a5aada0ee151d470e00d027c1ed57db5b0bc2 100644 (file)
@@ -359,12 +359,30 @@ enum secure_load_status_reg {
 
 /* Rx FIFO */
 #define RXF_SIZE_ADDR                  (0xa00c88)
+#define RXF_RD_D_SPACE                 (0xa00c40)
+#define RXF_RD_WR_PTR                  (0xa00c50)
+#define RXF_RD_RD_PTR                  (0xa00c54)
+#define RXF_RD_FENCE_PTR               (0xa00c4c)
+#define RXF_SET_FENCE_MODE             (0xa00c14)
+#define RXF_LD_WR2FENCE                (0xa00c1c)
+#define RXF_FIFO_RD_FENCE_INC          (0xa00c68)
 #define RXF_SIZE_BYTE_CND_POS          (7)
 #define RXF_SIZE_BYTE_CNT_MSK          (0x3ff << RXF_SIZE_BYTE_CND_POS)
+#define RXF_DIFF_FROM_PREV             (0x200)
 
 #define RXF_LD_FENCE_OFFSET_ADDR       (0xa00c10)
 #define RXF_FIFO_RD_FENCE_ADDR         (0xa00c0c)
 
+/* Tx FIFO */
+#define TXF_FIFO_ITEM_CNT              (0xa00438)
+#define TXF_WR_PTR                     (0xa00414)
+#define TXF_RD_PTR                     (0xa00410)
+#define TXF_FENCE_PTR                  (0xa00418)
+#define TXF_LOCK_FENCE                 (0xa00424)
+#define TXF_LARC_NUM                   (0xa0043c)
+#define TXF_READ_MODIFY_DATA           (0xa00448)
+#define TXF_READ_MODIFY_ADDR           (0xa0044c)
+
 /* FW monitor */
 #define MON_BUFF_SAMPLE_CTL            (0xa03c00)
 #define MON_BUFF_BASE_ADDR             (0xa03c3c)
index b1e9dac186c6a6f62bb856ff0bf4b9990c003a33..b3badec1d2284554ab5e9f318c57639a76a91231 100644 (file)
@@ -128,6 +128,9 @@ enum {
        /* global key */
        WEP_KEY = 0x20,
 
+       /* Memory */
+       SHARED_MEM_CFG = 0x25,
+
        /* TDLS */
        TDLS_CHANNEL_SWITCH_CMD = 0x27,
        TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa,
@@ -1821,4 +1824,36 @@ struct iwl_tdls_config_res {
        struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT];
 } __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */
 
+#define TX_FIFO_MAX_NUM                8
+#define RX_FIFO_MAX_NUM                2
+
+/**
+ * Shared memory configuration information from the FW
+ *
+ * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not
+ *     accessible)
+ * @shared_mem_size: shared memory size
+ * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to
+ *     0x0 as accessible only via DBGM RDAT)
+ * @sample_buff_size: internal sample buff size
+ * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre
+ *     8000 HW set to 0x0 as not accessible)
+ * @txfifo_size: size of TXF0 ... TXF7
+ * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0
+ * @page_buff_addr: used by UMAC and performance debug (page miss analysis),
+ *     when paging is not supported this should be 0
+ * @page_buff_size: size of %page_buff_addr
+ */
+struct iwl_shared_mem_cfg {
+       __le32 shared_mem_addr;
+       __le32 shared_mem_size;
+       __le32 sample_buff_addr;
+       __le32 sample_buff_size;
+       __le32 txfifo_addr;
+       __le32 txfifo_size[TX_FIFO_MAX_NUM];
+       __le32 rxfifo_size[RX_FIFO_MAX_NUM];
+       __le32 page_buff_addr;
+       __le32 page_buff_size;
+} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */
+
 #endif /* __fw_api_h__ */
index 52338b73722382b88ab7a730ee6b01fc0e6eac2d..2e12f41dd65b392bb00d7e269618fced44a55936 100644 (file)
@@ -400,6 +400,57 @@ out:
        return ret;
 }
 
+static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm)
+{
+       struct iwl_host_cmd cmd = {
+               .id = SHARED_MEM_CFG,
+               .flags = CMD_WANT_SKB,
+               .data = { NULL, },
+               .len = { 0, },
+       };
+       struct iwl_rx_packet *pkt;
+       struct iwl_shared_mem_cfg *mem_cfg;
+       u32 i;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd)))
+               return;
+
+       pkt = cmd.resp_pkt;
+       if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
+               IWL_ERR(mvm, "Bad return from SHARED_MEM_CFG (0x%08X)\n",
+                       pkt->hdr.flags);
+               goto exit;
+       }
+
+       mem_cfg = (void *)pkt->data;
+
+       mvm->shared_mem_cfg.shared_mem_addr =
+               le32_to_cpu(mem_cfg->shared_mem_addr);
+       mvm->shared_mem_cfg.shared_mem_size =
+               le32_to_cpu(mem_cfg->shared_mem_size);
+       mvm->shared_mem_cfg.sample_buff_addr =
+               le32_to_cpu(mem_cfg->sample_buff_addr);
+       mvm->shared_mem_cfg.sample_buff_size =
+               le32_to_cpu(mem_cfg->sample_buff_size);
+       mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr);
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++)
+               mvm->shared_mem_cfg.txfifo_size[i] =
+                       le32_to_cpu(mem_cfg->txfifo_size[i]);
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++)
+               mvm->shared_mem_cfg.rxfifo_size[i] =
+                       le32_to_cpu(mem_cfg->rxfifo_size[i]);
+       mvm->shared_mem_cfg.page_buff_addr =
+               le32_to_cpu(mem_cfg->page_buff_addr);
+       mvm->shared_mem_cfg.page_buff_size =
+               le32_to_cpu(mem_cfg->page_buff_size);
+       IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n");
+
+exit:
+       iwl_free_resp(&cmd);
+}
+
 void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm)
 {
        /* stop recording */
@@ -495,6 +546,8 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
                goto error;
        }
 
+       iwl_mvm_get_shared_mem_conf(mvm);
+
        ret = iwl_mvm_sf_update(mvm, NULL, false);
        if (ret)
                IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
index b5f401da91a0868ade32bc5cc46457f03b046227..0ea0f0a2239a77b933321b64ea5ba7d12bfe4c29 100644 (file)
@@ -766,6 +766,132 @@ static void iwl_mvm_free_coredump(const void *data)
        kfree(fw_error_dump);
 }
 
+static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
+                              struct iwl_fw_error_dump_data **dump_data)
+{
+       struct iwl_fw_error_dump_fifo *fifo_hdr;
+       u32 *fifo_data;
+       u32 fifo_len;
+       unsigned long flags;
+       int i, j;
+
+       if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags))
+               return;
+
+       /* Pull RXF data from all RXFs */
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) {
+               /*
+                * Keep aside the additional offset that might be needed for
+                * next RXF
+                */
+               u32 offset_diff = RXF_DIFF_FROM_PREV * i;
+
+               fifo_hdr = (void *)(*dump_data)->data;
+               fifo_data = (void *)fifo_hdr->data;
+               fifo_len = mvm->shared_mem_cfg.rxfifo_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 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(i);
+               fifo_hdr->available_bytes =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_D_SPACE +
+                                                       offset_diff));
+               fifo_hdr->wr_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_WR_PTR +
+                                                       offset_diff));
+               fifo_hdr->rd_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_RD_PTR +
+                                                       offset_diff));
+               fifo_hdr->fence_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_RD_FENCE_PTR +
+                                                       offset_diff));
+               fifo_hdr->fence_mode =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       RXF_SET_FENCE_MODE +
+                                                       offset_diff));
+
+               /* Lock fence */
+               iwl_trans_write_prph(mvm->trans,
+                                    RXF_SET_FENCE_MODE + offset_diff, 0x1);
+               /* Set fence pointer to the same place like WR pointer */
+               iwl_trans_write_prph(mvm->trans,
+                                    RXF_LD_WR2FENCE + offset_diff, 0x1);
+               /* Set fence offset */
+               iwl_trans_write_prph(mvm->trans,
+                                    RXF_LD_FENCE_OFFSET_ADDR + offset_diff,
+                                    0x0);
+
+               /* Read FIFO */
+               fifo_len /= sizeof(u32); /* Size in DWORDS */
+               for (j = 0; j < fifo_len; j++)
+                       fifo_data[j] = iwl_trans_read_prph(mvm->trans,
+                                                        RXF_FIFO_RD_FENCE_INC +
+                                                        offset_diff);
+               *dump_data = iwl_fw_error_next_data(*dump_data);
+       }
+
+       /* Pull TXF data from all TXFs */
+       for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) {
+               /* Mark the number of TXF we're pulling now */
+               iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i);
+
+               fifo_hdr = (void *)(*dump_data)->data;
+               fifo_data = (void *)fifo_hdr->data;
+               fifo_len = mvm->shared_mem_cfg.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 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(i);
+               fifo_hdr->available_bytes =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_FIFO_ITEM_CNT));
+               fifo_hdr->wr_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_WR_PTR));
+               fifo_hdr->rd_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_RD_PTR));
+               fifo_hdr->fence_ptr =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_FENCE_PTR));
+               fifo_hdr->fence_mode =
+                       cpu_to_le32(iwl_trans_read_prph(mvm->trans,
+                                                       TXF_LOCK_FENCE));
+
+               /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */
+               iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR,
+                                    TXF_WR_PTR);
+
+               /* Dummy-read to advance the read pointer to the head */
+               iwl_trans_read_prph(mvm->trans, TXF_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(mvm->trans,
+                                                         TXF_READ_MODIFY_DATA);
+               *dump_data = iwl_fw_error_next_data(*dump_data);
+       }
+
+       iwl_trans_release_nic_access(mvm->trans, &flags);
+}
+
 void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
 {
        struct iwl_fw_error_dump_file *dump_file;
@@ -774,9 +900,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        struct iwl_fw_error_dump_mem *dump_mem;
        struct iwl_mvm_dump_ptrs *fw_error_dump;
        u32 sram_len, sram_ofs;
-       u32 file_len, rxf_len;
-       unsigned long flags;
-       int reg_val;
+       u32 file_len, fifo_data_len = 0;
        u32 smem_len = mvm->cfg->smem_len;
        u32 sram2_len = mvm->cfg->dccm2_len;
 
@@ -808,17 +932,39 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                sram_len = mvm->cfg->dccm_len;
        }
 
-       /* reading buffer size */
-       reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
-       rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+       /* reading RXF/TXF sizes */
+       if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) {
+               struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg;
+               int i;
+
+               fifo_data_len = 0;
+
+               /* Count RXF size */
+               for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) {
+                       if (!mem_cfg->rxfifo_size[i])
+                               continue;
+
+                       /* Add header info */
+                       fifo_data_len += mem_cfg->rxfifo_size[i] +
+                                        sizeof(*dump_data) +
+                                        sizeof(struct iwl_fw_error_dump_fifo);
+               }
+
+               for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) {
+                       if (!mem_cfg->txfifo_size[i])
+                               continue;
 
-       /* the register holds the value divided by 128 */
-       rxf_len = rxf_len << 7;
+                       /* Add header info */
+                       fifo_data_len += mem_cfg->txfifo_size[i] +
+                                        sizeof(*dump_data) +
+                                        sizeof(struct iwl_fw_error_dump_fifo);
+               }
+       }
 
        file_len = sizeof(*dump_file) +
-                  sizeof(*dump_data) * 3 +
+                  sizeof(*dump_data) * 2 +
                   sram_len + sizeof(*dump_mem) +
-                  rxf_len +
+                  fifo_data_len +
                   sizeof(*dump_info);
 
        /* Make room for the SMEM, if it exists */
@@ -856,24 +1002,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
                sizeof(dump_info->bus_human_readable));
 
        dump_data = iwl_fw_error_next_data(dump_data);
-       dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
-       dump_data->len = cpu_to_le32(rxf_len);
-
-       if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
-               u32 *rxf = (void *)dump_data->data;
-               int i;
+       /* We only dump the FIFOs if the FW is in error state */
+       if (test_bit(STATUS_FW_ERROR, &mvm->trans->status))
+               iwl_mvm_dump_fifos(mvm, &dump_data);
 
-               for (i = 0; i < (rxf_len / sizeof(u32)); i++) {
-                       iwl_trans_write_prph(mvm->trans,
-                                            RXF_LD_FENCE_OFFSET_ADDR,
-                                            i * sizeof(u32));
-                       rxf[i] = iwl_trans_read_prph(mvm->trans,
-                                                    RXF_FIFO_RD_FENCE_ADDR);
-               }
-               iwl_trans_release_nic_access(mvm->trans, &flags);
-       }
-
-       dump_data = iwl_fw_error_next_data(dump_data);
        dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
        dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
        dump_mem = (void *)dump_data->data;
index e9c05d70dde063b70822af1de33468fd14ecf257..aa93ee79467073c6e24d5d35c0f169d512f6bd21 100644 (file)
@@ -536,6 +536,18 @@ enum iwl_mvm_tdls_cs_state {
        IWL_MVM_TDLS_SW_ACTIVE,
 };
 
+struct iwl_mvm_shared_mem_cfg {
+       u32 shared_mem_addr;
+       u32 shared_mem_size;
+       u32 sample_buff_addr;
+       u32 sample_buff_size;
+       u32 txfifo_addr;
+       u32 txfifo_size[TX_FIFO_MAX_NUM];
+       u32 rxfifo_size[RX_FIFO_MAX_NUM];
+       u32 page_buff_addr;
+       u32 page_buff_size;
+};
+
 struct iwl_mvm {
        /* for logger access */
        struct device *dev;
@@ -787,6 +799,8 @@ struct iwl_mvm {
                        u32 ch_sw_tm_ie;
                } peer;
        } tdls_cs;
+
+       struct iwl_mvm_shared_mem_cfg shared_mem_cfg;
 };
 
 /* Extract MVM priv from op_mode and _hw */
@@ -1002,6 +1016,9 @@ int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                         struct iwl_device_cmd *cmd);
 int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
                            struct iwl_device_cmd *cmd);
+int iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb,
+                                   struct iwl_device_cmd *cmd);
 
 /* MVM PHY */
 int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt,
index 90143aa838bebcc3b6811fbb2c88319a02a4af91..8bf8c2a29e5eff6121a788333d9f6c12306e39fd 100644 (file)
@@ -270,6 +270,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
        CMD(MGMT_MCAST_KEY),
        CMD(TX_CMD),
        CMD(TXPATH_FLUSH),
+       CMD(SHARED_MEM_CFG),
        CMD(MAC_CONTEXT_CMD),
        CMD(TIME_EVENT_CMD),
        CMD(TIME_EVENT_NOTIFICATION),