Commit e5f3f215 authored by Haim Dreyfuss's avatar Haim Dreyfuss Committed by Luca Coelho
Browse files

iwlwifi: add support for suspend-resume flow for new device generation



The new device generation has a slightly different suspend resume flow
Currently, the way the driver instruct the device to move to D3 is by
sending D3_CONFIG_CMD.
Instead of using the host command the indication is by writing to the
doorbell interrupt.
The FW will respond with interrupt to indicate transition completion.

Signed-off-by: default avatarHaim Dreyfuss <haim.dreyfuss@intel.com>
Signed-off-by: default avatarLuca Coelho <luciano.coelho@intel.com>
parent 973ef19e
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -451,6 +451,8 @@ enum {

#define UREG_DOORBELL_TO_ISR6		0xA05C04
#define UREG_DOORBELL_TO_ISR6_NMI_BIT	BIT(0)
#define UREG_DOORBELL_TO_ISR6_SUSPEND	BIT(18)
#define UREG_DOORBELL_TO_ISR6_RESUME	BIT(19)

#define FSEQ_ERROR_CODE			0xA340C8
#define FSEQ_TOP_INIT_VERSION		0xA34038
@@ -460,4 +462,7 @@ enum {
#define FSEQ_ALIVE_TOKEN		0xA340F0
#define FSEQ_CNVI_ID			0xA3408C
#define FSEQ_CNVR_ID			0xA34090

#define IWL_D3_SLEEP_STATUS_SUSPEND	0xD3
#define IWL_D3_SLEEP_STATUS_RESUME	0xD0
#endif				/* __iwl_prph_h__ */
+7 −5
Original line number Diff line number Diff line
@@ -537,7 +537,7 @@ struct iwl_trans_ops {
	void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr);
	void (*stop_device)(struct iwl_trans *trans);

	void (*d3_suspend)(struct iwl_trans *trans, bool test, bool reset);
	int (*d3_suspend)(struct iwl_trans *trans, bool test, bool reset);
	int (*d3_resume)(struct iwl_trans *trans, enum iwl_d3_status *status,
			 bool test, bool reset);

@@ -883,12 +883,14 @@ static inline void iwl_trans_stop_device(struct iwl_trans *trans)
	trans->state = IWL_TRANS_NO_FW;
}

static inline void iwl_trans_d3_suspend(struct iwl_trans *trans, bool test,
static inline int iwl_trans_d3_suspend(struct iwl_trans *trans, bool test,
				       bool reset)
{
	might_sleep();
	if (trans->ops->d3_suspend)
		trans->ops->d3_suspend(trans, test, reset);
	if (!trans->ops->d3_suspend)
		return 0;

	return trans->ops->d3_suspend(trans, test, reset);
}

static inline int iwl_trans_d3_resume(struct iwl_trans *trans,
+10 −10
Original line number Diff line number Diff line
@@ -1068,7 +1068,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,

	clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);

	iwl_trans_d3_suspend(mvm->trans, test, !unified_image);
	ret = iwl_trans_d3_suspend(mvm->trans, test, !unified_image);
 out:
	if (ret < 0) {
		iwl_mvm_free_nd(mvm);
@@ -1930,15 +1930,6 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
	if (IS_ERR_OR_NULL(vif))
		goto err;

	ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test, !unified_image);
	if (ret)
		goto err;

	if (d3_status != IWL_D3_STATUS_ALIVE) {
		IWL_INFO(mvm, "Device was reset during suspend\n");
		goto err;
	}

	iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);

	if (iwl_mvm_check_rt_status(mvm, vif)) {
@@ -1950,6 +1941,15 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
		goto err;
	}

	ret = iwl_trans_d3_resume(mvm->trans, &d3_status, test, !unified_image);
	if (ret)
		goto err;

	if (d3_status != IWL_D3_STATUS_ALIVE) {
		IWL_INFO(mvm, "Device was reset during suspend\n");
		goto err;
	}

	if (d0i3_first) {
		ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, 0, 0, NULL);
		if (ret < 0) {
+4 −0
Original line number Diff line number Diff line
@@ -558,8 +558,10 @@ struct iwl_trans_pcie {
	void __iomem *hw_base;

	bool ucode_write_complete;
	bool sx_complete;
	wait_queue_head_t ucode_write_waitq;
	wait_queue_head_t wait_command_queue;
	wait_queue_head_t sx_waitq;

	u8 page_offs, dev_cmd_offs;

@@ -1117,4 +1119,6 @@ void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans);
void iwl_pcie_gen2_txq_unmap(struct iwl_trans *trans, int txq_id);
void iwl_pcie_gen2_tx_free(struct iwl_trans *trans);
void iwl_pcie_gen2_tx_stop(struct iwl_trans *trans);
void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans,
				  bool test, bool reset);
#endif /* __iwl_trans_int_pcie_h__ */
+16 −5
Original line number Diff line number Diff line
@@ -2196,6 +2196,16 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
			iwl_pcie_irq_handle_error(trans);
		}
	} else if (inta_hw & MSIX_HW_INT_CAUSES_REG_WAKEUP) {
		u32 sleep_notif =
			le32_to_cpu(trans_pcie->prph_info->sleep_notif);
		if (sleep_notif == IWL_D3_SLEEP_STATUS_SUSPEND ||
		    sleep_notif == IWL_D3_SLEEP_STATUS_RESUME) {
			IWL_DEBUG_ISR(trans,
				      "Sx interrupt: sleep notification = 0x%x\n",
				      sleep_notif);
			trans_pcie->sx_complete = true;
			wake_up(&trans_pcie->sx_waitq);
		} else {
			/* uCode wakes up after power-down sleep */
			IWL_DEBUG_ISR(trans, "Wakeup interrupt\n");
			iwl_pcie_rxq_check_wrptr(trans);
@@ -2203,6 +2213,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)

			isr_stats->wakeup++;
		}
	}

	if (inta_hw & MSIX_HW_INT_CAUSES_REG_IML) {
		/* Reflect IML transfer status */
Loading