Commit 909e1be6 authored by Johannes Berg's avatar Johannes Berg Committed by Miri Korenblit
Browse files

wifi: iwlwifi: implement TOP reset



Implement TOP reset (new in the SC family), which resets much
of the (shared) hardware without resetting the bus interfaces.
Use it to recover from TOP fatal error, or if manually used;
we'll need to add using it for FSEQ updates later.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20250430155443.12f38024a3b4.I9c22f6c4f6de64f3b34ccd898370ec1859ab7dbf@changeid
parent abbcea13
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ enum iwl_prph_scratch_mtr_format {
 * @IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K: 16kB RB size
 * @IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE: Indicate fw to set SCU_FORCE_ACTIVE
 *	upon reset.
 * @IWL_PRPH_SCRATCH_TOP_RESET: request TOP reset
 */
enum iwl_prph_scratch_flags {
	IWL_PRPH_SCRATCH_IMR_DEBUG_EN		= BIT(1),
@@ -74,6 +75,7 @@ enum iwl_prph_scratch_flags {
	IWL_PRPH_SCRATCH_RB_SIZE_EXT_12K	= 9 << 20,
	IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K	= 10 << 20,
	IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE	= BIT(29),
	IWL_PRPH_SCRATCH_TOP_RESET		= BIT(30),
};

/**
@@ -321,8 +323,9 @@ struct iwl_context_info_gen3 {
	__le32 reserved;
} __packed; /* IPC_CONTEXT_INFO_S */

int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
int iwl_pcie_ctxt_info_gen3_alloc(struct iwl_trans *trans,
				  const struct fw_img *fw);
void iwl_pcie_ctxt_info_gen3_kick(struct iwl_trans *trans);
void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive);

int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans,
+6 −0
Original line number Diff line number Diff line
@@ -53,6 +53,9 @@ struct iwl_cfg;
 *	the device will be shut down
 * @IWL_ERR_TYPE_CMD_QUEUE_FULL: command queue was full
 * @IWL_ERR_TYPE_TOP_RESET_BY_BT: TOP reset initiated by BT
 * @IWL_ERR_TYPE_TOP_FATAL_ERROR: TOP fatal error
 * @IWL_ERR_TYPE_TOP_RESET_FAILED: TOP reset failed
 * @IWL_ERR_TYPE_DEBUGFS: error/reset indication from debugfs
 */
enum iwl_fw_error_type {
	IWL_ERR_TYPE_IRQ,
@@ -60,6 +63,9 @@ enum iwl_fw_error_type {
	IWL_ERR_TYPE_RESET_HS_TIMEOUT,
	IWL_ERR_TYPE_CMD_QUEUE_FULL,
	IWL_ERR_TYPE_TOP_RESET_BY_BT,
	IWL_ERR_TYPE_TOP_FATAL_ERROR,
	IWL_ERR_TYPE_TOP_RESET_FAILED,
	IWL_ERR_TYPE_DEBUGFS,
};

/**
+40 −7
Original line number Diff line number Diff line
@@ -130,18 +130,46 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
	struct iwl_trans_dev_restart_data *data;
	enum iwl_reset_mode at_least = 0;
	unsigned int index;
	static const enum iwl_reset_mode escalation_list[] = {
	static const enum iwl_reset_mode escalation_list_old[] = {
		IWL_RESET_MODE_SW_RESET,
		IWL_RESET_MODE_REPROBE,
		IWL_RESET_MODE_REPROBE,
		IWL_RESET_MODE_FUNC_RESET,
		/* FIXME: add TOP reset */
		IWL_RESET_MODE_PROD_RESET,
		/* FIXME: add TOP reset */
	};
	static const enum iwl_reset_mode escalation_list_sc[] = {
		IWL_RESET_MODE_SW_RESET,
		IWL_RESET_MODE_REPROBE,
		IWL_RESET_MODE_REPROBE,
		IWL_RESET_MODE_FUNC_RESET,
		IWL_RESET_MODE_TOP_RESET,
		IWL_RESET_MODE_PROD_RESET,
		IWL_RESET_MODE_TOP_RESET,
		IWL_RESET_MODE_PROD_RESET,
		/* FIXME: add TOP reset */
		IWL_RESET_MODE_TOP_RESET,
		IWL_RESET_MODE_PROD_RESET,
	};
	const enum iwl_reset_mode *escalation_list;
	size_t escalation_list_size;

	/* used by TOP fatal error/TOP reset */
	if (trans->restart.mode.type == IWL_ERR_TYPE_TOP_RESET_FAILED)
		return IWL_RESET_MODE_PROD_RESET;

	if (trans->request_top_reset) {
		trans->request_top_reset = 0;
		if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_SC)
			return IWL_RESET_MODE_TOP_RESET;
		return IWL_RESET_MODE_PROD_RESET;
	}

	if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_SC) {
		escalation_list = escalation_list_sc;
		escalation_list_size = ARRAY_SIZE(escalation_list_sc);
	} else {
		escalation_list = escalation_list_old;
		escalation_list_size = ARRAY_SIZE(escalation_list_old);
	}

	if (trans->restart.during_reset)
		at_least = IWL_RESET_MODE_REPROBE;
