iwlwifi: remove IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API flag
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / iwlwifi / mvm / power.c
index d9eab3b7bb9f871a37b818e5a63e1320a12d7189..dba760f246275805afc219b13242a3dd85303cc3 100644 (file)
 
 #define POWER_KEEP_ALIVE_PERIOD_SEC    25
 
+static
 int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
-                                  struct iwl_beacon_filter_cmd *cmd)
+                                  struct iwl_beacon_filter_cmd *cmd,
+                                  u32 flags)
 {
-       int ret;
-
-       ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, CMD_SYNC,
-                                  sizeof(struct iwl_beacon_filter_cmd), cmd);
-
-       if (!ret) {
-               IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
-                               le32_to_cpu(cmd->ba_enable_beacon_abort));
-               IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
-                               le32_to_cpu(cmd->ba_escape_timer));
-               IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
-                               le32_to_cpu(cmd->bf_debug_flag));
-               IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
-                               le32_to_cpu(cmd->bf_enable_beacon_filter));
-               IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
-                               le32_to_cpu(cmd->bf_energy_delta));
-               IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
-                               le32_to_cpu(cmd->bf_escape_timer));
-               IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
-                               le32_to_cpu(cmd->bf_roaming_energy_delta));
-               IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
-                               le32_to_cpu(cmd->bf_roaming_state));
-               IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
-                               le32_to_cpu(cmd->bf_temp_threshold));
-               IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
-                               le32_to_cpu(cmd->bf_temp_fast_filter));
-               IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
-                               le32_to_cpu(cmd->bf_temp_slow_filter));
-       }
-       return ret;
+       IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
+                       le32_to_cpu(cmd->ba_enable_beacon_abort));
+       IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
+                       le32_to_cpu(cmd->ba_escape_timer));
+       IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
+                       le32_to_cpu(cmd->bf_debug_flag));
+       IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
+                       le32_to_cpu(cmd->bf_enable_beacon_filter));
+       IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
+                       le32_to_cpu(cmd->bf_energy_delta));
+       IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
+                       le32_to_cpu(cmd->bf_escape_timer));
+       IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
+                       le32_to_cpu(cmd->bf_roaming_energy_delta));
+       IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
+                       le32_to_cpu(cmd->bf_roaming_state));
+       IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
+                       le32_to_cpu(cmd->bf_temp_threshold));
+       IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
+                       le32_to_cpu(cmd->bf_temp_fast_filter));
+       IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
+                       le32_to_cpu(cmd->bf_temp_slow_filter));
+
+       return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags,
+                                   sizeof(struct iwl_beacon_filter_cmd), cmd);
 }
 
 static
@@ -145,7 +142,7 @@ int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
        mvmvif->bf_data.ba_enabled = enable;
        iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
        iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
-       return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+       return iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, CMD_SYNC);
 }
 
 static void iwl_mvm_power_log(struct iwl_mvm *mvm,
@@ -301,8 +298,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
        keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC);
        cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
-           mvm->ps_prevented)
+       if (mvm->ps_disabled)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
@@ -312,7 +308,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
            mvmvif->dbgfs_pm.disable_power_off)
                cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
 #endif
-       if (!vif->bss_conf.ps || mvmvif->pm_prevented)
+       if (!vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif) ||
+           !mvmvif->pm_enabled)
                return;
 
        cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
@@ -419,62 +416,22 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
 #endif /* CONFIG_IWLWIFI_DEBUGFS */
 }
 
-static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm,
+static int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
                                         struct ieee80211_vif *vif)
 {
-       int ret;
-       bool ba_enable;
        struct iwl_mac_power_cmd cmd = {};
 
-       if (vif->type != NL80211_IFTYPE_STATION)
-               return 0;
-
-       if (vif->p2p &&
-           !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS))
-               return 0;
-
        iwl_mvm_power_build_cmd(mvm, vif, &cmd);
        iwl_mvm_power_log(mvm, &cmd);
