diff mbox

[19/45] iwlwifi: replace d0i3_mode and wowlan_d0i3 with more generic variables

Message ID 1450731046-2796-19-git-send-email-emmanuel.grumbach@intel.com
State Accepted
Delegated to: Kalle Valo
Headers show

Commit Message

Emmanuel Grumbach Dec. 21, 2015, 8:50 p.m. UTC
From: Luca Coelho <luciano.coelho@intel.com>

The d0i3_mode variable is used to distinguish between transports that
handle d0i3 entry during suspend by themselves (i.e. the slave
transports) and those which rely on the op_mode layer to do it.  The
reason why the former do it by themselves is that they need to
transition from d0i3 in runtime_suspend into d0i3 in system-wide
suspend and this transition needs to happen before the op_mode's
suspend flow is called.

The wowlan_d0i3 element is also a bit confusing, because it just
reflects the wowlan->any value for the trans to understand.  This is a
bit unclear in the code and not generic enough for future use.

To make it clearer and to generalize the platform power mode settings,
introduce two variables to indicate the platform power management
modes used by the transport.

Additionally, in order not to take too big a step in one patch, treat
this new variables semantically in the same way as the old d0i3_mode
element, introducing a iwl_mvm_enter_d0i3_on_suspend() function to
help with that.

This commit also adds the foundation for a new concept where the
firmware configuration state (i.e. D0, D3 or D0i3) is abstracted from
the platform PM mode we are in (i.e. runtime suspend or system-wide
suspend).

Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
---
 drivers/net/wireless/intel/iwlwifi/iwl-trans.h    | 74 +++++++++++++++++++----
 drivers/net/wireless/intel/iwlwifi/mvm/d3.c       | 40 ++++++++----
 drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 16 +++--
 drivers/net/wireless/intel/iwlwifi/mvm/mvm.h      | 13 ++++
 drivers/net/wireless/intel/iwlwifi/pcie/trans.c   |  7 +--
 5 files changed, 111 insertions(+), 39 deletions(-)
diff mbox

Patch

diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
index f00c9ad..8caf68b 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h
@@ -669,18 +669,61 @@  enum iwl_trans_state {
 };
 
 /**
- * enum iwl_d0i3_mode - d0i3 mode
+ * DOC: Platform power management
  *
- * @IWL_D0I3_MODE_OFF - d0i3 is disabled
- * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle
- *	(e.g. no active references)
- * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend
- *	(in case of 'any' trigger)
+ * There are two types of platform power management: system-wide
+ * (WoWLAN) and runtime.
+ *
+ * In system-wide power management the entire platform goes into a low
+ * power state (e.g. idle or suspend to RAM) at the same time and the
+ * device is configured as a wakeup source for the entire platform.
+ * This is usually triggered by userspace activity (e.g. the user
+ * presses the suspend button or a power management daemon decides to
+ * put the platform in low power mode).  The device's behavior in this
+ * mode is dictated by the wake-on-WLAN configuration.
+ *
+ * In runtime power management, only the devices which are themselves
+ * idle enter a low power state.  This is done at runtime, which means
+ * that the entire system is still running normally.  This mode is
+ * usually triggered automatically by the device driver and requires
+ * the ability to enter and exit the low power modes in a very short
+ * time, so there is not much impact in usability.
+ *
+ * The terms used for the device's behavior are as follows:
+ *
+ *	- D0: the device is fully powered and the host is awake;
+ *	- D3: the device is in low power mode and only reacts to
+ *		specific events (e.g. magic-packet received or scan
+ *		results found);
+ *	- D0I3: the device is in low power mode and reacts to any
+ *		activity (e.g. RX);
+ *
+ * These terms reflect the power modes in the firmware and are not to
+ * be confused with the physical device power state.  The NIC can be
+ * in D0I3 mode even if, for instance, the PCI device is in D3 state.
  */