@@ -155,8 +183,8 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans)
		data->restart_count = 0;

	index = data->restart_count;
	if (index >= ARRAY_SIZE(escalation_list))
		index = ARRAY_SIZE(escalation_list) - 1;
	if (index >= escalation_list_size)
		index = escalation_list_size - 1;

	return max(at_least, escalation_list[index]);
}
@@ -203,7 +231,12 @@ static void iwl_trans_restart_wk(struct work_struct *wk)
	iwl_trans_inc_restart_count(trans->dev);

	switch (mode) {
	case IWL_RESET_MODE_TOP_RESET:
		trans->do_top_reset = 1;
		IWL_ERR(trans, "Device error - TOP reset\n");
		fallthrough;
	case IWL_RESET_MODE_SW_RESET:
		if (mode == IWL_RESET_MODE_SW_RESET)
			IWL_ERR(trans, "Device error - SW reset\n");
		iwl_trans_opmode_sw_reset(trans, trans->restart.mode.type);
		break;
+9 −0
Original line number Diff line number Diff line
@@ -893,6 +893,10 @@ struct iwl_txq {
 * @dsbr_urm_fw_dependent: switch to URM based on fw settings
 * @dsbr_urm_permanent: switch to URM permanently
 * @ext_32khz_clock_valid: if true, the external 32 KHz clock can be used
 * @request_top_reset: TOP reset was requested, used by the reset
 *	worker that should be scheduled (with appropriate reason)
 * @do_top_reset: indication to the (PCIe) transport/context-info
 *	to do the TOP reset
 */
struct iwl_trans {
	bool csme_own;
@@ -974,6 +978,9 @@ struct iwl_trans {
	struct delayed_work me_recheck_wk;
	s8 me_present;

	u8 request_top_reset:1,
	   do_top_reset:1;

	/* pointer to trans specific struct */
	/*Ensure that this pointer will always be aligned to sizeof pointer */
	char trans_specific[] __aligned(sizeof(void *));
@@ -1267,6 +1274,8 @@ enum iwl_reset_mode {
	/* upper level modes: */
	IWL_RESET_MODE_SW_RESET,
	IWL_RESET_MODE_REPROBE,
	/* TOP reset doesn't require PCIe remove */
	IWL_RESET_MODE_TOP_RESET,
	/* PCIE level modes: */
	IWL_RESET_MODE_REMOVE_ONLY,
	IWL_RESET_MODE_RESCAN,
+24 −14
Original line number Diff line number Diff line
@@ -97,7 +97,7 @@ iwl_pcie_ctxt_info_dbg_enable(struct iwl_trans *trans,
		*control_flags |= IWL_PRPH_SCRATCH_EARLY_DEBUG_EN | dbg_flags;
}

int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
int iwl_pcie_ctxt_info_gen3_alloc(struct iwl_trans *trans,
				  const struct fw_img *fw)
{
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
@@ -168,6 +168,11 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,
			     IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE);
	}

	if (trans->do_top_reset) {
		WARN_ON(trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_SC);
		control_flags |= IWL_PRPH_SCRATCH_TOP_RESET;
	}

	/* initialize RX default queue */
	prph_sc_ctrl->rbd_cfg.free_rbd_addr =
		cpu_to_le64(trans_pcie->rxq->bd_dma);
@@ -266,18 +271,6 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,

	memcpy(trans_pcie->iml, trans->iml, trans->iml_len);

	iwl_enable_fw_load_int_ctx_info(trans);

	/* kick FW self load */
	iwl_write64(trans, CSR_CTXT_INFO_ADDR,
		    trans_pcie->ctxt_info_dma_addr);
	iwl_write64(trans, CSR_IML_DATA_ADDR,
		    trans_pcie->iml_dma_addr);
	iwl_write32(trans, CSR_IML_SIZE_ADDR, trans->iml_len);

	iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL,
		    CSR_AUTO_FUNC_BOOT_ENA);

	return 0;

err_free_ctxt_info:
@@ -298,6 +291,23 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans,

}

void iwl_pcie_ctxt_info_gen3_kick(struct iwl_trans *trans)
{
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);

	iwl_enable_fw_load_int_ctx_info(trans, trans->do_top_reset);

	/* kick FW self load */
	iwl_write64(trans, CSR_CTXT_INFO_ADDR,
		    trans_pcie->ctxt_info_dma_addr);
	iwl_write64(trans, CSR_IML_DATA_ADDR,
		    trans_pcie->iml_dma_addr);
	iwl_write32(trans, CSR_IML_SIZE_ADDR, trans->iml_len);

	iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL,
		    CSR_AUTO_FUNC_BOOT_ENA);
}

void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive)
{
	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
Loading