-
-       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
-                                  sizeof(cmd), &cmd);
-       if (ret)
-               return ret;
-
-       ba_enable = !!(cmd.flags &
-                      cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK));
-
-       return iwl_mvm_update_beacon_abort(mvm, vif, ba_enable);
-}
-
-static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm,
-                                    struct ieee80211_vif *vif)
-{
-       struct iwl_mac_power_cmd cmd = {};
-       struct iwl_mvm_vif *mvmvif __maybe_unused =
-               iwl_mvm_vif_from_mac80211(vif);
-
-       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
-               return 0;
-
-       cmd.id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
-                                                          mvmvif->color));
-
-       if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
-               cmd.flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
-
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-       if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_DISABLE_POWER_OFF &&
-           mvmvif->dbgfs_pm.disable_power_off)
-               cmd.flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK);
+       memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
 #endif
-       iwl_mvm_power_log(mvm, &cmd);
 
-       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_ASYNC,
+       return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, CMD_SYNC,
                                    sizeof(cmd), &cmd);
 }
 
-static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
+int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
 {
        struct iwl_device_power_cmd cmd = {
                .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK),
@@ -483,8 +440,10 @@ static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
                return 0;
 
-       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM ||
-           force_disable)
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
+               mvm->ps_disabled = true;
+
+       if (mvm->ps_disabled)
                cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK);
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -501,11 +460,6 @@ static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable)
                                    &cmd);
 }
 
-static int iwl_mvm_power_update_device(struct iwl_mvm *mvm)
-{
-       return _iwl_mvm_power_update_device(mvm, false);
-}
-
 void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -544,44 +498,203 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
        return 0;
 }
 
-static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac,
-                                          struct ieee80211_vif *vif)
+struct iwl_power_vifs {
+       struct ieee80211_vif *bf_vif;
+       struct ieee80211_vif *bss_vif;
+       struct ieee80211_vif *p2p_vif;
+       struct ieee80211_vif *ap_vif;
+       struct ieee80211_vif *monitor_vif;
+       bool p2p_active;
+       bool bss_active;
+       bool ap_active;
+       bool monitor_active;
+};
+
+static void iwl_mvm_power_iterator(void *_data, u8 *mac,
+                                  struct ieee80211_vif *vif)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_mvm *mvm = _data;
-       int ret;
+       struct iwl_power_vifs *power_iterator = _data;
+
+       mvmvif->pm_enabled = false;
+       switch (ieee80211_vif_type_p2p(vif)) {
+       case NL80211_IFTYPE_P2P_DEVICE:
+               break;
+
+       case NL80211_IFTYPE_P2P_GO:
+       case NL80211_IFTYPE_AP:
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->ap_vif);
+               power_iterator->ap_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->ap_active = true;
+               break;
+
+       case NL80211_IFTYPE_MONITOR:
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->monitor_vif);
+               power_iterator->monitor_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->monitor_active = true;
+               break;
+
+       case NL80211_IFTYPE_P2P_CLIENT:
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->p2p_vif);
+               power_iterator->p2p_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->p2p_active = true;
+               break;
+
+       case NL80211_IFTYPE_STATION:
+               /* only a single MAC of the same type */
+               WARN_ON(power_iterator->bss_vif);
+               power_iterator->bss_vif = vif;
+               if (mvmvif->phy_ctxt)
+                       if (mvmvif->phy_ctxt->id < MAX_PHYS)
+                               power_iterator->bss_active = true;
+
+               if (mvmvif->bf_data.bf_enabled &&
+                   !WARN_ON(power_iterator->bf_vif))
+                       power_iterator->bf_vif = vif;
+
+               break;
+
+       default:
+               break;
+       }
+}
 
