]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
iwlwifi: support internal debug data collection for new devices
authorGolan Ben Ami <golan.ben.ami@intel.com>
Wed, 16 Aug 2017 09:14:56 +0000 (12:14 +0300)
committerLuca Coelho <luciano.coelho@intel.com>
Wed, 20 Dec 2017 16:28:23 +0000 (18:28 +0200)
Support internal debug data collection on 9000 and newer
devices.
The method for finding the base and end address has changed
on new HW's, so introduce a new version of debug destination
tlv.

Signed-off-by: Golan Ben Ami <golan.ben.ami@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/fw/file.h
drivers/net/wireless/intel/iwlwifi/fw/img.h
drivers/net/wireless/intel/iwlwifi/iwl-drv.c
drivers/net/wireless/intel/iwlwifi/iwl-trans.h
drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c
drivers/net/wireless/intel/iwlwifi/pcie/trans.c

index 4687d016f6767ea219cbf5b5a37be1a312327448..0824c007b6f8b583f4663cdf0f5079d47fe2a3d8 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2016        Intel Deutschland GmbH
+ * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,7 @@
  *
  * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2016        Intel Deutschland GmbH
+ * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -540,7 +540,7 @@ struct iwl_fw_dbg_mem_seg_tlv {
 } __packed;
 
 /**
- * struct iwl_fw_dbg_dest_tlv - configures the destination of the debug data
+ * struct iwl_fw_dbg_dest_tlv_v1 - configures the destination of the debug data
  *
  * @version: version of the TLV - currently 0
  * @monitor_mode: &enum iwl_fw_dbg_monitor_mode
@@ -555,7 +555,7 @@ struct iwl_fw_dbg_mem_seg_tlv {
  *
  * This parses IWL_UCODE_TLV_FW_DBG_DEST
  */
