iwlwifi: mvm: allow transport sleep when FW is operational
authorEliad Peller <eliad@wizery.com>
Thu, 16 Jan 2014 15:10:44 +0000 (17:10 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Mon, 3 Feb 2014 20:23:39 +0000 (22:23 +0200)
Hold a bitmap of taken references, according to the
reference reason (e.g. down, scan).

This will allow us validate our state and add some debugfs
entries later on.

Unref the transport when the FW is fully initialized,
allowing it to go into a low power mode.

Disallow the transition to low-power while recovery is in
progress.

Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/mvm/fw.c
drivers/net/wireless/iwlwifi/mvm/mac80211.c
drivers/net/wireless/iwlwifi/mvm/mvm.h
drivers/net/wireless/iwlwifi/mvm/ops.c

index 5798f1ae74829c73e8e5a354f521adc0ef722b6f..212ffecf038b9bc6718069c5f31a756b6be503a6 100644 (file)
@@ -446,6 +446,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
        if (ret)
                goto error;
 
+       /* allow FW/transport low power modes if not during restart */
+       if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        IWL_DEBUG_INFO(mvm, "RT uCode started.\n");
        return 0;
  error:
index de38deb7e4f2df4908718d70444878c4f716fc78..e12168556381ed9e14bbf19316738aad5cd66141 100644 (file)
@@ -202,6 +202,44 @@ static const struct iwl_fw_bcast_filter iwl_mvm_default_bcast_filters[] = {
 };
 #endif
 
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+       if (!mvm->trans->cfg->d0i3)
+               return;
+
+       IWL_DEBUG_RPM(mvm, "Take mvm reference - type %d\n", ref_type);
+       WARN_ON(test_and_set_bit(ref_type, mvm->ref_bitmap));
+       iwl_trans_ref(mvm->trans);
+}
+
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+       if (!mvm->trans->cfg->d0i3)
+               return;
+
+       IWL_DEBUG_RPM(mvm, "Leave mvm reference - type %d\n", ref_type);
+       WARN_ON(!test_and_clear_bit(ref_type, mvm->ref_bitmap));
+       iwl_trans_unref(mvm->trans);
+}
+
+static void
+iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
+{
+       int i;
+
+       if (!mvm->trans->cfg->d0i3)
+               return;
+
+       for_each_set_bit(i, mvm->ref_bitmap, IWL_MVM_REF_COUNT) {
+               if (ref == i)
+                       continue;
+
+               IWL_DEBUG_RPM(mvm, "Cleanup: remove mvm ref type %d\n", i);
+               clear_bit(i, mvm->ref_bitmap);
+               iwl_trans_unref(mvm->trans);
+       }
+}
+
 static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
 {
        int i;
@@ -516,6 +554,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
 
        ieee80211_wake_queues(mvm->hw);
 
+       /* cleanup all stale references (scan, roc), but keep the
+        * ucode_down ref until reconfig is complete */
+       iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        mvm->vif_count = 0;
        mvm->rx_ba_sessions = 0;
 }
@@ -550,6 +592,9 @@ static void iwl_mvm_mac_restart_complete(struct ieee80211_hw *hw)
                IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
                        ret);
 
+       /* allow transport/FW low power modes */
+       iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        mutex_unlock(&mvm->mutex);
 }
 
@@ -560,6 +605,10 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
        flush_work(&mvm->async_handlers_wk);
 
        mutex_lock(&mvm->mutex);
+
+       /* disallow low power states when the FW is down */
+       iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
        /* async_handlers_wk is now blocked */
 
        /*
index 0a8c65bd18a075b3ef8313a971e67f6457a00eae..9ffafe80dad5caabef06cd7fb8ba7bd9fd62f8ca 100644 (file)
@@ -239,6 +239,12 @@ enum iwl_mvm_smps_type_request {
        NUM_IWL_MVM_SMPS_REQ,
 };
 
+enum iwl_mvm_ref_type {
+       IWL_MVM_REF_UCODE_DOWN,
+
+       IWL_MVM_REF_COUNT,
+};
+
 /**
 * struct iwl_mvm_vif_bf_data - beacon filtering related data
 * @bf_enabled: indicates if beacon filtering is enabled
@@ -542,6 +548,9 @@ struct iwl_mvm {
         */
        unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)];
 
+       /* A bitmap of reference types taken by the driver. */
+       unsigned long ref_bitmap[BITS_TO_LONGS(IWL_MVM_REF_COUNT)];
+
        u8 vif_count;
 
        /* -1 for always, 0 for never, >0 for that many times */
@@ -877,6 +886,10 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 }
 #endif
 
+/* D0i3 */
+void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
+
 /* BT Coex */
 int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm);
 int iwl_send_bt_init_conf(struct iwl_mvm *mvm);
index cdb4c929e0172b56df4fb4dde0f6fc671eb5a069..5bc44395fa96072ce70591e405b3504960808fab 100644 (file)
@@ -501,6 +501,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 
        memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx));
 
+       /* rpm starts with a taken ref. only set the appropriate bit here. */
+       set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap);
+
        return op_mode;
 
  out_unregister:
@@ -788,6 +791,9 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
                INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
                schedule_work(&reprobe->work);
        } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
+               /* don't let the transport/FW power down */
+               iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
+
                if (mvm->restart_fw > 0)
                        mvm->restart_fw--;
                ieee80211_restart_hw(mvm->hw);