-       mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true;
+static void
+iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
+                                   struct iwl_power_vifs *vifs)
+{
+       struct iwl_mvm_vif *bss_mvmvif = NULL;
+       struct iwl_mvm_vif *p2p_mvmvif = NULL;
+       struct iwl_mvm_vif *ap_mvmvif = NULL;
+       bool client_same_channel = false;
+       bool ap_same_channel = false;
 
-       ret = iwl_mvm_power_mac_update_mode(mvm, vif);
-       WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n");
+       lockdep_assert_held(&mvm->mutex);
+
+       /* get vifs info + set pm_enable to false */
+       ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_power_iterator, vifs);
+
+       if (vifs->bss_vif)
+               bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif);
+
+       if (vifs->p2p_vif)
+               p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif);
+
+       if (vifs->ap_vif)
+               ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
+
+       /* enable PM on bss if bss stand alone */
+       if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
+               bss_mvmvif->pm_enabled = true;
+               return;
+       }
+
+       /* enable PM on p2p if p2p stand alone */
+       if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
+               if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+                       p2p_mvmvif->pm_enabled = true;
+               return;
+       }
+
+       if (vifs->bss_active && vifs->p2p_active)
+               client_same_channel = (bss_mvmvif->phy_ctxt->id ==
+                                      p2p_mvmvif->phy_ctxt->id);
+       if (vifs->bss_active && vifs->ap_active)
+               ap_same_channel = (bss_mvmvif->phy_ctxt->id ==
+                                  ap_mvmvif->phy_ctxt->id);
+
+       /* bss is not stand alone: enable PM if alone on its channel */
+       if (vifs->bss_active && !(client_same_channel || ap_same_channel) &&
+           (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM)) {
+                       bss_mvmvif->pm_enabled = true;
+                       return;
+       }
+
+       /*
+        * There is only one channel in the system and there are only
+        * bss and p2p clients that share it
+        */
+       if (client_same_channel && !vifs->ap_active &&
+           (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM)) {
+               /* share same channel*/
+               bss_mvmvif->pm_enabled = true;
+               if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
+                       p2p_mvmvif->pm_enabled = true;
+       }
 }
 
-static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm,
-                                         struct ieee80211_vif *vif,
-                                         bool assign)
+int iwl_mvm_power_update_mac(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
-       if (vif->type == NL80211_IFTYPE_MONITOR) {
-               int ret = _iwl_mvm_power_update_device(mvm, assign);
-               mvm->ps_prevented = assign;
-               WARN_ONCE(ret, "Failed to update power device state\n");
+       struct iwl_mvm_vif *mvmvif;
+       struct iwl_power_vifs vifs = {};
+       bool ba_enable;
+       int ret;
+
+       lockdep_assert_held(&mvm->mutex);
+
+       iwl_mvm_power_set_pm(mvm, &vifs);
+
+       /* disable PS if CAM */
+       if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) {
+               mvm->ps_disabled = true;
+       } else {
+       /* don't update device power state unless we add / remove monitor */
+               if (vifs.monitor_vif) {
+                       if (vifs.monitor_active)
+                               mvm->ps_disabled = true;
+                       ret = iwl_mvm_power_update_device(mvm);
+                       if (ret)
+                               return ret;
+               }
        }
 
-       ieee80211_iterate_active_interfaces(mvm->hw,
-                                           IEEE80211_IFACE_ITER_NORMAL,
-                                           iwl_mvm_power_binding_iterator,
-                                           mvm);
+       if (vifs.bss_vif) {
+               ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif);
+               if (ret)
+                       return ret;
+       }
+
+       if (vifs.p2p_vif) {
+               ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif);
+               if (ret)
+                       return ret;
+       }
+
+       if (!vifs.bf_vif)
+               return 0;
+
+       vif = vifs.bf_vif;
+       mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       ba_enable = !(!mvmvif->pm_enabled || mvm->ps_disabled ||
+                     !vif->bss_conf.ps || iwl_mvm_vif_low_latency(mvmvif));
+
+       return iwl_mvm_update_beacon_abort(mvm, vifs.bf_vif, ba_enable);
 }
 
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
-                                       struct ieee80211_vif *vif, char *buf,
-                                       int bufsz)
+int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif, char *buf,
+                                int bufsz)
 {
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mac_power_cmd cmd = {};
        int pos = 0;
 
-       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+       mutex_lock(&mvm->mutex);
+       memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
+       mutex_unlock(&mvm->mutex);
 
        if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD))
                pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n",
@@ -685,42 +798,55 @@ iwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
 }
 #endif
 
-int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif)
+static int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+                                        struct ieee80211_vif *vif,
+                                        struct iwl_beacon_filter_cmd *cmd,
+                                        u32 cmd_flags,
+                                        bool d0i3)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-       struct iwl_beacon_filter_cmd cmd = {
-               IWL_BF_CMD_CONFIG_DEFAULTS,
-               .bf_enable_beacon_filter = cpu_to_le32(1),
-       };
        int ret;
 
        if (mvmvif != mvm->bf_allowed_vif ||
            vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
-       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, &cmd);
-       iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd);
-       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+       iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd);
+       if (!d0i3)
+               iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);
+       ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags);
 
