Commit 66a9448b authored by Baochen Qiang's avatar Baochen Qiang Committed by Kalle Valo

wifi: ath12k: implement hardware data filter

Host needs to set hardware data filter before entering WoW to
let firmware drop needless broadcast/multicast frames to avoid
frequent wakeup. Host clears hardware data filter when leaving WoW.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4
Signed-off-by: default avatarBaochen Qiang <quic_bqiang@quicinc.com>
Acked-by: default avatarJeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: default avatarKalle Valo <quic_kvalo@quicinc.com>
Link: https://patch.msgid.link/20240604055407.12506-6-quic_bqiang@quicinc.com
parent 16f474d6
...@@ -7424,6 +7424,37 @@ void ath12k_wmi_detach(struct ath12k_base *ab) ...@@ -7424,6 +7424,37 @@ void ath12k_wmi_detach(struct ath12k_base *ab)
ath12k_wmi_free_dbring_caps(ab); ath12k_wmi_free_dbring_caps(ab);
} }
int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar, struct wmi_hw_data_filter_arg *arg)
{
struct wmi_hw_data_filter_cmd *cmd;
struct sk_buff *skb;
int len;
len = sizeof(*cmd);
skb = ath12k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
if (!skb)
return -ENOMEM;
cmd = (struct wmi_hw_data_filter_cmd *)skb->data;
cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HW_DATA_FILTER_CMD,
sizeof(*cmd));
cmd->vdev_id = cpu_to_le32(arg->vdev_id);
cmd->enable = cpu_to_le32(arg->enable ? 1 : 0);
/* Set all modes in case of disable */
if (arg->enable)
cmd->hw_filter_bitmap = cpu_to_le32(arg->hw_filter_bitmap);
else
cmd->hw_filter_bitmap = cpu_to_le32((u32)~0U);
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
"wmi hw data filter enable %d filter_bitmap 0x%x\n",
arg->enable, arg->hw_filter_bitmap);
return ath12k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
}
int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar) int ath12k_wmi_wow_host_wakeup_ind(struct ath12k *ar)
{ {
struct wmi_wow_host_wakeup_cmd *cmd; struct wmi_wow_host_wakeup_cmd *cmd;
......
...@@ -5294,6 +5294,25 @@ struct wmi_wow_nlo_config_cmd { ...@@ -5294,6 +5294,25 @@ struct wmi_wow_nlo_config_cmd {
*/ */
} __packed; } __packed;
/* 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 {
__le32 tlv_header;
__le32 vdev_id;
__le32 enable;
__le32 hw_filter_bitmap;
} __packed;
struct wmi_hw_data_filter_arg {
u32 vdev_id;
bool enable;
u32 hw_filter_bitmap;
};
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
struct ath12k_wmi_resource_config_arg *config); struct ath12k_wmi_resource_config_arg *config);
void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, void ath12k_wmi_init_wcn7850(struct ath12k_base *ab,
...@@ -5457,4 +5476,6 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id, ...@@ -5457,4 +5476,6 @@ int ath12k_wmi_wow_add_wakeup_event(struct ath12k *ar, u32 vdev_id,
u32 enable); u32 enable);
int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id, int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id,
struct wmi_pno_scan_req_arg *pno_scan); struct wmi_pno_scan_req_arg *pno_scan);
int ath12k_wmi_hw_data_filter_cmd(struct ath12k *ar,
struct wmi_hw_data_filter_arg *arg);
#endif #endif
...@@ -546,6 +546,59 @@ static int ath12k_wow_nlo_cleanup(struct ath12k *ar) ...@@ -546,6 +546,59 @@ static int ath12k_wow_nlo_cleanup(struct ath12k *ar)
return 0; return 0;
} }
static int ath12k_wow_set_hw_filter(struct ath12k *ar)
{
struct wmi_hw_data_filter_arg arg;
struct ath12k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
continue;
arg.vdev_id = arvif->vdev_id;
arg.enable = true;
arg.hw_filter_bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC;
ret = ath12k_wmi_hw_data_filter_cmd(ar, &arg);
if (ret) {
ath12k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
static int ath12k_wow_clear_hw_filter(struct ath12k *ar)
{
struct wmi_hw_data_filter_arg arg;
struct ath12k_vif *arvif;
int ret;
lockdep_assert_held(&ar->conf_mutex);
list_for_each_entry(arvif, &ar->arvifs, list) {
if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
continue;
arg.vdev_id = arvif->vdev_id;
arg.enable = false;
arg.hw_filter_bitmap = 0;
ret = ath12k_wmi_hw_data_filter_cmd(ar, &arg);
if (ret) {
ath12k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n",
arvif->vdev_id, ret);
return ret;
}
}
return 0;
}
int ath12k_wow_op_suspend(struct ieee80211_hw *hw, int ath12k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan) struct cfg80211_wowlan *wowlan)
{ {
...@@ -575,6 +628,13 @@ int ath12k_wow_op_suspend(struct ieee80211_hw *hw, ...@@ -575,6 +628,13 @@ int ath12k_wow_op_suspend(struct ieee80211_hw *hw,
goto cleanup; goto cleanup;
} }
ret = ath12k_wow_set_hw_filter(ar);
if (ret) {
ath12k_warn(ar->ab, "failed to set hw filter: %d\n",
ret);
goto cleanup;
}
ret = ath12k_wow_enable(ar); ret = ath12k_wow_enable(ar);
if (ret) { if (ret) {
ath12k_warn(ar->ab, "failed to start wow: %d\n", ret); ath12k_warn(ar->ab, "failed to start wow: %d\n", ret);
...@@ -642,6 +702,12 @@ int ath12k_wow_op_resume(struct ieee80211_hw *hw) ...@@ -642,6 +702,12 @@ int ath12k_wow_op_resume(struct ieee80211_hw *hw)
goto exit; goto exit;
} }
ret = ath12k_wow_clear_hw_filter(ar);
if (ret) {
ath12k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);
goto exit;
}
exit: exit:
if (ret) { if (ret) {
switch (ah->state) { switch (ah->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