Commit e8bb19c1 authored by Emmanuel Grumbach's avatar Emmanuel Grumbach Committed by Johannes Berg
Browse files

wifi: iwlwifi: support fast resume



This will allow to suspend / resume the system without resetting the
firmware. This will allow to reduce the resume time.
In case the fast_resume fails, stop the device and bring it up from
scratch.

Raise the timeout for the D3_END notification since in some iterations,
it took 240ms.

Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20240618194245.03b8d2801044.I613d17c712de7a0d611cde4e14f37ebbe0c3c964@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 0091eda0
Loading
Loading
Loading
Loading
+80 −5
Original line number Diff line number Diff line
@@ -2493,6 +2493,9 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
		return;
	}

	if (mvm->fast_resume)
		return;

	iwl_mvm_convert_key_counters_v5(status, &data->gtk[0].sc);
	iwl_mvm_convert_gtk_v3(status, data->gtk);
	iwl_mvm_convert_igtk(status, &data->igtk[0]);
@@ -3049,7 +3052,7 @@ static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm,
	if (iwl_mvm_rt_status(mvm->trans,
			      mvm->trans->dbg.lmac_error_event_table[0],
			      &err_id)) {
		if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
		if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) {
			struct cfg80211_wowlan_wakeup wakeup = {
				.rfkill_release = true,
			};
@@ -3366,7 +3369,7 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test)
	return ret;
}

#define IWL_MVM_D3_NOTIF_TIMEOUT (HZ / 5)
#define IWL_MVM_D3_NOTIF_TIMEOUT (HZ / 3)

static int iwl_mvm_d3_notif_wait(struct iwl_mvm *mvm,
				 struct iwl_d3_data *d3_data)
@@ -3377,11 +3380,21 @@ static int iwl_mvm_d3_notif_wait(struct iwl_mvm *mvm,
		WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF),
		WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION)
	};
	static const u16 d3_fast_resume_notif[] = {
		WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION)
	};
	struct iwl_notification_wait wait_d3_notif;
	int ret;

	if (mvm->fast_resume)
		iwl_init_notification_wait(&mvm->notif_wait, &wait_d3_notif,
					   d3_fast_resume_notif,
					   ARRAY_SIZE(d3_fast_resume_notif),
					   iwl_mvm_wait_d3_notif, d3_data);
	else
		iwl_init_notification_wait(&mvm->notif_wait, &wait_d3_notif,
				   d3_resume_notif, ARRAY_SIZE(d3_resume_notif),
					   d3_resume_notif,
					   ARRAY_SIZE(d3_resume_notif),
					   iwl_mvm_wait_d3_notif, d3_data);

	ret = iwl_mvm_resume_firmware(mvm, d3_data->test);
@@ -3567,6 +3580,68 @@ void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
	device_set_wakeup_enable(mvm->trans->dev, enabled);
}

void iwl_mvm_fast_suspend(struct iwl_mvm *mvm)
{
	struct iwl_d3_manager_config d3_cfg_cmd_data = {};
	int ret;

	lockdep_assert_held(&mvm->mutex);

	IWL_DEBUG_WOWLAN(mvm, "Starting fast suspend flow\n");

	mvm->fast_resume = true;
	set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);

	WARN_ON(iwl_mvm_power_update_device(mvm));
	mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
	ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SEND_IN_D3,
				   sizeof(d3_cfg_cmd_data), &d3_cfg_cmd_data);
	if (ret)
		IWL_ERR(mvm,
			"fast suspend: couldn't send D3_CONFIG_CMD %d\n", ret);

	WARN_ON(iwl_mvm_power_update_mac(mvm));

	ret = iwl_trans_d3_suspend(mvm->trans, false, false);
	if (ret)
		IWL_ERR(mvm, "fast suspend: trans_d3_suspend failed %d\n", ret);
}

int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
{
	struct iwl_d3_data d3_data = {
		.notif_expected =
			IWL_D3_NOTIF_D3_END_NOTIF,
	};
	int ret;

	lockdep_assert_held(&mvm->mutex);

	IWL_DEBUG_WOWLAN(mvm, "Starting the fast resume flow\n");

	mvm->last_reset_or_resume_time_jiffies = jiffies;
	iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt);

	if (iwl_mvm_check_rt_status(mvm, NULL)) {
		set_bit(STATUS_FW_ERROR, &mvm->trans->status);
		iwl_mvm_dump_nic_error_log(mvm);
		iwl_dbg_tlv_time_point(&mvm->fwrt,
				       IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL);
		iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert,
					false, 0);
		return -ENODEV;
	}
	ret = iwl_mvm_d3_notif_wait(mvm, &d3_data);
	clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
	mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
	mvm->fast_resume = false;

	if (ret)
		IWL_ERR(mvm, "Couldn't get the d3 notif %d\n", ret);

	return ret;
}

#ifdef CONFIG_IWLWIFI_DEBUGFS
static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
{
+36 −4
Original line number Diff line number Diff line
@@ -1222,6 +1222,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)

int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
{
	bool fast_resume = false;
	int ret;

	lockdep_assert_held(&mvm->mutex);
@@ -1247,6 +1248,30 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
		mvm->nvm_data = NULL;
	}

#ifdef CONFIG_PM
	/* fast_resume will be cleared by iwl_mvm_fast_resume */
	fast_resume = mvm->fast_resume;

	if (fast_resume) {
		ret = iwl_mvm_fast_resume(mvm);
		if (ret) {
			iwl_mvm_stop_device(mvm);
			/* iwl_mvm_up() will be called further down */
		} else {
			/*
			 * We clear IWL_MVM_STATUS_FIRMWARE_RUNNING upon
			 * mac_down() so that debugfs will stop honoring
			 * requests after we flush all the workers.
			 * Set the IWL_MVM_STATUS_FIRMWARE_RUNNING bit again
			 * now that we are back. This is a bit abusing the
			 * flag since the firmware wasn't really ever stopped,
			 * but this still serves the purpose.
			 */
			set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status);
		}
	}
#endif /* CONFIG_PM */

	if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status)) {
		/*
		 * Now convert the HW_RESTART_REQUESTED flag to IN_HW_RESTART
@@ -1257,6 +1282,9 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
		/* Clean up some internal and mac80211 state on restart */
		iwl_mvm_restart_cleanup(mvm);
	}

	/* we also want to load the firmware if fast_resume failed */
	if (!fast_resume || ret)
		ret = iwl_mvm_up(mvm);

	iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_POST_INIT,
@@ -1340,7 +1368,7 @@ void iwl_mvm_mac_reconfig_complete(struct ieee80211_hw *hw,
	}
}

void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend)
{
	lockdep_assert_held(&mvm->mutex);

@@ -1356,6 +1384,10 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
	if (!iwl_mvm_has_new_station_api(mvm->fw))
		iwl_mvm_rm_aux_sta(mvm);

	if (suspend &&
	    mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)
		iwl_mvm_fast_suspend(mvm);
	else
		iwl_mvm_stop_device(mvm);

	iwl_mvm_async_handlers_purge(mvm);
@@ -1425,7 +1457,7 @@ void iwl_mvm_mac_stop(struct ieee80211_hw *hw, bool suspend)
	iwl_mvm_mei_set_sw_rfkill_state(mvm);

	mutex_lock(&mvm->mutex);
	__iwl_mvm_mac_stop(mvm);
	__iwl_mvm_mac_stop(mvm, suspend);
	mutex_unlock(&mvm->mutex);

	/*
+13 −1
Original line number Diff line number Diff line
@@ -1161,6 +1161,7 @@ struct iwl_mvm {
	struct ieee80211_channel **nd_channels;
	int n_nd_channels;
	bool net_detect;
	bool fast_resume;
	u8 offload_tid;
#ifdef CONFIG_IWLWIFI_DEBUGFS
	bool d3_wake_sysassert;
@@ -1739,7 +1740,7 @@ struct iwl_rate_info {
	u8 ieee;	/* MAC header:  IWL_RATE_6M_IEEE, etc. */
};

void __iwl_mvm_mac_stop(struct iwl_mvm *mvm);
void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend);
int __iwl_mvm_mac_start(struct iwl_mvm *mvm);

/******************
@@ -2261,11 +2262,22 @@ extern const struct file_operations iwl_dbgfs_d3_test_ops;
#ifdef CONFIG_PM
void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm,
				 struct ieee80211_vif *vif);
void iwl_mvm_fast_suspend(struct iwl_mvm *mvm);
int iwl_mvm_fast_resume(struct iwl_mvm *mvm);
#else
static inline void
iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
}

static inline void iwl_mvm_fast_suspend(struct iwl_mvm *mvm)
{
}

static inline int iwl_mvm_fast_resume(struct iwl_mvm *mvm)
{
	return 0;
}
#endif
void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
				struct iwl_wowlan_config_cmd *cmd);
+1 −1
Original line number Diff line number Diff line
@@ -299,7 +299,7 @@ static void check_exit_ctkill(struct work_struct *work)

	ret = iwl_mvm_get_temp(mvm, &temp);

	__iwl_mvm_mac_stop(mvm);
	__iwl_mvm_mac_stop(mvm, false);

	if (ret)
		goto reschedule;