iwlwifi: allow memory debug TLV to specify the memory type
authorJohannes Berg <johannes.berg@intel.com>
Thu, 20 Oct 2016 08:01:43 +0000 (10:01 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Thu, 26 Jan 2017 07:38:27 +0000 (09:38 +0200)
Due to some new features and changes, the firmware file will now
specify what type of memory to dump, in upper 8 bits of the type
field of the TLV. Parse it (types we don't understand are errors)
and teach the code to dump periphery memory.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/iwl-drv.c
drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c

index 4315812..a6719d6 100644 (file)
@@ -1020,6 +1020,18 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                        IWL_DEBUG_INFO(drv, "Found debug memory segment: %u\n",
                                       dbg_mem->data_type);
 
+                       switch (type & FW_DBG_MEM_TYPE_MASK) {
+                       case FW_DBG_MEM_TYPE_REGULAR:
+                       case FW_DBG_MEM_TYPE_PRPH:
+                               /* we know how to handle these */
+                               break;
+                       default:
+                               IWL_ERR(drv,
+                                       "Found debug memory segment with invalid type: 0x%x\n",
+                                       type);
+                               return -EINVAL;
+                       }
+
                        size = sizeof(*pieces->dbg_mem_tlv) *
                               (pieces->n_dbg_mem_tlv + 1);
                        n = krealloc(pieces->dbg_mem_tlv, size, GFP_KERNEL);
index 11245b9..c842075 100644 (file)
@@ -489,9 +489,22 @@ enum iwl_fw_dbg_monitor_mode {
 };
 
 /**
+ * enum iwl_fw_mem_seg_type - memory segment type
+ * @FW_DBG_MEM_TYPE_MASK: mask for the type indication
+ * @FW_DBG_MEM_TYPE_REGULAR: regular memory
+ * @FW_DBG_MEM_TYPE_PRPH: periphery memory (requires special reading)
+ */
+enum iwl_fw_mem_seg_type {
+       FW_DBG_MEM_TYPE_MASK    = 0xff000000,
+       FW_DBG_MEM_TYPE_REGULAR = 0x00000000,
+       FW_DBG_MEM_TYPE_PRPH    = 0x01000000,
+};
+
+/**
  * struct iwl_fw_dbg_mem_seg_tlv - configures the debug data memory segments
  *
- * @data_type: the memory segment type to record
+ * @data_type: the memory segment type to record, see &enum iwl_fw_mem_seg_type
+ *     for what we care about
  * @ofs: the memory segment offset
  * @len: the memory segment length, in bytes
  *
index 7df3c3f..e447cd9 100644 (file)
@@ -406,6 +406,30 @@ static const struct iwl_prph_range iwl_prph_dump_addr_9000[] = {
        { .start = 0x00a02400, .end = 0x00a02758 },
 };
 
+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 bool iwl_read_prph_block(struct iwl_trans *trans, u32 start,
+                               u32 len_bytes, __le32 *data)
+{
+       unsigned long flags;
+       bool success = false;
+
+       if (iwl_trans_grab_nic_access(trans, &flags)) {
+               success = true;
+               _iwl_read_prph_block(trans, start, len_bytes, data);
+               iwl_trans_release_nic_access(trans, &flags);
+       }
+
+       return success;
+}
+
 static void iwl_dump_prph(struct iwl_trans *trans,
                          struct iwl_fw_error_dump_data **data,
                          const struct iwl_prph_range *iwl_prph_dump_addr,
@@ -422,21 +446,18 @@ static void iwl_dump_prph(struct iwl_trans *trans,
                /* The range includes both boundaries */
                int num_bytes_in_chunk = iwl_prph_dump_addr[i].end -
                         iwl_prph_dump_addr[i].start + 4;
-               int reg;
-               __le32 *val;
 
                (*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);
-               val = (void *)prph->data;
 
-               for (reg = iwl_prph_dump_addr[i].start;
-                    reg <= iwl_prph_dump_addr[i].end;
-                    reg += 4)
-                       *val++ = cpu_to_le32(iwl_read_prph_no_grab(trans,
-                                                                  reg));
+               _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);
        }
@@ -716,16 +737,36 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
        for (i = 0; i < mvm->fw->n_dbg_mem_tlv; i++) {
                u32 len = le32_to_cpu(fw_dbg_mem[i].len);
                u32 ofs = le32_to_cpu(fw_dbg_mem[i].ofs);
+               bool success;
 
                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 = fw_dbg_mem[i].data_type;
                dump_mem->offset = cpu_to_le32(ofs);
-               iwl_trans_read_mem_bytes(mvm->trans, ofs,
-                                        dump_mem->data,
-                                        len);
-               dump_data = iwl_fw_error_next_data(dump_data);
+
+               switch (dump_mem->type & cpu_to_le32(FW_DBG_MEM_TYPE_MASK)) {
+               case cpu_to_le32(FW_DBG_MEM_TYPE_REGULAR):
+                       iwl_trans_read_mem_bytes(mvm->trans, ofs,
+                                                dump_mem->data,
+                                                len);
+                       success = true;
+                       break;
+               case cpu_to_le32(FW_DBG_MEM_TYPE_PRPH):
+                       success = iwl_read_prph_block(mvm->trans, ofs, len,
+                                                     (void *)dump_mem->data);
+                       break;
+               default:
+                       /*
+                        * shouldn't get here, we ignored this kind
+                        * of TLV earlier during the TLV parsing?!
+                        */
+                       WARN_ON(1);
+                       success = false;
+               }
+
+               if (success)
+                       dump_data = iwl_fw_error_next_data(dump_data);
        }
 
        if (smem_len) {