]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: ath12k: implement hardware data filter
authorBaochen Qiang <quic_bqiang@quicinc.com>
Wed, 19 Jun 2024 14:21:12 +0000 (17:21 +0300)
committerKalle Valo <quic_kvalo@quicinc.com>
Mon, 24 Jun 2024 16:31:47 +0000 (19:31 +0300)
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: Baochen Qiang <quic_bqiang@quicinc.com>
Acked-by: Jeff Johnson <quic_jjohnson@quicinc.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://patch.msgid.link/20240604055407.12506-6-quic_bqiang@quicinc.com
drivers/net/wireless/ath/ath12k/wmi.c
drivers/net/wireless/ath/ath12k/wmi.h
drivers/net/wireless/ath/ath12k/wow.c

index b69f20495ab4b5fe7bc23f6c67499b951e1baf4f..b1f343efa4b0d7d575ab82f3895a13b352c0e17f 100644 (file)
@@ -7424,6 +7424,37 @@ void ath12k_wmi_detach(struct ath12k_base *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)
 {
        struct wmi_wow_host_wakeup_cmd *cmd;
index 0842ab760f2b258461607eeddbc22b4687774351..62d7bb9e791d91e857d7c3d2f394964724894849 100644 (file)
@@ -5294,6 +5294,25 @@ struct wmi_wow_nlo_config_cmd {
         */
 } __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,
                             struct ath12k_wmi_resource_config_arg *config);
 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,
                                    u32 enable);
 int ath12k_wmi_wow_config_pno(struct ath12k *ar, u32 vdev_id,
                              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
index 5d4f11028403f62810874e8912eecae7edb3538b..146281244f70084c7fc1a7a5576b6c03101b21dc 100644 (file)
@@ -546,6 +546,59 @@ static int ath12k_wow_nlo_cleanup(struct ath12k *ar)
        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,
                          struct cfg80211_wowlan *wowlan)
 {
@@ -575,6 +628,13 @@ int ath12k_wow_op_suspend(struct ieee80211_hw *hw,
                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);
        if (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)
                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:
        if (ret) {
                switch (ah->state) {