-enum iwl_d0i3_mode {
-	IWL_D0I3_MODE_OFF = 0,
-	IWL_D0I3_MODE_ON_IDLE,
-	IWL_D0I3_MODE_ON_SUSPEND,
+
+/**
+ * enum iwl_plat_pm_mode - platform power management mode
+ *
+ * This enumeration describes the device's platform power management
+ * behavior when in idle mode (i.e. runtime power management) or when
+ * in system-wide suspend (i.e WoWLAN).
+ *
+ * @IWL_PLAT_PM_MODE_DISABLED: power management is disabled for this
+ *	device.  At runtime, this means that nothing happens and the
+ *	device always remains in active.  In system-wide suspend mode,
+ *	it means that the all connections will be closed automatically
+ *	by mac80211 before the platform is suspended.
+ * @IWL_PLAT_PM_MODE_D3: the device goes into D3 mode (i.e. WoWLAN).
+ *	For runtime power management, this mode is not officially
+ *	supported.
+ * @IWL_PLAT_PM_MODE_D0I3: the device goes into D0I3 mode.
+ */
+enum iwl_plat_pm_mode {
+	IWL_PLAT_PM_MODE_DISABLED,
+	IWL_PLAT_PM_MODE_D3,
+	IWL_PLAT_PM_MODE_D0I3,
 };
 
 /**
@@ -720,6 +763,12 @@  enum iwl_d0i3_mode {
  *	the opmode.
  * @paging_download_buf: Buffer used for copying all of the pages before
  *	downloading them to the FW. The buffer is allocated in the opmode
+ * @system_pm_mode: the system-wide power management mode in use.
+ *	This mode is set dynamically, depending on the WoWLAN values
+ *	configured from the userspace at runtime.
+ * @runtime_pm_mode: the runtime power management mode in use.  This
+ *	mode is set during the initialization phase and is not
+ *	supposed to change during runtime.
  */
 struct iwl_trans {
 	const struct iwl_trans_ops *ops;
@@ -770,9 +819,8 @@  struct iwl_trans {
 	struct iwl_fw_paging *paging_db;
 	void *paging_download_buf;
 
-	enum iwl_d0i3_mode d0i3_mode;
-
-	bool wowlan_d0i3;
+	enum iwl_plat_pm_mode system_pm_mode;
+	enum iwl_plat_pm_mode runtime_pm_mode;
 
 	/* pointer to trans specific struct */
 	/*Ensure that this pointer will always be aligned to sizeof pointer */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 542de74..b1c9992 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -1183,19 +1183,20 @@  remove_notif:
 int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	struct iwl_trans *trans = mvm->trans;
 	int ret;
 
 	/* make sure the d0i3 exit work is not pending */
 	flush_work(&mvm->d0i3_exit_work);
 
-	ret = iwl_trans_suspend(mvm->trans);
+	ret = iwl_trans_suspend(trans);
 	if (ret)
 		return ret;
 
-	mvm->trans->wowlan_d0i3 = wowlan->any;
-	if (mvm->trans->wowlan_d0i3) {
-		/* 'any' trigger means d0i3 usage */
-		if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
+	if (wowlan->any) {
+		trans->system_pm_mode = IWL_PLAT_PM_MODE_D0I3;
+
+		if (iwl_mvm_enter_d0i3_on_suspend(mvm)) {
 			ret = iwl_mvm_enter_d0i3_sync(mvm);
 
 			if (ret)
@@ -1206,11 +1207,13 @@  int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 		__set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags);
 		mutex_unlock(&mvm->d0i3_suspend_mutex);
 
-		iwl_trans_d3_suspend(mvm->trans, false);
+		iwl_trans_d3_suspend(trans, false);
 
 		return 0;
 	}
 
+	trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
+
 	return __iwl_mvm_suspend(hw, wowlan, false);
 }
 
@@ -1973,8 +1976,9 @@  static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
 {
 	bool exit_now;
 	enum iwl_d3_status d3_status;
+	struct iwl_trans *trans = mvm->trans;
 
-	iwl_trans_d3_resume(mvm->trans, &d3_status, false);
+	iwl_trans_d3_resume(trans, &d3_status, false);
 
 	/*
 	 * make sure to clear D0I3_DEFER_WAKEUP before
@@ -1991,9 +1995,9 @@  static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
 		_iwl_mvm_exit_d0i3(mvm);
 	}
 
-	iwl_trans_resume(mvm->trans);
+	iwl_trans_resume(trans);
 
-	if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) {
+	if (iwl_mvm_enter_d0i3_on_suspend(mvm)) {
 		int ret = iwl_mvm_exit_d0i3(mvm->hw->priv);
 
 		if (ret)
@@ -2009,12 +2013,16 @@  static int iwl_mvm_resume_d0i3(struct iwl_mvm *mvm)
 int iwl_mvm_resume(struct ieee80211_hw *hw)
 {
 	struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+	int ret;
 
-	/* 'any' trigger means d0i3 was used */
-	if (hw->wiphy->wowlan_config->any)
-		return iwl_mvm_resume_d0i3(mvm);
+	if (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3)
+		ret = iwl_mvm_resume_d0i3(mvm);
 	else
-		return iwl_mvm_resume_d3(mvm);
+		ret = iwl_mvm_resume_d3(mvm);
+
+	mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
+
+	return ret;
 }
 
 void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled)
@@ -2038,6 +2046,8 @@  static int iwl_mvm_d3_test_open(struct inode *inode, struct file *file)
 	ieee80211_stop_queues(mvm->hw);
 	synchronize_net();
 
+	mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3;
+
 	/* start pseudo D3 */
 	rtnl_lock();
 	err = __iwl_mvm_suspend(mvm->hw, mvm->hw->wiphy->wowlan_config, true);
@@ -2092,9 +2102,13 @@  static int iwl_mvm_d3_test_release(struct inode *inode, struct file *file)
 	int remaining_time = 10;
 
 	mvm->d3_test_active = false;
+
 	rtnl_lock();
 	__iwl_mvm_resume(mvm, true);
 	rtnl_unlock();
+
+	mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED;
+
 	iwl_abort_notification_waits(&mvm->notif_wait);
 	ieee80211_restart_hw(mvm->hw);
 
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 7487eb0..3a78a3f 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1087,15 +1087,13 @@  static void iwl_mvm_restart_complete(struct iwl_mvm *mvm)
 
 static void iwl_mvm_resume_complete(struct iwl_mvm *mvm)
 {
-	if (!iwl_mvm_is_d0i3_supported(mvm))
-		return;
-
-	if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND)
-		if (!wait_event_timeout(mvm->d0i3_exit_waitq,
-					!test_bit(IWL_MVM_STATUS_IN_D0I3,
-						  &mvm->status),
-					HZ))
-			WARN_ONCE(1, "D0i3 exit on resume timed out\n");
+	if (iwl_mvm_is_d0i3_supported(mvm) &&
+	    iwl_mvm_enter_d0i3_on_suspend(mvm))
+		WARN_ONCE(!wait_event_timeout(mvm->d0i3_exit_waitq,
+					      !test_bit(IWL_MVM_STATUS_IN_D0I3,
+							&mvm->status),
+					      HZ),
+			  "D0i3 exit on resume timed out\n");
 }
 
 static void
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
index 3f0db29..3fc7199 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
@@ -933,6 +933,19 @@  static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm)
 			   IWL_UCODE_TLV_CAPA_DQA_SUPPORT);
 }
 
