]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: iwlwifi: implement dump region split
authorJohannes Berg <johannes.berg@intel.com>
Wed, 5 Feb 2025 12:55:48 +0000 (14:55 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Tue, 11 Feb 2025 10:59:48 +0000 (11:59 +0100)
Due to hardware design constraints, a reset handshake may be
necessary even when the firmware has already crashed, with
the dump descriptions indicating which parts should be done
before/after the handshake, if needed. Implement that.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250205145347.9296e3113d42.Ifb32703fd06a644d08a86b7af1b990738e3c8134@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h
drivers/net/wireless/intel/iwlwifi/fw/dbg.c
drivers/net/wireless/intel/iwlwifi/iwl-trans.h
drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c

index 550de6db18340ba3ba85f6bb1282b84753791604..4fab6c66994e5284416388441c0f27f64248deda 100644 (file)
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /*
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
  */
 #ifndef __iwl_fw_dbg_tlv_h__
 #define __iwl_fw_dbg_tlv_h__
@@ -527,6 +527,8 @@ enum iwl_fw_ini_time_point {
  * @IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA: override trigger data.
  *     Append otherwise
  * @IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD: send cmd once dump collected
+ * @IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE: perform reset handshake and
+ *     split dump to before/after with region marking
  */
 enum iwl_fw_ini_trigger_apply_policy {
        IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT        = BIT(0),
@@ -535,6 +537,7 @@ enum iwl_fw_ini_trigger_apply_policy {
        IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG            = BIT(9),
        IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA           = BIT(10),
        IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD       = BIT(16),
+       IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE         = BIT(17),
 };
 
 /**
@@ -556,12 +559,14 @@ enum iwl_fw_ini_trigger_reset_fw_policy {
  * @IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT: OS has no limit of dump size
  * @IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB: mini dump only 600KB region dump
  * @IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB: mini dump 5MB size dump
+ * @IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET: dump this region before reset
+ *     handshake (if requested by %IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE)
  */
 enum iwl_fw_ini_dump_policy {
        IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT           = BIT(0),
        IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB    = BIT(1),
        IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB      = BIT(2),
-
+       IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET       = BIT(3),
 };
 
 /**
index 6819c615fef611e99d74a9d20867fff433339e58..634e2543ba6dd7675ae261040247b312e7c73a45 100644 (file)
@@ -2588,29 +2588,28 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = {
        },
 };
 
-static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
-                               struct iwl_fwrt_dump_data *dump_data,
-                               struct list_head *list)
+enum iwl_dump_ini_region_selector {
+       IWL_INI_DUMP_ALL_REGIONS,
+       IWL_INI_DUMP_EARLY_REGIONS,
+       IWL_INI_DUMP_LATE_REGIONS,
+};
+
+static u32
+iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt,
+                         struct iwl_fwrt_dump_data *dump_data,
+                         struct list_head *list,
+                         enum iwl_fw_ini_time_point tp_id,
+                         u64 regions_mask,
+                         struct iwl_dump_ini_region_data *imr_reg_data,
+                         enum iwl_dump_ini_region_selector which)
 {
-       struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig;
-       enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point);
-       struct iwl_dump_ini_region_data reg_data = {
-               .dump_data = dump_data,
-       };
-       struct iwl_dump_ini_region_data imr_reg_data = {
-               .dump_data = dump_data,
-       };
-       int i;
        u32 size = 0;
-       u64 regions_mask = le64_to_cpu(trigger->regions_mask) &
-                          ~(fwrt->trans->dbg.unsupported_region_msk);
 
-       BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask));
-       BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) <
-                    ARRAY_SIZE(fwrt->trans->dbg.active_regions));
-
-       for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) {
-               u32 reg_type;
+       for (int i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) {
+               struct iwl_dump_ini_region_data reg_data = {
+                       .dump_data = dump_data,
+               };
+               u32 reg_type, dp;
                struct iwl_fw_ini_region_tlv *reg;
 
                if (!(BIT_ULL(i) & regions_mask))
@@ -2628,6 +2627,8 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
                if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops))
                        continue;
 
+               dp = le32_get_bits(reg->id, IWL_FW_INI_REGION_DUMP_POLICY_MASK);
+
                if ((reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY ||
                     reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE ||
                     reg_type == IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP) &&
@@ -2637,6 +2638,20 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
                                 tp_id);
                        continue;
                }
+
+               switch (which) {
+               case IWL_INI_DUMP_ALL_REGIONS:
+                       break;
+               case IWL_INI_DUMP_EARLY_REGIONS:
+                       if (!(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET))
+                               continue;
+                       break;
+               case IWL_INI_DUMP_LATE_REGIONS:
+                       if (dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET)
+                               continue;
+                       break;
+               }
+
                /*
                 * DRAM_IMR can be collected only for FW/HW error timepoint
                 * when fw is not alive. In addition, it must be collected
@@ -2646,7 +2661,8 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
                if (reg_type == IWL_FW_INI_REGION_DRAM_IMR) {
                        if (tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT ||
                            tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR)
-                               imr_reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i];
+                               imr_reg_data->reg_tlv =
+                                       fwrt->trans->dbg.active_regions[i];
                        else
                                IWL_INFO(fwrt,
                                         "WRT: trying to collect DRAM_IMR at time point: %d, skipping\n",
@@ -2659,6 +2675,41 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
                size += iwl_dump_ini_mem(fwrt, list, &reg_data,
                                         &iwl_dump_ini_region_ops[reg_type]);
        }
+
+       return size;
+}
+
+static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt,
+                               struct iwl_fwrt_dump_data *dump_data,
+                               struct list_head *list)
+{
+       struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig;
+       enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point);
+       struct iwl_dump_ini_region_data imr_reg_data = {
+               .dump_data = dump_data,
+       };
+       u32 size = 0;
+       u64 regions_mask = le64_to_cpu(trigger->regions_mask) &
+                          ~(fwrt->trans->dbg.unsupported_region_msk);
+
+       BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask));
+       BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) <
+                    ARRAY_SIZE(fwrt->trans->dbg.active_regions));
+
+       if (trigger->time_point &
+                       cpu_to_le32(IWL_FW_INI_APPLY_POLICY_RESET_HANDSHAKE)) {
+               size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
+                                                 regions_mask, &imr_reg_data,
+                                                 IWL_INI_DUMP_EARLY_REGIONS);
+               iwl_trans_pcie_fw_reset_handshake(fwrt->trans);
+               size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
+                                                 regions_mask, &imr_reg_data,
+                                                 IWL_INI_DUMP_LATE_REGIONS);
+       } else {
+               size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id,
+                                                 regions_mask, &imr_reg_data,
+                                                 IWL_INI_DUMP_ALL_REGIONS);
+       }
        /* collect DRAM_IMR region in the last */
        if (imr_reg_data.reg_tlv)
                size += iwl_dump_ini_mem(fwrt, list, &imr_reg_data,
@@ -3071,6 +3122,7 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
        struct iwl_fw_dbg_params params = {0};
        struct iwl_fwrt_dump_data *dump_data =
                &fwrt->dump.wks[wk_idx].dump_data;
+
        if (!test_bit(wk_idx, &fwrt->dump.active_wks))
                return;
 
@@ -3095,9 +3147,9 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
 
        IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection start\n");
        if (iwl_trans_dbg_ini_valid(fwrt->trans))
-               iwl_fw_error_ini_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data);
+               iwl_fw_error_ini_dump(fwrt, dump_data);
        else
-               iwl_fw_error_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data);
+               iwl_fw_error_dump(fwrt, dump_data);
        IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection done\n");
 
        iwl_fw_dbg_stop_restart_recording(fwrt, &params, false);
@@ -3114,7 +3166,6 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)
 
        if (fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY)
                iwl_force_nmi(fwrt->trans);
-
 out:
        if (iwl_trans_dbg_ini_valid(fwrt->trans)) {
                iwl_fw_error_dump_data_free(dump_data);
index 3561ff8483f20bec24fab64cf6375493a8a13c5b..021691513a576798e1c9fa3cbaeac9369f385df2 100644 (file)
@@ -1262,6 +1262,7 @@ enum iwl_reset_mode {
 };
 
 void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode);
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans);
 
 int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
                             struct iwl_host_cmd *cmd);
index 793514a1852a3dc29d89fcb86461800d8e33c382..3ece34e30d580642a3ac177bc04be614048a95b3 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
  * Copyright (C) 2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2024 Intel Corporation
+ * Copyright (C) 2018-2025 Intel Corporation
  */
 #include "iwl-trans.h"
 #include "iwl-prph.h"
@@ -95,7 +95,7 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave)
                              CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 }
 
-static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
+void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans)
 {
        struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
        int ret;