summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/ath11k/wow.c
diff options
context:
space:
mode:
authorCarl Huang <quic_cjhuang@quicinc.com>2022-03-14 07:18:16 +0200
committerKalle Valo <quic_kvalo@quicinc.com>2022-03-18 17:35:20 +0200
commitc417b247ba042161ddfb34f26a42ec67edc5c378 (patch)
tree181b2614e4e5b0aa3433dc7392ef363951eb9728 /drivers/net/wireless/ath/ath11k/wow.c
parentfec4b898f369a9b9d516f7bfc459eb4a8c5ceb2c (diff)
ath11k: implement hardware data filter
Host needs to set hardware data filter before entering WoW to let firmware drop needless broadcast/mulitcast frames to avoid frequent wakeup. Host clears hardware data filter when leaving WoW. Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1 Signed-off-by: Carl Huang <quic_cjhuang@quicinc.com> Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> Link: https://lore.kernel.org/r/1644308006-22784-4-git-send-email-quic_cjhuang@quicinc.com
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/wow.c')
-rw-r--r--drivers/net/wireless/ath/ath11k/wow.c57
1 files changed, 57 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c
index 5f3aa0b7eb9b..50dd50944edd 100644
--- a/drivers/net/wireless/ath/ath11k/wow.c
+++ b/drivers/net/wireless/ath/ath11k/wow.c
@@ -514,6 +514,50 @@ static int ath11k_wow_nlo_cleanup(struct ath11k *ar)
return 0;
}
+static int ath11k_wow_set_hw_filter(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+ u32 bitmap;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC |
+ WMI_HW_DATA_FILTER_DROP_NON_ARP_BC;
+ ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id,
+ bitmap,
+ true);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ath11k_wow_clear_hw_filter(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false);
+
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
@@ -542,6 +586,13 @@ int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
goto cleanup;
}
+ ret = ath11k_wow_set_hw_filter(ar);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to set hw filter: %d\n",
+ ret);
+ goto cleanup;
+ }
+
ret = ath11k_wow_enable(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
@@ -610,6 +661,12 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw)
goto exit;
}
+ ret = ath11k_wow_clear_hw_filter(ar);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);
+ goto exit;
+ }
+
exit:
if (ret) {
switch (ar->state) {