+static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm)
+{
+	/* For now we only use this mode to differentiate between
+	 * slave transports, which handle D0i3 entry in suspend by
+	 * themselves in conjunction with runtime PM D0i3.  So, this
+	 * function is used to check whether we need to do anything
+	 * when entering suspend or if the transport layer has already
+	 * done it.
+	 */
+	return (mvm->trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) &&
+		(mvm->trans->runtime_pm_mode != IWL_PLAT_PM_MODE_D0I3);
+}
+
 static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm)
 {
 	bool nvm_lar = mvm->nvm_data->lar_enabled;
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
index 0d92763..05d5a5b 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
@@ -1213,7 +1213,7 @@  static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
 {
 	struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
 
-	if (trans->wowlan_d0i3) {
+	if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) {
 		/* Enable persistence mode to avoid reset */
 		iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
 			    CSR_HW_IF_CONFIG_REG_PERSIST_MODE);
@@ -1237,7 +1237,7 @@  static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test)
 	iwl_clear_bit(trans, CSR_GP_CNTRL,
 		      CSR_GP_CNTRL_REG_FLAG_INIT_DONE);
 
-	if (!trans->wowlan_d0i3) {
+	if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D3) {
 		/*
 		 * reset TX queues -- some of their registers reset during S3
 		 * so if we don't reset everything here the D3 image would try
@@ -1286,7 +1286,7 @@  static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans,
 
 	iwl_pcie_set_pwr(trans, false);
 
-	if (trans->wowlan_d0i3) {
+	if (trans->system_pm_mode == IWL_PLAT_PM_MODE_D0I3) {
 		iwl_clear_bit(trans, CSR_GP_CNTRL,
 			      CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
 	} else {
@@ -2677,7 +2677,6 @@  struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
 	}
 
 	trans_pcie->inta_mask = CSR_INI_SET_MASK;
-	trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND;
 
 	return trans;