-struct iwl_fw_dbg_dest_tlv {
+struct iwl_fw_dbg_dest_tlv_v1 {
        u8 version;
        u8 monitor_mode;
        u8 size_power;
@@ -569,6 +569,26 @@ struct iwl_fw_dbg_dest_tlv {
        struct iwl_fw_dbg_reg_op reg_ops[0];
 } __packed;
 
+/* Mask of the register for defining the LDBG MAC2SMEM buffer SMEM size */
+#define IWL_LDBG_M2S_BUF_SIZE_MSK      0x0fff0000
+/* Mask of the register for defining the LDBG MAC2SMEM SMEM base address */
+#define IWL_LDBG_M2S_BUF_BA_MSK                0x00000fff
+/* The smem buffer chunks are in units of 256 bits */
+#define IWL_M2S_UNIT_SIZE                      0x100
+
+struct iwl_fw_dbg_dest_tlv {
+       u8 version;
+       u8 monitor_mode;
+       u8 size_power;
+       u8 reserved;
+       __le32 cfg_reg;
+       __le32 write_ptr_reg;
+       __le32 wrap_count;
+       u8 base_shift;
+       u8 size_shift;
+       struct iwl_fw_dbg_reg_op reg_ops[0];
+} __packed;
+
 struct iwl_fw_dbg_conf_hcmd {
        u8 id;
        u8 reserved;
index 985496cc01d0f47b503f19d440c033cc1dbfd1d9..b23ffe12ad84d1b747bd812c0e0e601127e6ae71 100644 (file)
@@ -284,7 +284,7 @@ struct iwl_fw {
        struct iwl_fw_cipher_scheme cs[IWL_UCODE_MAX_CS];
        u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
 
-       struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+       struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv;
        struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
        size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
        struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
index de8f6ae2f51bdbd15c3cfe26ecf374610c386844..9c4a7f648a44e263b4a16142a68d9fb67c0a0059 100644 (file)
@@ -296,7 +296,12 @@ struct iwl_firmware_pieces {
        u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
 
        /* FW debug data parsed for driver usage */
-       struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+       bool dbg_dest_tlv_init;
+       u8 *dbg_dest_ver;
+       union {
+               struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+               struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv_v1;
+       };
        struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
        size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
        struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
@@ -930,21 +935,49 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                        break;
                        }
                case IWL_UCODE_TLV_FW_DBG_DEST: {
-                       struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
+                       struct iwl_fw_dbg_dest_tlv *dest = NULL;
+                       struct iwl_fw_dbg_dest_tlv_v1 *dest_v1 = NULL;
+                       u8 mon_mode;
+
+                       pieces->dbg_dest_ver = (u8 *)tlv_data;
+                       if (*pieces->dbg_dest_ver == 1) {
+                               dest = (void *)tlv_data;
+                       } else if (*pieces->dbg_dest_ver == 0) {
+                               dest_v1 = (void *)tlv_data;
+                       } else {
+                               IWL_ERR(drv,
+                                       "The version is %d, and it is invalid\n",
+                                       *pieces->dbg_dest_ver);
+                               break;
+                       }
 
-                       if (pieces->dbg_dest_tlv) {
+                       if (pieces->dbg_dest_tlv_init) {
                                IWL_ERR(drv,
                                        "dbg destination ignored, already exists\n");
                                break;
                        }
 
-                       pieces->dbg_dest_tlv = dest;
+                       pieces->dbg_dest_tlv_init = true;
+
+                       if (dest_v1) {
+                               pieces->dbg_dest_tlv_v1 = dest_v1;
+                               mon_mode = dest_v1->monitor_mode;
+                       } else {
+                               pieces->dbg_dest_tlv = dest;
+                               mon_mode = dest->monitor_mode;
+                       }
+
                        IWL_INFO(drv, "Found debug destination: %s\n",
-                                get_fw_dbg_mode_string(dest->monitor_mode));
+                                get_fw_dbg_mode_string(mon_mode));
+
+                       drv->fw.dbg_dest_reg_num = (dest_v1) ?
+                               tlv_len -
+                               offsetof(struct iwl_fw_dbg_dest_tlv_v1,
+                                        reg_ops) :
+                               tlv_len -
+                               offsetof(struct iwl_fw_dbg_dest_tlv,
+                                        reg_ops);
 
-                       drv->fw.dbg_dest_reg_num =
-                               tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
-                                                  reg_ops);
                        drv->fw.dbg_dest_reg_num /=
                                sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
 
@@ -953,7 +986,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
                case IWL_UCODE_TLV_FW_DBG_CONF: {
                        struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
 
-                       if (!pieces->dbg_dest_tlv) {
+                       if (!pieces->dbg_dest_tlv_init) {
                                IWL_ERR(drv,
                                        "Ignore dbg config %d - no destination configured\n",
                                        conf->id);
@@ -1340,15 +1373,51 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
                if (iwl_alloc_ucode(drv, pieces, i))
                        goto out_free_fw;
 
-       if (pieces->dbg_dest_tlv) {
-               drv->fw.dbg_dest_tlv =
-                       kmemdup(pieces->dbg_dest_tlv,
-                               sizeof(*pieces->dbg_dest_tlv) +
-                               sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
-                               drv->fw.dbg_dest_reg_num, GFP_KERNEL);
+       if (pieces->dbg_dest_tlv_init) {
+               size_t dbg_dest_size = sizeof(*drv->fw.dbg_dest_tlv) +
+                       sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]) *
+                       drv->fw.dbg_dest_reg_num;
+
+               drv->fw.dbg_dest_tlv = kmalloc(dbg_dest_size, GFP_KERNEL);
 
                if (!drv->fw.dbg_dest_tlv)
                        goto out_free_fw;
+
+               if (*pieces->dbg_dest_ver == 0) {
+                       memcpy(drv->fw.dbg_dest_tlv, pieces->dbg_dest_tlv_v1,
+                              dbg_dest_size);
+               } else {
+                       struct iwl_fw_dbg_dest_tlv_v1 *dest_tlv =
+                               drv->fw.dbg_dest_tlv;
+
+                       dest_tlv->version = pieces->dbg_dest_tlv->version;
+                       dest_tlv->monitor_mode =
+                               pieces->dbg_dest_tlv->monitor_mode;
+                       dest_tlv->size_power =
+                               pieces->dbg_dest_tlv->size_power;
+                       dest_tlv->wrap_count =
+                               pieces->dbg_dest_tlv->wrap_count;
+                       dest_tlv->write_ptr_reg =
+                               pieces->dbg_dest_tlv->write_ptr_reg;
+                       dest_tlv->base_shift =
+                               pieces->dbg_dest_tlv->base_shift;
+                       memcpy(dest_tlv->reg_ops,
+                              pieces->dbg_dest_tlv->reg_ops,
+                              sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]) *
+                              drv->fw.dbg_dest_reg_num);
+
+                       /* In version 1 of the destination tlv, which is
+                        * relevant for internal buffer exclusively,
+                        * the base address is part of given with the length
+                        * of the buffer, and the size shift is give instead of
+                        * end shift. We now store these values in base_reg,
+                        * and end shift, and when dumping the data we'll
+                        * manipulate it for extracting both the length and
+                        * base address */
+                       dest_tlv->base_reg = pieces->dbg_dest_tlv->cfg_reg;
+                       dest_tlv->end_shift =
+                               pieces->dbg_dest_tlv->size_shift;
+               }
        }
 
        for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
index 84ae1e274d3855ebe25f307afd05372eed45423c..af50d78cc193e90b9a62daafef41067ae573c5f7 100644 (file)
@@ -744,7 +744,7 @@ struct iwl_trans {
        struct lockdep_map sync_cmd_lockdep_map;
 #endif
 
-       const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
+       const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv;
        const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
        struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
        u8 dbg_dest_reg_num;
index 270781e13e893ed66f423e20d20d345c899995fb..037dc4a0f13370c9dfeedba086ebda1242572c5c 100644 (file)
@@ -1221,7 +1221,7 @@ static ssize_t iwl_dbgfs_cont_recording_write(struct iwl_mvm *mvm,
                                              loff_t *ppos)
 {
        struct iwl_trans *trans = mvm->trans;
-       const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+       const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg_dest_tlv;
        struct iwl_continuous_record_cmd cont_rec = {};
        int ret, rec_mode;
 
index fbc45361f0bb516e3c74c378eb60aaa6f9c10518..2652e099281957a6da55d36365785d82137c185c 100644 (file)
@@ -915,14 +915,9 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
 void iwl_pcie_apply_destination(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
-       const struct iwl_fw_dbg_dest_tlv *dest = trans->dbg_dest_tlv;
+       const struct iwl_fw_dbg_dest_tlv_v1 *dest = trans->dbg_dest_tlv;
        int i;
 
-       if (dest->version)
-               IWL_ERR(trans,
-                       "DBG DEST version is %d - expect issues\n",
-                       dest->version);
-
        IWL_INFO(trans, "Applying debug destination %s\n",
                 get_fw_dbg_mode_string(dest->monitor_mode));
 
@@ -2816,8 +2811,17 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans,
                         * Update pointers to reflect actual values after
                         * shifting
                         */
-                       base = iwl_read_prph(trans, base) <<
-                              trans->dbg_dest_tlv->base_shift;
+                       if (trans->dbg_dest_tlv->version) {
+                               base = (iwl_read_prph(trans, base) &
+                                       IWL_LDBG_M2S_BUF_BA_MSK) <<
+                                      trans->dbg_dest_tlv->base_shift;
+                               base *= IWL_M2S_UNIT_SIZE;
+                               base += trans->cfg->smem_offset;
+                       } else {
+                               base = iwl_read_prph(trans, base) <<
+                                      trans->dbg_dest_tlv->base_shift;
+                       }
+
                        iwl_trans_read_mem(trans, base, fw_mon_data->data,
                                           monitor_len / sizeof(u32));
                } else if (trans->dbg_dest_tlv->monitor_mode == MARBH_MODE) {
@@ -2865,21 +2869,36 @@ static struct iwl_trans_dump_data
                       trans_pcie->fw_mon_size;
                monitor_len = trans_pcie->fw_mon_size;
        } else if (trans->dbg_dest_tlv) {
-               u32 base, end;
+               u32 base, end, cfg_reg;
 
-               base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
-               end = le32_to_cpu(trans->dbg_dest_tlv->end_reg);
+               if (trans->dbg_dest_tlv->version == 1) {
+                       cfg_reg = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+                       cfg_reg = iwl_read_prph(trans, cfg_reg);
+                       base = (cfg_reg & IWL_LDBG_M2S_BUF_BA_MSK) <<
+                               trans->dbg_dest_tlv->base_shift;
+                       base *= IWL_M2S_UNIT_SIZE;
+                       base += trans->cfg->smem_offset;
 
-               base = iwl_read_prph(trans, base) <<
-                      trans->dbg_dest_tlv->base_shift;
-               end = iwl_read_prph(trans, end) <<
-                     trans->dbg_dest_tlv->end_shift;
+                       monitor_len =
+                               (cfg_reg & IWL_LDBG_M2S_BUF_SIZE_MSK) >>
+                               trans->dbg_dest_tlv->end_shift;
+                       monitor_len *= IWL_M2S_UNIT_SIZE;
+               } else {
+                       base = le32_to_cpu(trans->dbg_dest_tlv->base_reg);
+                       end = le32_to_cpu(trans->dbg_dest_tlv->end_reg);
 
-               /* Make "end" point to the actual end */
-               if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000 ||
-                   trans->dbg_dest_tlv->monitor_mode == MARBH_MODE)
-                       end += (1 << trans->dbg_dest_tlv->end_shift);
-               monitor_len = end - base;
+                       base = iwl_read_prph(trans, base) <<
+                              trans->dbg_dest_tlv->base_shift;
+                       end = iwl_read_prph(trans, end) <<
+                             trans->dbg_dest_tlv->end_shift;
+
+                       /* Make "end" point to the actual end */
+                       if (trans->cfg->device_family >=
+                           IWL_DEVICE_FAMILY_8000 ||
+                           trans->dbg_dest_tlv->monitor_mode == MARBH_MODE)
+                               end += (1 << trans->dbg_dest_tlv->end_shift);
+                       monitor_len = end - base;
+               }
                len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
                       monitor_len;
        } else {