18c2ecf20Sopenharmony_ci/******************************************************************************
28c2ecf20Sopenharmony_ci *
38c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license.  When using or
48c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
98c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
108c2ecf20Sopenharmony_ci * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
118c2ecf20Sopenharmony_ci * Copyright (C) 2018 - 2019 Intel Corporation
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify
148c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as
158c2ecf20Sopenharmony_ci * published by the Free Software Foundation.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
188c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
198c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
208c2ecf20Sopenharmony_ci * General Public License for more details.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * The full GNU General Public License is included in this distribution
238c2ecf20Sopenharmony_ci * in the file called COPYING.
248c2ecf20Sopenharmony_ci *
258c2ecf20Sopenharmony_ci * Contact Information:
268c2ecf20Sopenharmony_ci *  Intel Linux Wireless <linuxwifi@intel.com>
278c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * BSD LICENSE
308c2ecf20Sopenharmony_ci *
318c2ecf20Sopenharmony_ci * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
328c2ecf20Sopenharmony_ci * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
338c2ecf20Sopenharmony_ci * Copyright(c) 2015 - 2017 Intel Deutschland GmbH
348c2ecf20Sopenharmony_ci * Copyright (C) 2018 - 2019 Intel Corporation
358c2ecf20Sopenharmony_ci * All rights reserved.
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
388c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions
398c2ecf20Sopenharmony_ci * are met:
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci *  * Redistributions of source code must retain the above copyright
428c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
438c2ecf20Sopenharmony_ci *  * Redistributions in binary form must reproduce the above copyright
448c2ecf20Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in
458c2ecf20Sopenharmony_ci *    the documentation and/or other materials provided with the
468c2ecf20Sopenharmony_ci *    distribution.
478c2ecf20Sopenharmony_ci *  * Neither the name Intel Corporation nor the names of its
488c2ecf20Sopenharmony_ci *    contributors may be used to endorse or promote products derived
498c2ecf20Sopenharmony_ci *    from this software without specific prior written permission.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
528c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
538c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
548c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
558c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
568c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
578c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
588c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
598c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
608c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
618c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci *****************************************************************************/
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#include <linux/kernel.h>
668c2ecf20Sopenharmony_ci#include <linux/module.h>
678c2ecf20Sopenharmony_ci#include <linux/slab.h>
688c2ecf20Sopenharmony_ci#include <linux/etherdevice.h>
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci#include <net/mac80211.h>
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#include "iwl-debug.h"
738c2ecf20Sopenharmony_ci#include "mvm.h"
748c2ecf20Sopenharmony_ci#include "iwl-modparams.h"
758c2ecf20Sopenharmony_ci#include "fw/api/power.h"
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci#define POWER_KEEP_ALIVE_PERIOD_SEC    25
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic
808c2ecf20Sopenharmony_ciint iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm,
818c2ecf20Sopenharmony_ci				   struct iwl_beacon_filter_cmd *cmd,
828c2ecf20Sopenharmony_ci				   u32 flags)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	u16 len;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n",
878c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->ba_enable_beacon_abort));
888c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n",
898c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->ba_escape_timer));
908c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_debug_flag is: %d\n",
918c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_debug_flag));
928c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_enable_beacon_filter is: %d\n",
938c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_enable_beacon_filter));
948c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_energy_delta is: %d\n",
958c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_energy_delta));
968c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_escape_timer is: %d\n",
978c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_escape_timer));
988c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_roaming_energy_delta is: %d\n",
998c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_roaming_energy_delta));
1008c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_roaming_state is: %d\n",
1018c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_roaming_state));
1028c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_temp_threshold is: %d\n",
1038c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_temp_threshold));
1048c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_temp_fast_filter is: %d\n",
1058c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_temp_fast_filter));
1068c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n",
1078c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_temp_slow_filter));
1088c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_low is: %d, %d\n",
1098c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_threshold_absolute_low[0]),
1108c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_threshold_absolute_low[1]));
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_high is: %d, %d\n",
1138c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_threshold_absolute_high[0]),
1148c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->bf_threshold_absolute_high[1]));
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (fw_has_api(&mvm->fw->ucode_capa,
1178c2ecf20Sopenharmony_ci		       IWL_UCODE_TLV_API_BEACON_FILTER_V4))
1188c2ecf20Sopenharmony_ci		len = sizeof(struct iwl_beacon_filter_cmd);
1198c2ecf20Sopenharmony_ci	else
1208c2ecf20Sopenharmony_ci		len = offsetof(struct iwl_beacon_filter_cmd,
1218c2ecf20Sopenharmony_ci			       bf_threshold_absolute_low);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags,
1248c2ecf20Sopenharmony_ci				    len, cmd);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic
1288c2ecf20Sopenharmony_civoid iwl_mvm_beacon_filter_set_cqm_params(struct iwl_mvm *mvm,
1298c2ecf20Sopenharmony_ci					  struct ieee80211_vif *vif,
1308c2ecf20Sopenharmony_ci					  struct iwl_beacon_filter_cmd *cmd)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (vif->bss_conf.cqm_rssi_thold) {
1358c2ecf20Sopenharmony_ci		cmd->bf_energy_delta =
1368c2ecf20Sopenharmony_ci			cpu_to_le32(vif->bss_conf.cqm_rssi_hyst);
1378c2ecf20Sopenharmony_ci		/* fw uses an absolute value for this */
1388c2ecf20Sopenharmony_ci		cmd->bf_roaming_state =
1398c2ecf20Sopenharmony_ci			cpu_to_le32(-vif->bss_conf.cqm_rssi_thold);
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci	cmd->ba_enable_beacon_abort = cpu_to_le32(mvmvif->bf_data.ba_enabled);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic void iwl_mvm_power_log(struct iwl_mvm *mvm,
1458c2ecf20Sopenharmony_ci			      struct iwl_mac_power_cmd *cmd)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm,
1488c2ecf20Sopenharmony_ci			"Sending power table command on mac id 0x%X for power level %d, flags = 0x%X\n",
1498c2ecf20Sopenharmony_ci			cmd->id_and_color, iwlmvm_mod_params.power_scheme,
1508c2ecf20Sopenharmony_ci			le16_to_cpu(cmd->flags));
1518c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "Keep alive = %u sec\n",
1528c2ecf20Sopenharmony_ci			le16_to_cpu(cmd->keep_alive_seconds));
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) {
1558c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "Disable power management\n");
1568c2ecf20Sopenharmony_ci		return;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "Rx timeout = %u usec\n",
1608c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->rx_data_timeout));
1618c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm, "Tx timeout = %u usec\n",
1628c2ecf20Sopenharmony_ci			le32_to_cpu(cmd->tx_data_timeout));
1638c2ecf20Sopenharmony_ci	if (cmd->flags & cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK))
1648c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "DTIM periods to skip = %u\n",
1658c2ecf20Sopenharmony_ci				cmd->skip_dtim_periods);
1668c2ecf20Sopenharmony_ci	if (cmd->flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
1678c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "LP RX RSSI threshold = %u\n",
1688c2ecf20Sopenharmony_ci				cmd->lprx_rssi_threshold);
1698c2ecf20Sopenharmony_ci	if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) {
1708c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "uAPSD enabled\n");
1718c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "Rx timeout (uAPSD) = %u usec\n",
1728c2ecf20Sopenharmony_ci				le32_to_cpu(cmd->rx_data_timeout_uapsd));
1738c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "Tx timeout (uAPSD) = %u usec\n",
1748c2ecf20Sopenharmony_ci				le32_to_cpu(cmd->tx_data_timeout_uapsd));
1758c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "QNDP TID = %d\n", cmd->qndp_tid);
1768c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "ACs flags = 0x%x\n", cmd->uapsd_ac_flags);
1778c2ecf20Sopenharmony_ci		IWL_DEBUG_POWER(mvm, "Max SP = %d\n", cmd->uapsd_max_sp);
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
1828c2ecf20Sopenharmony_ci					  struct ieee80211_vif *vif,
1838c2ecf20Sopenharmony_ci					  struct iwl_mac_power_cmd *cmd)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
1868c2ecf20Sopenharmony_ci	enum ieee80211_ac_numbers ac;
1878c2ecf20Sopenharmony_ci	bool tid_found = false;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
1908c2ecf20Sopenharmony_ci	/* set advanced pm flag with no uapsd ACs to enable ps-poll */
1918c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.use_ps_poll) {
1928c2ecf20Sopenharmony_ci		cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
1938c2ecf20Sopenharmony_ci		return;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci#endif
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) {
1988c2ecf20Sopenharmony_ci		if (!mvmvif->queue_params[ac].uapsd)
1998c2ecf20Sopenharmony_ci			continue;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci		if (!test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))
2028c2ecf20Sopenharmony_ci			cmd->flags |=
2038c2ecf20Sopenharmony_ci				cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		cmd->uapsd_ac_flags |= BIT(ac);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci		/* QNDP TID - the highest TID with no admission control */
2088c2ecf20Sopenharmony_ci		if (!tid_found && !mvmvif->queue_params[ac].acm) {
2098c2ecf20Sopenharmony_ci			tid_found = true;
2108c2ecf20Sopenharmony_ci			switch (ac) {
2118c2ecf20Sopenharmony_ci			case IEEE80211_AC_VO:
2128c2ecf20Sopenharmony_ci				cmd->qndp_tid = 6;
2138c2ecf20Sopenharmony_ci				break;
2148c2ecf20Sopenharmony_ci			case IEEE80211_AC_VI:
2158c2ecf20Sopenharmony_ci				cmd->qndp_tid = 5;
2168c2ecf20Sopenharmony_ci				break;
2178c2ecf20Sopenharmony_ci			case IEEE80211_AC_BE:
2188c2ecf20Sopenharmony_ci				cmd->qndp_tid = 0;
2198c2ecf20Sopenharmony_ci				break;
2208c2ecf20Sopenharmony_ci			case IEEE80211_AC_BK:
2218c2ecf20Sopenharmony_ci				cmd->qndp_tid = 1;
2228c2ecf20Sopenharmony_ci				break;
2238c2ecf20Sopenharmony_ci			}
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci	}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) |
2308c2ecf20Sopenharmony_ci				    BIT(IEEE80211_AC_VI) |
2318c2ecf20Sopenharmony_ci				    BIT(IEEE80211_AC_BE) |
2328c2ecf20Sopenharmony_ci				    BIT(IEEE80211_AC_BK))) {
2338c2ecf20Sopenharmony_ci		cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
2348c2ecf20Sopenharmony_ci		cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL);
2358c2ecf20Sopenharmony_ci		cmd->snooze_window =
2368c2ecf20Sopenharmony_ci			test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status) ?
2378c2ecf20Sopenharmony_ci				cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) :
2388c2ecf20Sopenharmony_ci				cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW);
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	cmd->uapsd_max_sp = mvm->hw->uapsd_max_sp_len;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status) ||
2448c2ecf20Sopenharmony_ci	    cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
2458c2ecf20Sopenharmony_ci		cmd->rx_data_timeout_uapsd =
2468c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
2478c2ecf20Sopenharmony_ci		cmd->tx_data_timeout_uapsd =
2488c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
2498c2ecf20Sopenharmony_ci	} else {
2508c2ecf20Sopenharmony_ci		cmd->rx_data_timeout_uapsd =
2518c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT);
2528c2ecf20Sopenharmony_ci		cmd->tx_data_timeout_uapsd =
2538c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) {
2578c2ecf20Sopenharmony_ci		cmd->heavy_tx_thld_packets =
2588c2ecf20Sopenharmony_ci			IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS;
2598c2ecf20Sopenharmony_ci		cmd->heavy_rx_thld_packets =
2608c2ecf20Sopenharmony_ci			IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS;
2618c2ecf20Sopenharmony_ci	} else {
2628c2ecf20Sopenharmony_ci		cmd->heavy_tx_thld_packets =
2638c2ecf20Sopenharmony_ci			IWL_MVM_PS_HEAVY_TX_THLD_PACKETS;
2648c2ecf20Sopenharmony_ci		cmd->heavy_rx_thld_packets =
2658c2ecf20Sopenharmony_ci			IWL_MVM_PS_HEAVY_RX_THLD_PACKETS;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci	cmd->heavy_tx_thld_percentage =
2688c2ecf20Sopenharmony_ci		IWL_MVM_PS_HEAVY_TX_THLD_PERCENT;
2698c2ecf20Sopenharmony_ci	cmd->heavy_rx_thld_percentage =
2708c2ecf20Sopenharmony_ci		IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void iwl_mvm_p2p_standalone_iterator(void *_data, u8 *mac,
2748c2ecf20Sopenharmony_ci					    struct ieee80211_vif *vif)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	bool *is_p2p_standalone = _data;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	switch (ieee80211_vif_type_p2p(vif)) {
2798c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
2808c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
2818c2ecf20Sopenharmony_ci		*is_p2p_standalone = false;
2828c2ecf20Sopenharmony_ci		break;
2838c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
2848c2ecf20Sopenharmony_ci		if (vif->bss_conf.assoc)
2858c2ecf20Sopenharmony_ci			*is_p2p_standalone = false;
2868c2ecf20Sopenharmony_ci		break;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	default:
2898c2ecf20Sopenharmony_ci		break;
2908c2ecf20Sopenharmony_ci	}
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
2948c2ecf20Sopenharmony_ci				       struct ieee80211_vif *vif)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
2998c2ecf20Sopenharmony_ci		    ETH_ALEN))
3008c2ecf20Sopenharmony_ci		return false;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/*
3038c2ecf20Sopenharmony_ci	 * Avoid using uAPSD if P2P client is associated to GO that uses
3048c2ecf20Sopenharmony_ci	 * opportunistic power save. This is due to current FW limitation.
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	if (vif->p2p &&
3078c2ecf20Sopenharmony_ci	    (vif->bss_conf.p2p_noa_attr.oppps_ctwindow &
3088c2ecf20Sopenharmony_ci	    IEEE80211_P2P_OPPPS_ENABLE_BIT))
3098c2ecf20Sopenharmony_ci		return false;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/*
3128c2ecf20Sopenharmony_ci	 * Avoid using uAPSD if client is in DCM -
3138c2ecf20Sopenharmony_ci	 * low latency issue in Miracast
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	if (iwl_mvm_phy_ctx_count(mvm) >= 2)
3168c2ecf20Sopenharmony_ci		return false;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (vif->p2p) {
3198c2ecf20Sopenharmony_ci		/* Allow U-APSD only if p2p is stand alone */
3208c2ecf20Sopenharmony_ci		bool is_p2p_standalone = true;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci		if (!iwl_mvm_is_p2p_scm_uapsd_supported(mvm))
3238c2ecf20Sopenharmony_ci			return false;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		ieee80211_iterate_active_interfaces_atomic(mvm->hw,
3268c2ecf20Sopenharmony_ci					IEEE80211_IFACE_ITER_NORMAL,
3278c2ecf20Sopenharmony_ci					iwl_mvm_p2p_standalone_iterator,
3288c2ecf20Sopenharmony_ci					&is_p2p_standalone);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci		if (!is_p2p_standalone)
3318c2ecf20Sopenharmony_ci			return false;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return true;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic bool iwl_mvm_power_is_radar(struct ieee80211_vif *vif)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	struct ieee80211_chanctx_conf *chanctx_conf;
3408c2ecf20Sopenharmony_ci	struct ieee80211_channel *chan;
3418c2ecf20Sopenharmony_ci	bool radar_detect = false;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	rcu_read_lock();
3448c2ecf20Sopenharmony_ci	chanctx_conf = rcu_dereference(vif->chanctx_conf);
3458c2ecf20Sopenharmony_ci	WARN_ON(!chanctx_conf);
3468c2ecf20Sopenharmony_ci	if (chanctx_conf) {
3478c2ecf20Sopenharmony_ci		chan = chanctx_conf->def.chan;
3488c2ecf20Sopenharmony_ci		radar_detect = chan->flags & IEEE80211_CHAN_RADAR;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci	rcu_read_unlock();
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	return radar_detect;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic void iwl_mvm_power_config_skip_dtim(struct iwl_mvm *mvm,
3568c2ecf20Sopenharmony_ci					   struct ieee80211_vif *vif,
3578c2ecf20Sopenharmony_ci					   struct iwl_mac_power_cmd *cmd)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	int dtimper = vif->bss_conf.dtim_period ?: 1;
3608c2ecf20Sopenharmony_ci	int skip;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	/* disable, in case we're supposed to override */
3638c2ecf20Sopenharmony_ci	cmd->skip_dtim_periods = 0;
3648c2ecf20Sopenharmony_ci	cmd->flags &= ~cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (iwl_mvm_power_is_radar(vif))
3678c2ecf20Sopenharmony_ci		return;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (dtimper >= 10)
3708c2ecf20Sopenharmony_ci		return;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (!test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status)) {
3738c2ecf20Sopenharmony_ci		if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_LP)
3748c2ecf20Sopenharmony_ci			return;
3758c2ecf20Sopenharmony_ci		skip = 2;
3768c2ecf20Sopenharmony_ci	} else {
3778c2ecf20Sopenharmony_ci		int dtimper_tu = dtimper * vif->bss_conf.beacon_int;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		if (WARN_ON(!dtimper_tu))
3808c2ecf20Sopenharmony_ci			return;
3818c2ecf20Sopenharmony_ci		/* configure skip over dtim up to 306TU - 314 msec */
3828c2ecf20Sopenharmony_ci		skip = max_t(u8, 1, 306 / dtimper_tu);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	/* the firmware really expects "look at every X DTIMs", so add 1 */
3868c2ecf20Sopenharmony_ci	cmd->skip_dtim_periods = 1 + skip;
3878c2ecf20Sopenharmony_ci	cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm,
3918c2ecf20Sopenharmony_ci				    struct ieee80211_vif *vif,
3928c2ecf20Sopenharmony_ci				    struct iwl_mac_power_cmd *cmd)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	int dtimper, bi;
3958c2ecf20Sopenharmony_ci	int keep_alive;
3968c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif __maybe_unused =
3978c2ecf20Sopenharmony_ci		iwl_mvm_vif_from_mac80211(vif);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id,
4008c2ecf20Sopenharmony_ci							    mvmvif->color));
4018c2ecf20Sopenharmony_ci	dtimper = vif->bss_conf.dtim_period;
4028c2ecf20Sopenharmony_ci	bi = vif->bss_conf.beacon_int;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/*
4058c2ecf20Sopenharmony_ci	 * Regardless of power management state the driver must set
4068c2ecf20Sopenharmony_ci	 * keep alive period. FW will use it for sending keep alive NDPs
4078c2ecf20Sopenharmony_ci	 * immediately after association. Check that keep alive period
4088c2ecf20Sopenharmony_ci	 * is at least 3 * DTIM
4098c2ecf20Sopenharmony_ci	 */
4108c2ecf20Sopenharmony_ci	keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi),
4118c2ecf20Sopenharmony_ci				  USEC_PER_SEC);
4128c2ecf20Sopenharmony_ci	keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC);
4138c2ecf20Sopenharmony_ci	cmd->keep_alive_seconds = cpu_to_le16(keep_alive);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	if (mvm->ps_disabled)
4168c2ecf20Sopenharmony_ci		return;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (!vif->bss_conf.ps || !mvmvif->pm_enabled)
4218c2ecf20Sopenharmony_ci		return;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
4248c2ecf20Sopenharmony_ci	    (!fw_has_capa(&mvm->fw->ucode_capa,
4258c2ecf20Sopenharmony_ci			 IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) ||
4268c2ecf20Sopenharmony_ci	     !IWL_MVM_P2P_LOWLATENCY_PS_ENABLE))
4278c2ecf20Sopenharmony_ci		return;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	if (vif->bss_conf.beacon_rate &&
4328c2ecf20Sopenharmony_ci	    (vif->bss_conf.beacon_rate->bitrate == 10 ||
4338c2ecf20Sopenharmony_ci	     vif->bss_conf.beacon_rate->bitrate == 60)) {
4348c2ecf20Sopenharmony_ci		cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
4358c2ecf20Sopenharmony_ci		cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD;
4368c2ecf20Sopenharmony_ci	}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	iwl_mvm_power_config_skip_dtim(mvm, vif, cmd);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status)) {
4418c2ecf20Sopenharmony_ci		cmd->rx_data_timeout =
4428c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT);
4438c2ecf20Sopenharmony_ci		cmd->tx_data_timeout =
4448c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT);
4458c2ecf20Sopenharmony_ci	} else if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p &&
4468c2ecf20Sopenharmony_ci		   fw_has_capa(&mvm->fw->ucode_capa,
4478c2ecf20Sopenharmony_ci			       IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS)) {
4488c2ecf20Sopenharmony_ci		cmd->tx_data_timeout =
4498c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_SHORT_PS_TX_DATA_TIMEOUT);
4508c2ecf20Sopenharmony_ci		cmd->rx_data_timeout =
4518c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_SHORT_PS_RX_DATA_TIMEOUT);
4528c2ecf20Sopenharmony_ci	} else {
4538c2ecf20Sopenharmony_ci		cmd->rx_data_timeout =
4548c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_DEFAULT_PS_RX_DATA_TIMEOUT);
4558c2ecf20Sopenharmony_ci		cmd->tx_data_timeout =
4568c2ecf20Sopenharmony_ci			cpu_to_le32(IWL_MVM_DEFAULT_PS_TX_DATA_TIMEOUT);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if (iwl_mvm_power_allow_uapsd(mvm, vif))
4608c2ecf20Sopenharmony_ci		iwl_mvm_power_configure_uapsd(mvm, vif, cmd);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
4638c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE)
4648c2ecf20Sopenharmony_ci		cmd->keep_alive_seconds =
4658c2ecf20Sopenharmony_ci			cpu_to_le16(mvmvif->dbgfs_pm.keep_alive_seconds);
4668c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_OVER_DTIM) {
4678c2ecf20Sopenharmony_ci		if (mvmvif->dbgfs_pm.skip_over_dtim)
4688c2ecf20Sopenharmony_ci			cmd->flags |=
4698c2ecf20Sopenharmony_ci				cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK);
4708c2ecf20Sopenharmony_ci		else
4718c2ecf20Sopenharmony_ci			cmd->flags &=
4728c2ecf20Sopenharmony_ci				cpu_to_le16(~POWER_FLAGS_SKIP_OVER_DTIM_MSK);
4738c2ecf20Sopenharmony_ci	}
4748c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_RX_DATA_TIMEOUT)
4758c2ecf20Sopenharmony_ci		cmd->rx_data_timeout =
4768c2ecf20Sopenharmony_ci			cpu_to_le32(mvmvif->dbgfs_pm.rx_data_timeout);
4778c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_TX_DATA_TIMEOUT)
4788c2ecf20Sopenharmony_ci		cmd->tx_data_timeout =
4798c2ecf20Sopenharmony_ci			cpu_to_le32(mvmvif->dbgfs_pm.tx_data_timeout);
4808c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS)
4818c2ecf20Sopenharmony_ci		cmd->skip_dtim_periods = mvmvif->dbgfs_pm.skip_dtim_periods;
4828c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_ENA) {
4838c2ecf20Sopenharmony_ci		if (mvmvif->dbgfs_pm.lprx_ena)
4848c2ecf20Sopenharmony_ci			cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK);
4858c2ecf20Sopenharmony_ci		else
4868c2ecf20Sopenharmony_ci			cmd->flags &= cpu_to_le16(~POWER_FLAGS_LPRX_ENA_MSK);
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD)
4898c2ecf20Sopenharmony_ci		cmd->lprx_rssi_threshold = mvmvif->dbgfs_pm.lprx_rssi_threshold;
4908c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_SNOOZE_ENABLE) {
4918c2ecf20Sopenharmony_ci		if (mvmvif->dbgfs_pm.snooze_ena)
4928c2ecf20Sopenharmony_ci			cmd->flags |=
4938c2ecf20Sopenharmony_ci				cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK);
4948c2ecf20Sopenharmony_ci		else
4958c2ecf20Sopenharmony_ci			cmd->flags &=
4968c2ecf20Sopenharmony_ci				cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK);
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) {
4998c2ecf20Sopenharmony_ci		u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK;
5008c2ecf20Sopenharmony_ci		if (mvmvif->dbgfs_pm.uapsd_misbehaving)
5018c2ecf20Sopenharmony_ci			cmd->flags |= cpu_to_le16(flag);
5028c2ecf20Sopenharmony_ci		else
5038c2ecf20Sopenharmony_ci			cmd->flags &= cpu_to_le16(flag);
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci#endif /* CONFIG_IWLWIFI_DEBUGFS */
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_cistatic int iwl_mvm_power_send_cmd(struct iwl_mvm *mvm,
5098c2ecf20Sopenharmony_ci					 struct ieee80211_vif *vif)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	struct iwl_mac_power_cmd cmd = {};
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	iwl_mvm_power_build_cmd(mvm, vif, &cmd);
5148c2ecf20Sopenharmony_ci	iwl_mvm_power_log(mvm, &cmd);
5158c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
5168c2ecf20Sopenharmony_ci	memcpy(&iwl_mvm_vif_from_mac80211(vif)->mac_pwr_cmd, &cmd, sizeof(cmd));
5178c2ecf20Sopenharmony_ci#endif
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, MAC_PM_POWER_TABLE, 0,
5208c2ecf20Sopenharmony_ci				    sizeof(cmd), &cmd);
5218c2ecf20Sopenharmony_ci}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ciint iwl_mvm_power_update_device(struct iwl_mvm *mvm)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct iwl_device_power_cmd cmd = {
5268c2ecf20Sopenharmony_ci		.flags = 0,
5278c2ecf20Sopenharmony_ci	};
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM)
5308c2ecf20Sopenharmony_ci		mvm->ps_disabled = true;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	if (!mvm->ps_disabled)
5338c2ecf20Sopenharmony_ci		cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
5368c2ecf20Sopenharmony_ci	if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status) ?
5378c2ecf20Sopenharmony_ci			mvm->disable_power_off_d3 : mvm->disable_power_off)
5388c2ecf20Sopenharmony_ci		cmd.flags &=
5398c2ecf20Sopenharmony_ci			cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
5408c2ecf20Sopenharmony_ci#endif
5418c2ecf20Sopenharmony_ci	if (mvm->ext_clock_valid)
5428c2ecf20Sopenharmony_ci		cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_32K_CLK_VALID_MSK);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	IWL_DEBUG_POWER(mvm,
5458c2ecf20Sopenharmony_ci			"Sending device power command with flags = 0x%X\n",
5468c2ecf20Sopenharmony_ci			cmd.flags);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, 0, sizeof(cmd),
5498c2ecf20Sopenharmony_ci				    &cmd);
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_civoid iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid,
5578c2ecf20Sopenharmony_ci		   ETH_ALEN))
5588c2ecf20Sopenharmony_ci		eth_zero_addr(mvmvif->uapsd_misbehaving_bssid);
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac,
5628c2ecf20Sopenharmony_ci						     struct ieee80211_vif *vif)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	u8 *ap_sta_id = _data;
5658c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* The ap_sta_id is not expected to change during current association
5688c2ecf20Sopenharmony_ci	 * so no explicit protection is needed
5698c2ecf20Sopenharmony_ci	 */
5708c2ecf20Sopenharmony_ci	if (mvmvif->ap_sta_id == *ap_sta_id)
5718c2ecf20Sopenharmony_ci		memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
5728c2ecf20Sopenharmony_ci		       ETH_ALEN);
5738c2ecf20Sopenharmony_ci}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_civoid iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
5768c2ecf20Sopenharmony_ci					      struct iwl_rx_cmd_buffer *rxb)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	struct iwl_rx_packet *pkt = rxb_addr(rxb);
5798c2ecf20Sopenharmony_ci	struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data;
5808c2ecf20Sopenharmony_ci	u8 ap_sta_id = le32_to_cpu(notif->sta_id);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(
5838c2ecf20Sopenharmony_ci		mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
5848c2ecf20Sopenharmony_ci		iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id);
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistruct iwl_power_vifs {
5888c2ecf20Sopenharmony_ci	struct iwl_mvm *mvm;
5898c2ecf20Sopenharmony_ci	struct ieee80211_vif *bss_vif;
5908c2ecf20Sopenharmony_ci	struct ieee80211_vif *p2p_vif;
5918c2ecf20Sopenharmony_ci	struct ieee80211_vif *ap_vif;
5928c2ecf20Sopenharmony_ci	struct ieee80211_vif *monitor_vif;
5938c2ecf20Sopenharmony_ci	bool p2p_active;
5948c2ecf20Sopenharmony_ci	bool bss_active;
5958c2ecf20Sopenharmony_ci	bool ap_active;
5968c2ecf20Sopenharmony_ci	bool monitor_active;
5978c2ecf20Sopenharmony_ci};
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_cistatic void iwl_mvm_power_disable_pm_iterator(void *_data, u8* mac,
6008c2ecf20Sopenharmony_ci					      struct ieee80211_vif *vif)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	mvmvif->pm_enabled = false;
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic void iwl_mvm_power_ps_disabled_iterator(void *_data, u8* mac,
6088c2ecf20Sopenharmony_ci					       struct ieee80211_vif *vif)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
6118c2ecf20Sopenharmony_ci	bool *disable_ps = _data;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	if (mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < NUM_PHY_CTX)
6148c2ecf20Sopenharmony_ci		*disable_ps |= mvmvif->ps_disabled;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac,
6188c2ecf20Sopenharmony_ci					    struct ieee80211_vif *vif)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
6218c2ecf20Sopenharmony_ci	struct iwl_power_vifs *power_iterator = _data;
6228c2ecf20Sopenharmony_ci	bool active = mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < NUM_PHY_CTX;
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (!mvmvif->uploaded)
6258c2ecf20Sopenharmony_ci		return;
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	switch (ieee80211_vif_type_p2p(vif)) {
6288c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
6298c2ecf20Sopenharmony_ci		break;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
6328c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
6338c2ecf20Sopenharmony_ci		/* only a single MAC of the same type */
6348c2ecf20Sopenharmony_ci		WARN_ON(power_iterator->ap_vif);
6358c2ecf20Sopenharmony_ci		power_iterator->ap_vif = vif;
6368c2ecf20Sopenharmony_ci		if (active)
6378c2ecf20Sopenharmony_ci			power_iterator->ap_active = true;
6388c2ecf20Sopenharmony_ci		break;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
6418c2ecf20Sopenharmony_ci		/* only a single MAC of the same type */
6428c2ecf20Sopenharmony_ci		WARN_ON(power_iterator->monitor_vif);
6438c2ecf20Sopenharmony_ci		power_iterator->monitor_vif = vif;
6448c2ecf20Sopenharmony_ci		if (active)
6458c2ecf20Sopenharmony_ci			power_iterator->monitor_active = true;
6468c2ecf20Sopenharmony_ci		break;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
6498c2ecf20Sopenharmony_ci		/* only a single MAC of the same type */
6508c2ecf20Sopenharmony_ci		WARN_ON(power_iterator->p2p_vif);
6518c2ecf20Sopenharmony_ci		power_iterator->p2p_vif = vif;
6528c2ecf20Sopenharmony_ci		if (active)
6538c2ecf20Sopenharmony_ci			power_iterator->p2p_active = true;
6548c2ecf20Sopenharmony_ci		break;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
6578c2ecf20Sopenharmony_ci		power_iterator->bss_vif = vif;
6588c2ecf20Sopenharmony_ci		if (active)
6598c2ecf20Sopenharmony_ci			power_iterator->bss_active = true;
6608c2ecf20Sopenharmony_ci		break;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	default:
6638c2ecf20Sopenharmony_ci		break;
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_cistatic void iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
6688c2ecf20Sopenharmony_ci				 struct iwl_power_vifs *vifs)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *bss_mvmvif = NULL;
6718c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *p2p_mvmvif = NULL;
6728c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *ap_mvmvif = NULL;
6738c2ecf20Sopenharmony_ci	bool client_same_channel = false;
6748c2ecf20Sopenharmony_ci	bool ap_same_channel = false;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	/* set pm_enable to false */
6798c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
6808c2ecf20Sopenharmony_ci					IEEE80211_IFACE_ITER_NORMAL,
6818c2ecf20Sopenharmony_ci					iwl_mvm_power_disable_pm_iterator,
6828c2ecf20Sopenharmony_ci					NULL);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	if (vifs->bss_vif)
6858c2ecf20Sopenharmony_ci		bss_mvmvif = iwl_mvm_vif_from_mac80211(vifs->bss_vif);
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if (vifs->p2p_vif)
6888c2ecf20Sopenharmony_ci		p2p_mvmvif = iwl_mvm_vif_from_mac80211(vifs->p2p_vif);
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if (vifs->ap_vif)
6918c2ecf20Sopenharmony_ci		ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* don't allow PM if any TDLS stations exist */
6948c2ecf20Sopenharmony_ci	if (iwl_mvm_tdls_sta_count(mvm, NULL))
6958c2ecf20Sopenharmony_ci		return;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	/* enable PM on bss if bss stand alone */
6988c2ecf20Sopenharmony_ci	if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
6998c2ecf20Sopenharmony_ci		bss_mvmvif->pm_enabled = true;
7008c2ecf20Sopenharmony_ci		return;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	/* enable PM on p2p if p2p stand alone */
7048c2ecf20Sopenharmony_ci	if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
7058c2ecf20Sopenharmony_ci		p2p_mvmvif->pm_enabled = true;
7068c2ecf20Sopenharmony_ci		return;
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (vifs->bss_active && vifs->p2p_active)
7108c2ecf20Sopenharmony_ci		client_same_channel = (bss_mvmvif->phy_ctxt->id ==
7118c2ecf20Sopenharmony_ci				       p2p_mvmvif->phy_ctxt->id);
7128c2ecf20Sopenharmony_ci	if (vifs->bss_active && vifs->ap_active)
7138c2ecf20Sopenharmony_ci		ap_same_channel = (bss_mvmvif->phy_ctxt->id ==
7148c2ecf20Sopenharmony_ci				   ap_mvmvif->phy_ctxt->id);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* clients are not stand alone: enable PM if DCM */
7178c2ecf20Sopenharmony_ci	if (!(client_same_channel || ap_same_channel)) {
7188c2ecf20Sopenharmony_ci		if (vifs->bss_active)
7198c2ecf20Sopenharmony_ci			bss_mvmvif->pm_enabled = true;
7208c2ecf20Sopenharmony_ci		if (vifs->p2p_active)
7218c2ecf20Sopenharmony_ci			p2p_mvmvif->pm_enabled = true;
7228c2ecf20Sopenharmony_ci		return;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	/*
7268c2ecf20Sopenharmony_ci	 * There is only one channel in the system and there are only
7278c2ecf20Sopenharmony_ci	 * bss and p2p clients that share it
7288c2ecf20Sopenharmony_ci	 */
7298c2ecf20Sopenharmony_ci	if (client_same_channel && !vifs->ap_active) {
7308c2ecf20Sopenharmony_ci		/* share same channel*/
7318c2ecf20Sopenharmony_ci		bss_mvmvif->pm_enabled = true;
7328c2ecf20Sopenharmony_ci		p2p_mvmvif->pm_enabled = true;
7338c2ecf20Sopenharmony_ci	}
7348c2ecf20Sopenharmony_ci}
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS
7378c2ecf20Sopenharmony_ciint iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm,
7388c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif, char *buf,
7398c2ecf20Sopenharmony_ci				 int bufsz)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
7428c2ecf20Sopenharmony_ci	struct iwl_mac_power_cmd cmd = {};
7438c2ecf20Sopenharmony_ci	int pos = 0;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	mutex_lock(&mvm->mutex);
7468c2ecf20Sopenharmony_ci	memcpy(&cmd, &mvmvif->mac_pwr_cmd, sizeof(cmd));
7478c2ecf20Sopenharmony_ci	mutex_unlock(&mvm->mutex);
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n",
7508c2ecf20Sopenharmony_ci			 iwlmvm_mod_params.power_scheme);
7518c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n",
7528c2ecf20Sopenharmony_ci			 le16_to_cpu(cmd.flags));
7538c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n",
7548c2ecf20Sopenharmony_ci			 le16_to_cpu(cmd.keep_alive_seconds));
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)))
7578c2ecf20Sopenharmony_ci		return pos;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n",
7608c2ecf20Sopenharmony_ci			 (cmd.flags &
7618c2ecf20Sopenharmony_ci			 cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0);
7628c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n",
7638c2ecf20Sopenharmony_ci			 cmd.skip_dtim_periods);
7648c2ecf20Sopenharmony_ci	if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) {
7658c2ecf20Sopenharmony_ci		pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n",
7668c2ecf20Sopenharmony_ci				 le32_to_cpu(cmd.rx_data_timeout));
7678c2ecf20Sopenharmony_ci		pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n",
7688c2ecf20Sopenharmony_ci				 le32_to_cpu(cmd.tx_data_timeout));
7698c2ecf20Sopenharmony_ci	}
7708c2ecf20Sopenharmony_ci	if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK))
7718c2ecf20Sopenharmony_ci		pos += scnprintf(buf+pos, bufsz-pos,
7728c2ecf20Sopenharmony_ci				 "lprx_rssi_threshold = %d\n",
7738c2ecf20Sopenharmony_ci				 cmd.lprx_rssi_threshold);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)))
7768c2ecf20Sopenharmony_ci		return pos;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n",
7798c2ecf20Sopenharmony_ci			 le32_to_cpu(cmd.rx_data_timeout_uapsd));
7808c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n",
7818c2ecf20Sopenharmony_ci			 le32_to_cpu(cmd.tx_data_timeout_uapsd));
7828c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid);
7838c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n",
7848c2ecf20Sopenharmony_ci			 cmd.uapsd_ac_flags);
7858c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n",
7868c2ecf20Sopenharmony_ci			 cmd.uapsd_max_sp);
7878c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n",
7888c2ecf20Sopenharmony_ci			 cmd.heavy_tx_thld_packets);
7898c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n",
7908c2ecf20Sopenharmony_ci			 cmd.heavy_rx_thld_packets);
7918c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n",
7928c2ecf20Sopenharmony_ci			 cmd.heavy_tx_thld_percentage);
7938c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n",
7948c2ecf20Sopenharmony_ci			 cmd.heavy_rx_thld_percentage);
7958c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n",
7968c2ecf20Sopenharmony_ci			 (cmd.flags &
7978c2ecf20Sopenharmony_ci			  cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ?
7988c2ecf20Sopenharmony_ci			 1 : 0);
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)))
8018c2ecf20Sopenharmony_ci		return pos;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n",
8048c2ecf20Sopenharmony_ci			 cmd.snooze_interval);
8058c2ecf20Sopenharmony_ci	pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n",
8068c2ecf20Sopenharmony_ci			 cmd.snooze_window);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	return pos;
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_civoid
8128c2ecf20Sopenharmony_ciiwl_mvm_beacon_filter_debugfs_parameters(struct ieee80211_vif *vif,
8138c2ecf20Sopenharmony_ci					 struct iwl_beacon_filter_cmd *cmd)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
8168c2ecf20Sopenharmony_ci	struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ENERGY_DELTA)
8198c2ecf20Sopenharmony_ci		cmd->bf_energy_delta = cpu_to_le32(dbgfs_bf->bf_energy_delta);
8208c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA)
8218c2ecf20Sopenharmony_ci		cmd->bf_roaming_energy_delta =
8228c2ecf20Sopenharmony_ci				cpu_to_le32(dbgfs_bf->bf_roaming_energy_delta);
8238c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ROAMING_STATE)
8248c2ecf20Sopenharmony_ci		cmd->bf_roaming_state = cpu_to_le32(dbgfs_bf->bf_roaming_state);
8258c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_THRESHOLD)
8268c2ecf20Sopenharmony_ci		cmd->bf_temp_threshold =
8278c2ecf20Sopenharmony_ci				cpu_to_le32(dbgfs_bf->bf_temp_threshold);
8288c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_FAST_FILTER)
8298c2ecf20Sopenharmony_ci		cmd->bf_temp_fast_filter =
8308c2ecf20Sopenharmony_ci				cpu_to_le32(dbgfs_bf->bf_temp_fast_filter);
8318c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_TEMP_SLOW_FILTER)
8328c2ecf20Sopenharmony_ci		cmd->bf_temp_slow_filter =
8338c2ecf20Sopenharmony_ci				cpu_to_le32(dbgfs_bf->bf_temp_slow_filter);
8348c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_DEBUG_FLAG)
8358c2ecf20Sopenharmony_ci		cmd->bf_debug_flag = cpu_to_le32(dbgfs_bf->bf_debug_flag);
8368c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BF_ESCAPE_TIMER)
8378c2ecf20Sopenharmony_ci		cmd->bf_escape_timer = cpu_to_le32(dbgfs_bf->bf_escape_timer);
8388c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ESCAPE_TIMER)
8398c2ecf20Sopenharmony_ci		cmd->ba_escape_timer = cpu_to_le32(dbgfs_bf->ba_escape_timer);
8408c2ecf20Sopenharmony_ci	if (dbgfs_bf->mask & MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT)
8418c2ecf20Sopenharmony_ci		cmd->ba_enable_beacon_abort =
8428c2ecf20Sopenharmony_ci				cpu_to_le32(dbgfs_bf->ba_enable_beacon_abort);
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci#endif
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic int _iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
8478c2ecf20Sopenharmony_ci					 struct ieee80211_vif *vif,
8488c2ecf20Sopenharmony_ci					 struct iwl_beacon_filter_cmd *cmd,
8498c2ecf20Sopenharmony_ci					 u32 cmd_flags)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
8528c2ecf20Sopenharmony_ci	int ret;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (mvmvif != mvm->bf_allowed_vif || !vif->bss_conf.dtim_period ||
8558c2ecf20Sopenharmony_ci	    vif->type != NL80211_IFTYPE_STATION || vif->p2p)
8568c2ecf20Sopenharmony_ci		return 0;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	iwl_mvm_beacon_filter_set_cqm_params(mvm, vif, cmd);
8598c2ecf20Sopenharmony_ci	iwl_mvm_beacon_filter_debugfs_parameters(vif, cmd);
8608c2ecf20Sopenharmony_ci	ret = iwl_mvm_beacon_filter_send_cmd(mvm, cmd, cmd_flags);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	if (!ret)
8638c2ecf20Sopenharmony_ci		mvmvif->bf_data.bf_enabled = true;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	return ret;
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ciint iwl_mvm_enable_beacon_filter(struct iwl_mvm *mvm,
8698c2ecf20Sopenharmony_ci				 struct ieee80211_vif *vif,
8708c2ecf20Sopenharmony_ci				 u32 flags)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	struct iwl_beacon_filter_cmd cmd = {
8738c2ecf20Sopenharmony_ci		IWL_BF_CMD_CONFIG_DEFAULTS,
8748c2ecf20Sopenharmony_ci		.bf_enable_beacon_filter = cpu_to_le32(1),
8758c2ecf20Sopenharmony_ci	};
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, flags);
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistatic int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
8818c2ecf20Sopenharmony_ci					  struct ieee80211_vif *vif,
8828c2ecf20Sopenharmony_ci					  u32 flags)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	struct iwl_beacon_filter_cmd cmd = {};
8858c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
8868c2ecf20Sopenharmony_ci	int ret;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_STATION || vif->p2p)
8898c2ecf20Sopenharmony_ci		return 0;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	if (!ret)
8948c2ecf20Sopenharmony_ci		mvmvif->bf_data.bf_enabled = false;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	return ret;
8978c2ecf20Sopenharmony_ci}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ciint iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
9008c2ecf20Sopenharmony_ci				  struct ieee80211_vif *vif,
9018c2ecf20Sopenharmony_ci				  u32 flags)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	return _iwl_mvm_disable_beacon_filter(mvm, vif, flags);
9048c2ecf20Sopenharmony_ci}
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cistatic int iwl_mvm_power_set_ps(struct iwl_mvm *mvm)
9078c2ecf20Sopenharmony_ci{
9088c2ecf20Sopenharmony_ci	bool disable_ps;
9098c2ecf20Sopenharmony_ci	int ret;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	/* disable PS if CAM */
9128c2ecf20Sopenharmony_ci	disable_ps = (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM);
9138c2ecf20Sopenharmony_ci	/* ...or if any of the vifs require PS to be off */
9148c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
9158c2ecf20Sopenharmony_ci					IEEE80211_IFACE_ITER_NORMAL,
9168c2ecf20Sopenharmony_ci					iwl_mvm_power_ps_disabled_iterator,
9178c2ecf20Sopenharmony_ci					&disable_ps);
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	/* update device power state if it has changed */
9208c2ecf20Sopenharmony_ci	if (mvm->ps_disabled != disable_ps) {
9218c2ecf20Sopenharmony_ci		bool old_ps_disabled = mvm->ps_disabled;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci		mvm->ps_disabled = disable_ps;
9248c2ecf20Sopenharmony_ci		ret = iwl_mvm_power_update_device(mvm);
9258c2ecf20Sopenharmony_ci		if (ret) {
9268c2ecf20Sopenharmony_ci			mvm->ps_disabled = old_ps_disabled;
9278c2ecf20Sopenharmony_ci			return ret;
9288c2ecf20Sopenharmony_ci		}
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	return 0;
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_cistatic int iwl_mvm_power_set_ba(struct iwl_mvm *mvm,
9358c2ecf20Sopenharmony_ci				struct ieee80211_vif *vif)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
9388c2ecf20Sopenharmony_ci	struct iwl_beacon_filter_cmd cmd = {
9398c2ecf20Sopenharmony_ci		IWL_BF_CMD_CONFIG_DEFAULTS,
9408c2ecf20Sopenharmony_ci		.bf_enable_beacon_filter = cpu_to_le32(1),
9418c2ecf20Sopenharmony_ci	};
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	if (!mvmvif->bf_data.bf_enabled)
9448c2ecf20Sopenharmony_ci		return 0;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	if (test_bit(IWL_MVM_STATUS_IN_D3, &mvm->status))
9478c2ecf20Sopenharmony_ci		cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	mvmvif->bf_data.ba_enabled = !(!mvmvif->pm_enabled ||
9508c2ecf20Sopenharmony_ci				       mvm->ps_disabled ||
9518c2ecf20Sopenharmony_ci				       !vif->bss_conf.ps ||
9528c2ecf20Sopenharmony_ci				       iwl_mvm_vif_low_latency(mvmvif));
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0);
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ciint iwl_mvm_power_update_ps(struct iwl_mvm *mvm)
9588c2ecf20Sopenharmony_ci{
9598c2ecf20Sopenharmony_ci	struct iwl_power_vifs vifs = {
9608c2ecf20Sopenharmony_ci		.mvm = mvm,
9618c2ecf20Sopenharmony_ci	};
9628c2ecf20Sopenharmony_ci	int ret;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	/* get vifs info */
9678c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
9688c2ecf20Sopenharmony_ci					IEEE80211_IFACE_ITER_NORMAL,
9698c2ecf20Sopenharmony_ci					iwl_mvm_power_get_vifs_iterator, &vifs);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	ret = iwl_mvm_power_set_ps(mvm);
9728c2ecf20Sopenharmony_ci	if (ret)
9738c2ecf20Sopenharmony_ci		return ret;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	if (vifs.bss_vif)
9768c2ecf20Sopenharmony_ci		return iwl_mvm_power_set_ba(mvm, vifs.bss_vif);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	return 0;
9798c2ecf20Sopenharmony_ci}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ciint iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	struct iwl_power_vifs vifs = {
9848c2ecf20Sopenharmony_ci		.mvm = mvm,
9858c2ecf20Sopenharmony_ci	};
9868c2ecf20Sopenharmony_ci	int ret;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	lockdep_assert_held(&mvm->mutex);
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	/* get vifs info */
9918c2ecf20Sopenharmony_ci	ieee80211_iterate_active_interfaces_atomic(mvm->hw,
9928c2ecf20Sopenharmony_ci					IEEE80211_IFACE_ITER_NORMAL,
9938c2ecf20Sopenharmony_ci					iwl_mvm_power_get_vifs_iterator, &vifs);
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	iwl_mvm_power_set_pm(mvm, &vifs);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	ret = iwl_mvm_power_set_ps(mvm);
9988c2ecf20Sopenharmony_ci	if (ret)
9998c2ecf20Sopenharmony_ci		return ret;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	if (vifs.bss_vif) {
10028c2ecf20Sopenharmony_ci		ret = iwl_mvm_power_send_cmd(mvm, vifs.bss_vif);
10038c2ecf20Sopenharmony_ci		if (ret)
10048c2ecf20Sopenharmony_ci			return ret;
10058c2ecf20Sopenharmony_ci	}
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	if (vifs.p2p_vif) {
10088c2ecf20Sopenharmony_ci		ret = iwl_mvm_power_send_cmd(mvm, vifs.p2p_vif);
10098c2ecf20Sopenharmony_ci		if (ret)
10108c2ecf20Sopenharmony_ci			return ret;
10118c2ecf20Sopenharmony_ci	}
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	if (vifs.bss_vif)
10148c2ecf20Sopenharmony_ci		return iwl_mvm_power_set_ba(mvm, vifs.bss_vif);
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	return 0;
10178c2ecf20Sopenharmony_ci}
1018