Commit ddb6b76b authored by Mukesh Sisodiya's avatar Mukesh Sisodiya Committed by Luca Coelho
Browse files

iwlwifi: yoyo: support TLV-based firmware reset



Support resetting the firmware via TLV-based debugging.  When applied,
this will cause the driver to reset the firmware when the debugging
is triggered.

Signed-off-by: default avatarMukesh Sisodiya <mukesh.sisodiya@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
Link: https://lore.kernel.org/r/iwlwifi.20211219121514.d59b29653a1e.I7b3be4a1ad1a9d5d0e86259740e89ac113c9348b@changeid


Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 3efdf03b
Loading
Loading
Loading
Loading
+14 −1
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ struct iwl_fw_ini_region_internal_buffer {
 * @id: region id. Max id is &IWL_FW_INI_MAX_REGION_ID
 * @type: region type. One of &enum iwl_fw_ini_region_type
 * @sub_type: region sub type
 * @sub_type_ver: region sub type
 * @sub_type_ver: region sub type version
 * @reserved: not in use
 * @name: region name
 * @dev_addr: device address configuration. Used by
@@ -483,4 +483,17 @@ 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),
};

/**
 * enum iwl_fw_ini_trigger_reset_fw_policy - Determines how to handle reset
 *
 * @IWL_FW_INI_RESET_FW_MODE_NOTHING: do not stop FW and reload (default)
 * @IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY: stop FW without reload FW
 * @IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW: stop FW with reload FW
 */
enum iwl_fw_ini_trigger_reset_fw_policy {
	IWL_FW_INI_RESET_FW_MODE_NOTHING = 0,
	IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY,
	IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW
};
#endif
+3 −0
Original line number Diff line number Diff line
@@ -2719,6 +2719,9 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx)

	iwl_fw_dbg_stop_restart_recording(fwrt, &params, false);

	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);
+42 −1
Original line number Diff line number Diff line
@@ -233,6 +233,7 @@ static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans,
	const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data;
	struct iwl_fw_ini_trigger_tlv *dup_trig;
	u32 tp = le32_to_cpu(trig->time_point);
	u32 rf = le32_to_cpu(trig->reset_fw);
	struct iwl_ucode_tlv *dup = NULL;
	int ret;

@@ -247,6 +248,10 @@ static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans,
		return -EINVAL;
	}

	IWL_DEBUG_FW(trans,
		     "WRT: time point %u for trigger TLV with reset_fw %u\n",
		     tp, rf);
	trans->dbg.last_tp_resetfw = 0xFF;
	if (!le32_to_cpu(trig->occurrences)) {
		dup = kmemdup(tlv, sizeof(*tlv) + le32_to_cpu(tlv->length),
				GFP_KERNEL);
@@ -1166,6 +1171,8 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,
		u32 num_data = iwl_tlv_array_len(&node->tlv, dump_data.trig,
						 data);
		int ret, i;
		u32 tp = le32_to_cpu(dump_data.trig->time_point);


		if (!num_data) {
			ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);
@@ -1184,8 +1191,42 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,
				break;
			}
		}
	}

		fwrt->trans->dbg.restart_required = FALSE;
		IWL_DEBUG_INFO(fwrt, "WRT: tp %d, reset_fw %d\n",
			       tp, dump_data.trig->reset_fw);
		IWL_DEBUG_INFO(fwrt, "WRT: restart_required %d, last_tp_resetfw %d\n",
			       fwrt->trans->dbg.restart_required,
			       fwrt->trans->dbg.last_tp_resetfw);

		if (fwrt->trans->trans_cfg->device_family ==
		    IWL_DEVICE_FAMILY_9000) {
			fwrt->trans->dbg.restart_required = TRUE;
		} else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT &&
			   fwrt->trans->dbg.last_tp_resetfw ==
			   IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {
			fwrt->trans->dbg.restart_required = FALSE;
			fwrt->trans->dbg.last_tp_resetfw = 0xFF;
			IWL_DEBUG_FW(fwrt, "WRT: FW_ASSERT due to reset_fw_mode-no restart\n");
		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==
			   IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) {
			IWL_DEBUG_INFO(fwrt, "WRT: stop and reload firmware\n");
			fwrt->trans->dbg.restart_required = TRUE;
		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==
			   IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {
			IWL_DEBUG_INFO(fwrt, "WRT: stop only and no reload firmware\n");
			fwrt->trans->dbg.restart_required = FALSE;
			fwrt->trans->dbg.last_tp_resetfw =
				le32_to_cpu(dump_data.trig->reset_fw);
		} else if (le32_to_cpu(dump_data.trig->reset_fw) ==
			   IWL_FW_INI_RESET_FW_MODE_NOTHING) {
			IWL_DEBUG_INFO(fwrt,
				       "WRT: nothing need to be done after debug collection\n");
		} else {
			IWL_ERR(fwrt, "WRT: wrong resetfw %d\n",
				le32_to_cpu(dump_data.trig->reset_fw));
		}
	}
	return 0;
}

+2 −0
Original line number Diff line number Diff line
@@ -783,6 +783,8 @@ struct iwl_trans_debug {

	u32 domains_bitmap;
	u32 ucode_preset;
	bool restart_required;
	u32 last_tp_resetfw;
};

struct iwl_dma_ptr {
+10 −3
Original line number Diff line number Diff line
@@ -1836,9 +1836,16 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)

		iwl_fw_error_collect(&mvm->fwrt, false);

		if (fw_error && mvm->fw_restart > 0)
		if (fw_error && mvm->fw_restart > 0) {
			mvm->fw_restart--;
			ieee80211_restart_hw(mvm->hw);
		} else if (mvm->fwrt.trans->dbg.restart_required) {
			IWL_DEBUG_INFO(mvm, "FW restart requested after debug collection\n");
			mvm->fwrt.trans->dbg.restart_required = FALSE;
			ieee80211_restart_hw(mvm->hw);
		} else if (mvm->trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000) {
			ieee80211_restart_hw(mvm->hw);
		}
	}
}

@@ -1869,7 +1876,7 @@ static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode, bool sync)
	if (!test_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status))
		return;

	iwl_mvm_nic_restart(mvm, true);
	iwl_mvm_nic_restart(mvm, false);
}

static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)