-       if (!ret)
+       /* don't change bf_enabled in case of temporary d0i3 configuration */
+       if (!ret && !d0i3)
                mvmvif->bf_data.bf_enabled = true;
 
        return ret;
 }
 
+int iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                u32 flags)
+{
+       struct iwl_beacon_filter_cmd cmd = {
+               IWL_BF_CMD_CONFIG_DEFAULTS,
+               .bf_enable_beacon_filter = cpu_to_le32(1),
+       };
+
+       return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags, false);
+}
+
 int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
-                                 struct ieee80211_vif *vif)
+                                 struct ieee80211_vif *vif,
+                                 u32 flags)
 {
        struct iwl_beacon_filter_cmd cmd = {};
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int ret;
 
-       if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BF_UPDATED) ||
-           vif->type != NL80211_IFTYPE_STATION || vif->p2p)
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
-       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd);
+       ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
 
        if (!ret)
                mvmvif->bf_data.bf_enabled = false;
@@ -728,23 +854,79 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
        return ret;
 }
 
-int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif)
+int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
+                                  struct ieee80211_vif *vif,
+                                  bool enable, u32 flags)
 {
+       int ret;
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       struct iwl_mac_power_cmd cmd = {};
 
-       if (!mvmvif->bf_data.bf_enabled)
+       if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
                return 0;
 
-       return iwl_mvm_enable_beacon_filter(mvm, vif);
-}
+       if (!vif->bss_conf.assoc)
+               return 0;
 
-const struct iwl_mvm_power_ops pm_mac_ops = {
-       .power_update_mode = iwl_mvm_power_mac_update_mode,
-       .power_update_device_mode = iwl_mvm_power_update_device,
-       .power_disable = iwl_mvm_power_mac_disable,
-       .power_update_binding = _iwl_mvm_power_update_binding,
+       iwl_mvm_power_build_cmd(mvm, vif, &cmd);
+       if (enable) {
+               /* configure skip over dtim up to 300 msec */
+               int dtimper = mvm->hw->conf.ps_dtim_period ?: 1;
+               int dtimper_msec = dtimper * vif->bss_conf.beacon_int;
+
+               if (WARN_ON(!dtimper_msec))
+                       return 0;
+
+               cmd.flags |=
+                       cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
+               cmd.skip_dtim_periods = 300 / dtimper_msec;
+       }
+       iwl_mvm_power_log(mvm, &cmd);
 #ifdef CONFIG_IWLWIFI_DEBUGFS
-       .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read,
+       memcpy(&mvmvif->mac_pwr_cmd, &cmd, sizeof(cmd));
 #endif
-};
+       ret = iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, flags,
+                                  sizeof(cmd), &cmd);
+       if (ret)
+               return ret;
+
+       /* configure beacon filtering */
+       if (mvmvif != mvm->bf_allowed_vif)
+               return 0;
+
+       if (enable) {
+               struct iwl_beacon_filter_cmd cmd_bf = {
+                       IWL_BF_CMD_CONFIG_D0I3,
+                       .bf_enable_beacon_filter = cpu_to_le32(1),
+               };
+               ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
+                                                   flags, true);
+       } else {
+               if (mvmvif->bf_data.bf_enabled)
+                       ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags);
+               else
+                       ret = iwl_mvm_disable_beacon_filter(mvm, vif, flags);
+       }
+
+       return ret;
+}
+
+int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm,
+                                struct ieee80211_vif *vif,
+                                bool force,
+                                u32 flags)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (mvmvif != mvm->bf_allowed_vif)
+               return 0;
+
+       if (!mvmvif->bf_data.bf_enabled) {
+               /* disable beacon filtering explicitly if force is true */
+               if (force)
+                       return iwl_mvm_disable_beacon_filter(mvm, vif, flags);
+               return 0;
+       }
+
+       return iwl_mvm_enable_beacon_filter(mvm, vif, flags);
+}