Commit c417b247 authored by Carl Huang's avatar Carl Huang Committed by Kalle Valo

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: default avatarCarl Huang <quic_cjhuang@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/1644308006-22784-4-git-send-email-quic_cjhuang@quicinc.com
parent fec4b898
...@@ -8165,6 +8165,39 @@ void ath11k_wmi_detach(struct ath11k_base *ab) ...@@ -8165,6 +8165,39 @@ void ath11k_wmi_detach(struct ath11k_base *ab)
ath11k_wmi_free_dbring_caps(ab); ath11k_wmi_free_dbring_caps(ab);
} }
int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
u32 filter_bitmap, bool enable)
{
struct wmi_hw_data_filter_cmd *cmd;
struct sk_buff *skb;
int len;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
if (!skb)
return -ENOMEM;
cmd = (struct wmi_hw_data_filter_cmd *)skb->data;
cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_HW_DATA_FILTER_CMD) |
FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
cmd->vdev_id = vdev_id;
cmd->enable = enable;
/* Set all modes in case of disable */
if (cmd->enable)
cmd->hw_filter_bitmap = filter_bitmap;
else
cmd->hw_filter_bitmap = ((u32)~0U);
ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
"wmi hw data filter enable %d filter_bitmap 0x%x\n",
enable, filter_bitmap);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
}
int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar) int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
{ {
struct wmi_wow_host_wakeup_ind *cmd; struct wmi_wow_host_wakeup_ind *cmd;
......
...@@ -5390,6 +5390,19 @@ struct ath11k_wmi_base { ...@@ -5390,6 +5390,19 @@ struct ath11k_wmi_base {
struct ath11k_targ_cap *targ_cap; struct ath11k_targ_cap *targ_cap;
}; };
/* Definition of HW data filtering */
enum hw_data_filter_type {
WMI_HW_DATA_FILTER_DROP_NON_ARP_BC = BIT(0),
WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC = BIT(1),
};
struct wmi_hw_data_filter_cmd {
u32 tlv_header;
u32 vdev_id;
u32 enable;
u32 hw_filter_bitmap;
} __packed;
/* WOW structures */ /* WOW structures */
enum wmi_wow_wakeup_event { enum wmi_wow_wakeup_event {
WOW_BMISS_EVENT = 0, WOW_BMISS_EVENT = 0,
...@@ -5953,4 +5966,6 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id, ...@@ -5953,4 +5966,6 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id, int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
enum wmi_wow_wakeup_event event, enum wmi_wow_wakeup_event event,
u32 enable); u32 enable);
int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
u32 filter_bitmap, bool enable);
#endif #endif
...@@ -514,6 +514,50 @@ static int ath11k_wow_nlo_cleanup(struct ath11k *ar) ...@@ -514,6 +514,50 @@ static int ath11k_wow_nlo_cleanup(struct ath11k *ar)
return 0; 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, int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan) struct cfg80211_wowlan *wowlan)
{ {
...@@ -542,6 +586,13 @@ int ath11k_wow_op_suspend(struct ieee80211_hw *hw, ...@@ -542,6 +586,13 @@ int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
goto cleanup; 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); ret = ath11k_wow_enable(ar->ab);
if (ret) { if (ret) {
ath11k_warn(ar->ab, "failed to start wow: %d\n", 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) ...@@ -610,6 +661,12 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw)
goto exit; 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: exit:
if (ret) { if (ret) {
switch (ar->state) { switch (ar->state) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment