xref: /linux/drivers/net/wireless/ath/ath11k/wow.c (revision 605194411d7379645ed44e447c4b804a0b476109)
179802b13SCarl Huang // SPDX-License-Identifier: BSD-3-Clause-Clear
279802b13SCarl Huang /*
379802b13SCarl Huang  * Copyright (c) 2020 The Linux Foundation. All rights reserved.
479802b13SCarl Huang  */
579802b13SCarl Huang 
679802b13SCarl Huang #include <linux/delay.h>
779802b13SCarl Huang 
879802b13SCarl Huang #include "mac.h"
9ba9177fcSCarl Huang 
10ba9177fcSCarl Huang #include <net/mac80211.h>
1179802b13SCarl Huang #include "core.h"
1279802b13SCarl Huang #include "hif.h"
1379802b13SCarl Huang #include "debug.h"
1479802b13SCarl Huang #include "wmi.h"
1579802b13SCarl Huang #include "wow.h"
1690bf5c8dSCarl Huang #include "dp_rx.h"
1779802b13SCarl Huang 
18ba9177fcSCarl Huang static const struct wiphy_wowlan_support ath11k_wowlan_support = {
19ba9177fcSCarl Huang 	.flags = WIPHY_WOWLAN_DISCONNECT |
20a16d9b50SCarl Huang 		 WIPHY_WOWLAN_MAGIC_PKT |
21a16d9b50SCarl Huang 		 WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
22a16d9b50SCarl Huang 		 WIPHY_WOWLAN_GTK_REKEY_FAILURE,
23ba9177fcSCarl Huang 	.pattern_min_len = WOW_MIN_PATTERN_SIZE,
24ba9177fcSCarl Huang 	.pattern_max_len = WOW_MAX_PATTERN_SIZE,
25ba9177fcSCarl Huang 	.max_pkt_offset = WOW_MAX_PKT_OFFSET,
26ba9177fcSCarl Huang };
27ba9177fcSCarl Huang 
2879802b13SCarl Huang int ath11k_wow_enable(struct ath11k_base *ab)
2979802b13SCarl Huang {
3079802b13SCarl Huang 	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
3179802b13SCarl Huang 	int i, ret;
3279802b13SCarl Huang 
3379802b13SCarl Huang 	clear_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags);
3479802b13SCarl Huang 
3579802b13SCarl Huang 	for (i = 0; i < ATH11K_WOW_RETRY_NUM; i++) {
3679802b13SCarl Huang 		reinit_completion(&ab->htc_suspend);
3779802b13SCarl Huang 
3879802b13SCarl Huang 		ret = ath11k_wmi_wow_enable(ar);
3979802b13SCarl Huang 		if (ret) {
4079802b13SCarl Huang 			ath11k_warn(ab, "failed to issue wow enable: %d\n", ret);
4179802b13SCarl Huang 			return ret;
4279802b13SCarl Huang 		}
4379802b13SCarl Huang 
4479802b13SCarl Huang 		ret = wait_for_completion_timeout(&ab->htc_suspend, 3 * HZ);
4579802b13SCarl Huang 		if (ret == 0) {
4679802b13SCarl Huang 			ath11k_warn(ab,
4779802b13SCarl Huang 				    "timed out while waiting for htc suspend completion\n");
4879802b13SCarl Huang 			return -ETIMEDOUT;
4979802b13SCarl Huang 		}
5079802b13SCarl Huang 
5179802b13SCarl Huang 		if (test_bit(ATH11K_FLAG_HTC_SUSPEND_COMPLETE, &ab->dev_flags))
5279802b13SCarl Huang 			/* success, suspend complete received */
5379802b13SCarl Huang 			return 0;
5479802b13SCarl Huang 
5579802b13SCarl Huang 		ath11k_warn(ab, "htc suspend not complete, retrying (try %d)\n",
5679802b13SCarl Huang 			    i);
5779802b13SCarl Huang 		msleep(ATH11K_WOW_RETRY_WAIT_MS);
5879802b13SCarl Huang 	}
5979802b13SCarl Huang 
6079802b13SCarl Huang 	ath11k_warn(ab, "htc suspend not complete, failing after %d tries\n", i);
6179802b13SCarl Huang 
6279802b13SCarl Huang 	return -ETIMEDOUT;
6379802b13SCarl Huang }
6479802b13SCarl Huang 
6579802b13SCarl Huang int ath11k_wow_wakeup(struct ath11k_base *ab)
6679802b13SCarl Huang {
6779802b13SCarl Huang 	struct ath11k *ar = ath11k_ab_to_ar(ab, 0);
6879802b13SCarl Huang 	int ret;
6979802b13SCarl Huang 
7079802b13SCarl Huang 	reinit_completion(&ab->wow.wakeup_completed);
7179802b13SCarl Huang 
7279802b13SCarl Huang 	ret = ath11k_wmi_wow_host_wakeup_ind(ar);
7379802b13SCarl Huang 	if (ret) {
7479802b13SCarl Huang 		ath11k_warn(ab, "failed to send wow wakeup indication: %d\n",
7579802b13SCarl Huang 			    ret);
7679802b13SCarl Huang 		return ret;
7779802b13SCarl Huang 	}
7879802b13SCarl Huang 
7979802b13SCarl Huang 	ret = wait_for_completion_timeout(&ab->wow.wakeup_completed, 3 * HZ);
8079802b13SCarl Huang 	if (ret == 0) {
8179802b13SCarl Huang 		ath11k_warn(ab, "timed out while waiting for wow wakeup completion\n");
8279802b13SCarl Huang 		return -ETIMEDOUT;
8379802b13SCarl Huang 	}
8479802b13SCarl Huang 
8579802b13SCarl Huang 	return 0;
8679802b13SCarl Huang }
87ba9177fcSCarl Huang 
88ba9177fcSCarl Huang static int ath11k_wow_vif_cleanup(struct ath11k_vif *arvif)
89ba9177fcSCarl Huang {
90ba9177fcSCarl Huang 	struct ath11k *ar = arvif->ar;
91ba9177fcSCarl Huang 	int i, ret;
92ba9177fcSCarl Huang 
93ba9177fcSCarl Huang 	for (i = 0; i < WOW_EVENT_MAX; i++) {
94ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 0);
95ba9177fcSCarl Huang 		if (ret) {
96ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to issue wow wakeup for event %s on vdev %i: %d\n",
97ba9177fcSCarl Huang 				    wow_wakeup_event(i), arvif->vdev_id, ret);
98ba9177fcSCarl Huang 			return ret;
99ba9177fcSCarl Huang 		}
100ba9177fcSCarl Huang 	}
101ba9177fcSCarl Huang 
102ba9177fcSCarl Huang 	for (i = 0; i < ar->wow.max_num_patterns; i++) {
103ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_del_pattern(ar, arvif->vdev_id, i);
104ba9177fcSCarl Huang 		if (ret) {
105ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to delete wow pattern %d for vdev %i: %d\n",
106ba9177fcSCarl Huang 				    i, arvif->vdev_id, ret);
107ba9177fcSCarl Huang 			return ret;
108ba9177fcSCarl Huang 		}
109ba9177fcSCarl Huang 	}
110ba9177fcSCarl Huang 
111ba9177fcSCarl Huang 	return 0;
112ba9177fcSCarl Huang }
113ba9177fcSCarl Huang 
114ba9177fcSCarl Huang static int ath11k_wow_cleanup(struct ath11k *ar)
115ba9177fcSCarl Huang {
116ba9177fcSCarl Huang 	struct ath11k_vif *arvif;
117ba9177fcSCarl Huang 	int ret;
118ba9177fcSCarl Huang 
119ba9177fcSCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
120ba9177fcSCarl Huang 
121ba9177fcSCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
122ba9177fcSCarl Huang 		ret = ath11k_wow_vif_cleanup(arvif);
123ba9177fcSCarl Huang 		if (ret) {
124ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to clean wow wakeups on vdev %i: %d\n",
125ba9177fcSCarl Huang 				    arvif->vdev_id, ret);
126ba9177fcSCarl Huang 			return ret;
127ba9177fcSCarl Huang 		}
128ba9177fcSCarl Huang 	}
129ba9177fcSCarl Huang 
130ba9177fcSCarl Huang 	return 0;
131ba9177fcSCarl Huang }
132ba9177fcSCarl Huang 
133ba9177fcSCarl Huang /* Convert a 802.3 format to a 802.11 format.
134ba9177fcSCarl Huang  *         +------------+-----------+--------+----------------+
135ba9177fcSCarl Huang  * 802.3:  |dest mac(6B)|src mac(6B)|type(2B)|     body...    |
136ba9177fcSCarl Huang  *         +------------+-----------+--------+----------------+
137ba9177fcSCarl Huang  *                |__         |_______    |____________  |________
138ba9177fcSCarl Huang  *                   |                |                |          |
139ba9177fcSCarl Huang  *         +--+------------+----+-----------+---------------+-----------+
140ba9177fcSCarl Huang  * 802.11: |4B|dest mac(6B)| 6B |src mac(6B)|  8B  |type(2B)|  body...  |
141ba9177fcSCarl Huang  *         +--+------------+----+-----------+---------------+-----------+
142ba9177fcSCarl Huang  */
143ba9177fcSCarl Huang static void ath11k_wow_convert_8023_to_80211(struct cfg80211_pkt_pattern *new,
144ba9177fcSCarl Huang 					     const struct cfg80211_pkt_pattern *old)
145ba9177fcSCarl Huang {
146ba9177fcSCarl Huang 	u8 hdr_8023_pattern[ETH_HLEN] = {};
147ba9177fcSCarl Huang 	u8 hdr_8023_bit_mask[ETH_HLEN] = {};
148ba9177fcSCarl Huang 	u8 hdr_80211_pattern[WOW_HDR_LEN] = {};
149ba9177fcSCarl Huang 	u8 hdr_80211_bit_mask[WOW_HDR_LEN] = {};
150ba9177fcSCarl Huang 
151ba9177fcSCarl Huang 	int total_len = old->pkt_offset + old->pattern_len;
152ba9177fcSCarl Huang 	int hdr_80211_end_offset;
153ba9177fcSCarl Huang 
154ba9177fcSCarl Huang 	struct ieee80211_hdr_3addr *new_hdr_pattern =
155ba9177fcSCarl Huang 		(struct ieee80211_hdr_3addr *)hdr_80211_pattern;
156ba9177fcSCarl Huang 	struct ieee80211_hdr_3addr *new_hdr_mask =
157ba9177fcSCarl Huang 		(struct ieee80211_hdr_3addr *)hdr_80211_bit_mask;
158ba9177fcSCarl Huang 	struct ethhdr *old_hdr_pattern = (struct ethhdr *)hdr_8023_pattern;
159ba9177fcSCarl Huang 	struct ethhdr *old_hdr_mask = (struct ethhdr *)hdr_8023_bit_mask;
160ba9177fcSCarl Huang 	int hdr_len = sizeof(*new_hdr_pattern);
161ba9177fcSCarl Huang 
162ba9177fcSCarl Huang 	struct rfc1042_hdr *new_rfc_pattern =
163ba9177fcSCarl Huang 		(struct rfc1042_hdr *)(hdr_80211_pattern + hdr_len);
164ba9177fcSCarl Huang 	struct rfc1042_hdr *new_rfc_mask =
165ba9177fcSCarl Huang 		(struct rfc1042_hdr *)(hdr_80211_bit_mask + hdr_len);
166ba9177fcSCarl Huang 	int rfc_len = sizeof(*new_rfc_pattern);
167ba9177fcSCarl Huang 
168ba9177fcSCarl Huang 	memcpy(hdr_8023_pattern + old->pkt_offset,
169ba9177fcSCarl Huang 	       old->pattern, ETH_HLEN - old->pkt_offset);
170ba9177fcSCarl Huang 	memcpy(hdr_8023_bit_mask + old->pkt_offset,
171ba9177fcSCarl Huang 	       old->mask, ETH_HLEN - old->pkt_offset);
172ba9177fcSCarl Huang 
173ba9177fcSCarl Huang 	/* Copy destination address */
174ba9177fcSCarl Huang 	memcpy(new_hdr_pattern->addr1, old_hdr_pattern->h_dest, ETH_ALEN);
175ba9177fcSCarl Huang 	memcpy(new_hdr_mask->addr1, old_hdr_mask->h_dest, ETH_ALEN);
176ba9177fcSCarl Huang 
177ba9177fcSCarl Huang 	/* Copy source address */
178ba9177fcSCarl Huang 	memcpy(new_hdr_pattern->addr3, old_hdr_pattern->h_source, ETH_ALEN);
179ba9177fcSCarl Huang 	memcpy(new_hdr_mask->addr3, old_hdr_mask->h_source, ETH_ALEN);
180ba9177fcSCarl Huang 
181ba9177fcSCarl Huang 	/* Copy logic link type */
182ba9177fcSCarl Huang 	memcpy(&new_rfc_pattern->snap_type,
183ba9177fcSCarl Huang 	       &old_hdr_pattern->h_proto,
184ba9177fcSCarl Huang 	       sizeof(old_hdr_pattern->h_proto));
185ba9177fcSCarl Huang 	memcpy(&new_rfc_mask->snap_type,
186ba9177fcSCarl Huang 	       &old_hdr_mask->h_proto,
187ba9177fcSCarl Huang 	       sizeof(old_hdr_mask->h_proto));
188ba9177fcSCarl Huang 
189ba9177fcSCarl Huang 	/* Compute new pkt_offset */
190ba9177fcSCarl Huang 	if (old->pkt_offset < ETH_ALEN)
191ba9177fcSCarl Huang 		new->pkt_offset = old->pkt_offset +
192ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr1);
193ba9177fcSCarl Huang 	else if (old->pkt_offset < offsetof(struct ethhdr, h_proto))
194ba9177fcSCarl Huang 		new->pkt_offset = old->pkt_offset +
195ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr3) -
196ba9177fcSCarl Huang 			offsetof(struct ethhdr, h_source);
197ba9177fcSCarl Huang 	else
198ba9177fcSCarl Huang 		new->pkt_offset = old->pkt_offset + hdr_len + rfc_len - ETH_HLEN;
199ba9177fcSCarl Huang 
200ba9177fcSCarl Huang 	/* Compute new hdr end offset */
201ba9177fcSCarl Huang 	if (total_len > ETH_HLEN)
202ba9177fcSCarl Huang 		hdr_80211_end_offset = hdr_len + rfc_len;
203ba9177fcSCarl Huang 	else if (total_len > offsetof(struct ethhdr, h_proto))
204ba9177fcSCarl Huang 		hdr_80211_end_offset = hdr_len + rfc_len + total_len - ETH_HLEN;
205ba9177fcSCarl Huang 	else if (total_len > ETH_ALEN)
206ba9177fcSCarl Huang 		hdr_80211_end_offset = total_len - ETH_ALEN +
207ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr3);
208ba9177fcSCarl Huang 	else
209ba9177fcSCarl Huang 		hdr_80211_end_offset = total_len +
210ba9177fcSCarl Huang 			offsetof(struct ieee80211_hdr_3addr, addr1);
211ba9177fcSCarl Huang 
212ba9177fcSCarl Huang 	new->pattern_len = hdr_80211_end_offset - new->pkt_offset;
213ba9177fcSCarl Huang 
214ba9177fcSCarl Huang 	memcpy((u8 *)new->pattern,
215ba9177fcSCarl Huang 	       hdr_80211_pattern + new->pkt_offset,
216ba9177fcSCarl Huang 	       new->pattern_len);
217ba9177fcSCarl Huang 	memcpy((u8 *)new->mask,
218ba9177fcSCarl Huang 	       hdr_80211_bit_mask + new->pkt_offset,
219ba9177fcSCarl Huang 	       new->pattern_len);
220ba9177fcSCarl Huang 
221ba9177fcSCarl Huang 	if (total_len > ETH_HLEN) {
222ba9177fcSCarl Huang 		/* Copy frame body */
223ba9177fcSCarl Huang 		memcpy((u8 *)new->pattern + new->pattern_len,
224ba9177fcSCarl Huang 		       (void *)old->pattern + ETH_HLEN - old->pkt_offset,
225ba9177fcSCarl Huang 		       total_len - ETH_HLEN);
226ba9177fcSCarl Huang 		memcpy((u8 *)new->mask + new->pattern_len,
227ba9177fcSCarl Huang 		       (void *)old->mask + ETH_HLEN - old->pkt_offset,
228ba9177fcSCarl Huang 		       total_len - ETH_HLEN);
229ba9177fcSCarl Huang 
230ba9177fcSCarl Huang 		new->pattern_len += total_len - ETH_HLEN;
231ba9177fcSCarl Huang 	}
232ba9177fcSCarl Huang }
233ba9177fcSCarl Huang 
234fec4b898SCarl Huang static int ath11k_wmi_pno_check_and_convert(struct ath11k *ar, u32 vdev_id,
235fec4b898SCarl Huang 					    struct cfg80211_sched_scan_request *nd_config,
236fec4b898SCarl Huang 					    struct wmi_pno_scan_req *pno)
237fec4b898SCarl Huang {
238fec4b898SCarl Huang 	int i, j;
239fec4b898SCarl Huang 	u8 ssid_len;
240fec4b898SCarl Huang 
241fec4b898SCarl Huang 	pno->enable = 1;
242fec4b898SCarl Huang 	pno->vdev_id = vdev_id;
243fec4b898SCarl Huang 	pno->uc_networks_count = nd_config->n_match_sets;
244fec4b898SCarl Huang 
245fec4b898SCarl Huang 	if (!pno->uc_networks_count ||
246fec4b898SCarl Huang 	    pno->uc_networks_count > WMI_PNO_MAX_SUPP_NETWORKS)
247fec4b898SCarl Huang 		return -EINVAL;
248fec4b898SCarl Huang 
249fec4b898SCarl Huang 	if (nd_config->n_channels > WMI_PNO_MAX_NETW_CHANNELS_EX)
250fec4b898SCarl Huang 		return -EINVAL;
251fec4b898SCarl Huang 
252fec4b898SCarl Huang 	/* Filling per profile params */
253fec4b898SCarl Huang 	for (i = 0; i < pno->uc_networks_count; i++) {
254fec4b898SCarl Huang 		ssid_len = nd_config->match_sets[i].ssid.ssid_len;
255fec4b898SCarl Huang 
256fec4b898SCarl Huang 		if (ssid_len == 0 || ssid_len > 32)
257fec4b898SCarl Huang 			return -EINVAL;
258fec4b898SCarl Huang 
259fec4b898SCarl Huang 		pno->a_networks[i].ssid.ssid_len = ssid_len;
260fec4b898SCarl Huang 
261fec4b898SCarl Huang 		memcpy(pno->a_networks[i].ssid.ssid,
262fec4b898SCarl Huang 		       nd_config->match_sets[i].ssid.ssid,
263fec4b898SCarl Huang 		       nd_config->match_sets[i].ssid.ssid_len);
264fec4b898SCarl Huang 		pno->a_networks[i].authentication = 0;
265fec4b898SCarl Huang 		pno->a_networks[i].encryption     = 0;
266fec4b898SCarl Huang 		pno->a_networks[i].bcast_nw_type  = 0;
267fec4b898SCarl Huang 
268fec4b898SCarl Huang 		/* Copying list of valid channel into request */
269fec4b898SCarl Huang 		pno->a_networks[i].channel_count = nd_config->n_channels;
270fec4b898SCarl Huang 		pno->a_networks[i].rssi_threshold = nd_config->match_sets[i].rssi_thold;
271fec4b898SCarl Huang 
272fec4b898SCarl Huang 		for (j = 0; j < nd_config->n_channels; j++) {
273fec4b898SCarl Huang 			pno->a_networks[i].channels[j] =
274fec4b898SCarl Huang 					nd_config->channels[j]->center_freq;
275fec4b898SCarl Huang 		}
276fec4b898SCarl Huang 	}
277fec4b898SCarl Huang 
278fec4b898SCarl Huang 	/* set scan to passive if no SSIDs are specified in the request */
279fec4b898SCarl Huang 	if (nd_config->n_ssids == 0)
280fec4b898SCarl Huang 		pno->do_passive_scan = true;
281fec4b898SCarl Huang 	else
282fec4b898SCarl Huang 		pno->do_passive_scan = false;
283fec4b898SCarl Huang 
284fec4b898SCarl Huang 	for (i = 0; i < nd_config->n_ssids; i++) {
285fec4b898SCarl Huang 		j = 0;
286fec4b898SCarl Huang 		while (j < pno->uc_networks_count) {
287fec4b898SCarl Huang 			if (pno->a_networks[j].ssid.ssid_len ==
288fec4b898SCarl Huang 				nd_config->ssids[i].ssid_len &&
289fec4b898SCarl Huang 			(memcmp(pno->a_networks[j].ssid.ssid,
290fec4b898SCarl Huang 				nd_config->ssids[i].ssid,
291fec4b898SCarl Huang 				pno->a_networks[j].ssid.ssid_len) == 0)) {
292fec4b898SCarl Huang 				pno->a_networks[j].bcast_nw_type = BCAST_HIDDEN;
293fec4b898SCarl Huang 				break;
294fec4b898SCarl Huang 			}
295fec4b898SCarl Huang 			j++;
296fec4b898SCarl Huang 		}
297fec4b898SCarl Huang 	}
298fec4b898SCarl Huang 
299fec4b898SCarl Huang 	if (nd_config->n_scan_plans == 2) {
300fec4b898SCarl Huang 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
301fec4b898SCarl Huang 		pno->fast_scan_max_cycles = nd_config->scan_plans[0].iterations;
302fec4b898SCarl Huang 		pno->slow_scan_period =
303fec4b898SCarl Huang 			nd_config->scan_plans[1].interval * MSEC_PER_SEC;
304fec4b898SCarl Huang 	} else if (nd_config->n_scan_plans == 1) {
305fec4b898SCarl Huang 		pno->fast_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
306fec4b898SCarl Huang 		pno->fast_scan_max_cycles = 1;
307fec4b898SCarl Huang 		pno->slow_scan_period = nd_config->scan_plans[0].interval * MSEC_PER_SEC;
308fec4b898SCarl Huang 	} else {
309fec4b898SCarl Huang 		ath11k_warn(ar->ab, "Invalid number of scan plans %d !!",
310fec4b898SCarl Huang 			    nd_config->n_scan_plans);
311fec4b898SCarl Huang 	}
312fec4b898SCarl Huang 
313fec4b898SCarl Huang 	if (nd_config->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
314fec4b898SCarl Huang 		/* enable mac randomization */
315fec4b898SCarl Huang 		pno->enable_pno_scan_randomization = 1;
316fec4b898SCarl Huang 		memcpy(pno->mac_addr, nd_config->mac_addr, ETH_ALEN);
317fec4b898SCarl Huang 		memcpy(pno->mac_addr_mask, nd_config->mac_addr_mask, ETH_ALEN);
318fec4b898SCarl Huang 	}
319fec4b898SCarl Huang 
320fec4b898SCarl Huang 	pno->delay_start_time = nd_config->delay;
321fec4b898SCarl Huang 
322fec4b898SCarl Huang 	/* Current FW does not support min-max range for dwell time */
323fec4b898SCarl Huang 	pno->active_max_time = WMI_ACTIVE_MAX_CHANNEL_TIME;
324fec4b898SCarl Huang 	pno->passive_max_time = WMI_PASSIVE_MAX_CHANNEL_TIME;
325fec4b898SCarl Huang 
326fec4b898SCarl Huang 	return 0;
327fec4b898SCarl Huang }
328fec4b898SCarl Huang 
329ba9177fcSCarl Huang static int ath11k_vif_wow_set_wakeups(struct ath11k_vif *arvif,
330ba9177fcSCarl Huang 				      struct cfg80211_wowlan *wowlan)
331ba9177fcSCarl Huang {
332ba9177fcSCarl Huang 	int ret, i;
333ba9177fcSCarl Huang 	unsigned long wow_mask = 0;
334ba9177fcSCarl Huang 	struct ath11k *ar = arvif->ar;
335ba9177fcSCarl Huang 	const struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
336ba9177fcSCarl Huang 	int pattern_id = 0;
337ba9177fcSCarl Huang 
338ba9177fcSCarl Huang 	/* Setup requested WOW features */
339ba9177fcSCarl Huang 	switch (arvif->vdev_type) {
340ba9177fcSCarl Huang 	case WMI_VDEV_TYPE_IBSS:
341ba9177fcSCarl Huang 		__set_bit(WOW_BEACON_EVENT, &wow_mask);
342ba9177fcSCarl Huang 		fallthrough;
343ba9177fcSCarl Huang 	case WMI_VDEV_TYPE_AP:
344ba9177fcSCarl Huang 		__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
345ba9177fcSCarl Huang 		__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
346ba9177fcSCarl Huang 		__set_bit(WOW_PROBE_REQ_WPS_IE_EVENT, &wow_mask);
347ba9177fcSCarl Huang 		__set_bit(WOW_AUTH_REQ_EVENT, &wow_mask);
348ba9177fcSCarl Huang 		__set_bit(WOW_ASSOC_REQ_EVENT, &wow_mask);
349ba9177fcSCarl Huang 		__set_bit(WOW_HTT_EVENT, &wow_mask);
350ba9177fcSCarl Huang 		__set_bit(WOW_RA_MATCH_EVENT, &wow_mask);
351ba9177fcSCarl Huang 		break;
352ba9177fcSCarl Huang 	case WMI_VDEV_TYPE_STA:
353ba9177fcSCarl Huang 		if (wowlan->disconnect) {
354ba9177fcSCarl Huang 			__set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask);
355ba9177fcSCarl Huang 			__set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask);
356ba9177fcSCarl Huang 			__set_bit(WOW_BMISS_EVENT, &wow_mask);
357ba9177fcSCarl Huang 			__set_bit(WOW_CSA_IE_EVENT, &wow_mask);
358ba9177fcSCarl Huang 		}
359ba9177fcSCarl Huang 
360ba9177fcSCarl Huang 		if (wowlan->magic_pkt)
361ba9177fcSCarl Huang 			__set_bit(WOW_MAGIC_PKT_RECVD_EVENT, &wow_mask);
362fec4b898SCarl Huang 
363fec4b898SCarl Huang 		if (wowlan->nd_config) {
364fec4b898SCarl Huang 			struct wmi_pno_scan_req *pno;
365fec4b898SCarl Huang 			int ret;
366fec4b898SCarl Huang 
367fec4b898SCarl Huang 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
368fec4b898SCarl Huang 			if (!pno)
369fec4b898SCarl Huang 				return -ENOMEM;
370fec4b898SCarl Huang 
371fec4b898SCarl Huang 			ar->nlo_enabled = true;
372fec4b898SCarl Huang 
373fec4b898SCarl Huang 			ret = ath11k_wmi_pno_check_and_convert(ar, arvif->vdev_id,
374fec4b898SCarl Huang 							       wowlan->nd_config, pno);
375fec4b898SCarl Huang 			if (!ret) {
376fec4b898SCarl Huang 				ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
377fec4b898SCarl Huang 				__set_bit(WOW_NLO_DETECTED_EVENT, &wow_mask);
378fec4b898SCarl Huang 			}
379fec4b898SCarl Huang 
380fec4b898SCarl Huang 			kfree(pno);
381fec4b898SCarl Huang 		}
382ba9177fcSCarl Huang 		break;
383ba9177fcSCarl Huang 	default:
384ba9177fcSCarl Huang 		break;
385ba9177fcSCarl Huang 	}
386ba9177fcSCarl Huang 
387ba9177fcSCarl Huang 	for (i = 0; i < wowlan->n_patterns; i++) {
388ba9177fcSCarl Huang 		u8 bitmask[WOW_MAX_PATTERN_SIZE] = {};
389ba9177fcSCarl Huang 		u8 ath_pattern[WOW_MAX_PATTERN_SIZE] = {};
390ba9177fcSCarl Huang 		u8 ath_bitmask[WOW_MAX_PATTERN_SIZE] = {};
391ba9177fcSCarl Huang 		struct cfg80211_pkt_pattern new_pattern = {};
392ba9177fcSCarl Huang 		struct cfg80211_pkt_pattern old_pattern = patterns[i];
393ba9177fcSCarl Huang 		int j;
394ba9177fcSCarl Huang 
395ba9177fcSCarl Huang 		new_pattern.pattern = ath_pattern;
396ba9177fcSCarl Huang 		new_pattern.mask = ath_bitmask;
397ba9177fcSCarl Huang 		if (patterns[i].pattern_len > WOW_MAX_PATTERN_SIZE)
398ba9177fcSCarl Huang 			continue;
399ba9177fcSCarl Huang 		/* convert bytemask to bitmask */
400ba9177fcSCarl Huang 		for (j = 0; j < patterns[i].pattern_len; j++)
401ba9177fcSCarl Huang 			if (patterns[i].mask[j / 8] & BIT(j % 8))
402ba9177fcSCarl Huang 				bitmask[j] = 0xff;
403ba9177fcSCarl Huang 		old_pattern.mask = bitmask;
404ba9177fcSCarl Huang 
405ba9177fcSCarl Huang 		if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
406ba9177fcSCarl Huang 		    ATH11K_HW_TXRX_NATIVE_WIFI) {
407ba9177fcSCarl Huang 			if (patterns[i].pkt_offset < ETH_HLEN) {
408ba9177fcSCarl Huang 				u8 pattern_ext[WOW_MAX_PATTERN_SIZE] = {};
409ba9177fcSCarl Huang 
410ba9177fcSCarl Huang 				memcpy(pattern_ext, old_pattern.pattern,
411ba9177fcSCarl Huang 				       old_pattern.pattern_len);
412ba9177fcSCarl Huang 				old_pattern.pattern = pattern_ext;
413ba9177fcSCarl Huang 				ath11k_wow_convert_8023_to_80211(&new_pattern,
414ba9177fcSCarl Huang 								 &old_pattern);
415ba9177fcSCarl Huang 			} else {
416ba9177fcSCarl Huang 				new_pattern = old_pattern;
417ba9177fcSCarl Huang 				new_pattern.pkt_offset += WOW_HDR_LEN - ETH_HLEN;
418ba9177fcSCarl Huang 			}
419ba9177fcSCarl Huang 		}
420ba9177fcSCarl Huang 
421ba9177fcSCarl Huang 		if (WARN_ON(new_pattern.pattern_len > WOW_MAX_PATTERN_SIZE))
422ba9177fcSCarl Huang 			return -EINVAL;
423ba9177fcSCarl Huang 
424ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_add_pattern(ar, arvif->vdev_id,
425ba9177fcSCarl Huang 						 pattern_id,
426ba9177fcSCarl Huang 						 new_pattern.pattern,
427ba9177fcSCarl Huang 						 new_pattern.mask,
428ba9177fcSCarl Huang 						 new_pattern.pattern_len,
429ba9177fcSCarl Huang 						 new_pattern.pkt_offset);
430ba9177fcSCarl Huang 		if (ret) {
431ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to add pattern %i to vdev %i: %d\n",
432ba9177fcSCarl Huang 				    pattern_id,
433ba9177fcSCarl Huang 				    arvif->vdev_id, ret);
434ba9177fcSCarl Huang 			return ret;
435ba9177fcSCarl Huang 		}
436ba9177fcSCarl Huang 
437ba9177fcSCarl Huang 		pattern_id++;
438ba9177fcSCarl Huang 		__set_bit(WOW_PATTERN_MATCH_EVENT, &wow_mask);
439ba9177fcSCarl Huang 	}
440ba9177fcSCarl Huang 
441ba9177fcSCarl Huang 	for (i = 0; i < WOW_EVENT_MAX; i++) {
442ba9177fcSCarl Huang 		if (!test_bit(i, &wow_mask))
443ba9177fcSCarl Huang 			continue;
444ba9177fcSCarl Huang 		ret = ath11k_wmi_wow_add_wakeup_event(ar, arvif->vdev_id, i, 1);
445ba9177fcSCarl Huang 		if (ret) {
446ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to enable wakeup event %s on vdev %i: %d\n",
447ba9177fcSCarl Huang 				    wow_wakeup_event(i), arvif->vdev_id, ret);
448ba9177fcSCarl Huang 			return ret;
449ba9177fcSCarl Huang 		}
450ba9177fcSCarl Huang 	}
451ba9177fcSCarl Huang 
452ba9177fcSCarl Huang 	return 0;
453ba9177fcSCarl Huang }
454ba9177fcSCarl Huang 
455ba9177fcSCarl Huang static int ath11k_wow_set_wakeups(struct ath11k *ar,
456ba9177fcSCarl Huang 				  struct cfg80211_wowlan *wowlan)
457ba9177fcSCarl Huang {
458ba9177fcSCarl Huang 	struct ath11k_vif *arvif;
459ba9177fcSCarl Huang 	int ret;
460ba9177fcSCarl Huang 
461ba9177fcSCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
462ba9177fcSCarl Huang 
463ba9177fcSCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
464ba9177fcSCarl Huang 		ret = ath11k_vif_wow_set_wakeups(arvif, wowlan);
465ba9177fcSCarl Huang 		if (ret) {
466ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "failed to set wow wakeups on vdev %i: %d\n",
467ba9177fcSCarl Huang 				    arvif->vdev_id, ret);
468ba9177fcSCarl Huang 			return ret;
469ba9177fcSCarl Huang 		}
470ba9177fcSCarl Huang 	}
471ba9177fcSCarl Huang 
472ba9177fcSCarl Huang 	return 0;
473ba9177fcSCarl Huang }
474ba9177fcSCarl Huang 
475fec4b898SCarl Huang static int ath11k_vif_wow_clean_nlo(struct ath11k_vif *arvif)
476fec4b898SCarl Huang {
477fec4b898SCarl Huang 	int ret = 0;
478fec4b898SCarl Huang 	struct ath11k *ar = arvif->ar;
479fec4b898SCarl Huang 
480fec4b898SCarl Huang 	switch (arvif->vdev_type) {
481fec4b898SCarl Huang 	case WMI_VDEV_TYPE_STA:
482fec4b898SCarl Huang 		if (ar->nlo_enabled) {
483fec4b898SCarl Huang 			struct wmi_pno_scan_req *pno;
484fec4b898SCarl Huang 
485fec4b898SCarl Huang 			pno = kzalloc(sizeof(*pno), GFP_KERNEL);
486fec4b898SCarl Huang 			if (!pno)
487fec4b898SCarl Huang 				return -ENOMEM;
488fec4b898SCarl Huang 
489fec4b898SCarl Huang 			pno->enable = 0;
490fec4b898SCarl Huang 			ar->nlo_enabled = false;
491fec4b898SCarl Huang 			ret = ath11k_wmi_wow_config_pno(ar, arvif->vdev_id, pno);
492fec4b898SCarl Huang 			kfree(pno);
493fec4b898SCarl Huang 		}
494fec4b898SCarl Huang 		break;
495fec4b898SCarl Huang 	default:
496fec4b898SCarl Huang 		break;
497fec4b898SCarl Huang 	}
498fec4b898SCarl Huang 	return ret;
499fec4b898SCarl Huang }
500fec4b898SCarl Huang 
501fec4b898SCarl Huang static int ath11k_wow_nlo_cleanup(struct ath11k *ar)
502fec4b898SCarl Huang {
503fec4b898SCarl Huang 	struct ath11k_vif *arvif;
504fec4b898SCarl Huang 	int ret;
505fec4b898SCarl Huang 
506fec4b898SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
507fec4b898SCarl Huang 
508fec4b898SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
509fec4b898SCarl Huang 		ret = ath11k_vif_wow_clean_nlo(arvif);
510fec4b898SCarl Huang 		if (ret) {
511fec4b898SCarl Huang 			ath11k_warn(ar->ab, "failed to clean nlo settings on vdev %i: %d\n",
512fec4b898SCarl Huang 				    arvif->vdev_id, ret);
513fec4b898SCarl Huang 			return ret;
514fec4b898SCarl Huang 		}
515fec4b898SCarl Huang 	}
516fec4b898SCarl Huang 
517fec4b898SCarl Huang 	return 0;
518fec4b898SCarl Huang }
519fec4b898SCarl Huang 
520c417b247SCarl Huang static int ath11k_wow_set_hw_filter(struct ath11k *ar)
521c417b247SCarl Huang {
522c417b247SCarl Huang 	struct ath11k_vif *arvif;
523c417b247SCarl Huang 	u32 bitmap;
524c417b247SCarl Huang 	int ret;
525c417b247SCarl Huang 
526c417b247SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
527c417b247SCarl Huang 
528c417b247SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
529c417b247SCarl Huang 		bitmap = WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC |
530c417b247SCarl Huang 			WMI_HW_DATA_FILTER_DROP_NON_ARP_BC;
531c417b247SCarl Huang 		ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id,
532c417b247SCarl Huang 						    bitmap,
533c417b247SCarl Huang 						    true);
534c417b247SCarl Huang 		if (ret) {
535c417b247SCarl Huang 			ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n",
536c417b247SCarl Huang 				    arvif->vdev_id, ret);
537c417b247SCarl Huang 			return ret;
538c417b247SCarl Huang 		}
539c417b247SCarl Huang 	}
540c417b247SCarl Huang 
541c417b247SCarl Huang 	return 0;
542c417b247SCarl Huang }
543c417b247SCarl Huang 
544c417b247SCarl Huang static int ath11k_wow_clear_hw_filter(struct ath11k *ar)
545c417b247SCarl Huang {
546c417b247SCarl Huang 	struct ath11k_vif *arvif;
547c417b247SCarl Huang 	int ret;
548c417b247SCarl Huang 
549c417b247SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
550c417b247SCarl Huang 
551c417b247SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
552c417b247SCarl Huang 		ret = ath11k_wmi_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false);
553c417b247SCarl Huang 
554c417b247SCarl Huang 		if (ret) {
555c417b247SCarl Huang 			ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n",
556c417b247SCarl Huang 				    arvif->vdev_id, ret);
557c417b247SCarl Huang 			return ret;
558c417b247SCarl Huang 		}
559c417b247SCarl Huang 	}
560c417b247SCarl Huang 
561c417b247SCarl Huang 	return 0;
562c417b247SCarl Huang }
563c417b247SCarl Huang 
564c3c36bfeSCarl Huang static int ath11k_wow_arp_ns_offload(struct ath11k *ar, bool enable)
565c3c36bfeSCarl Huang {
566c3c36bfeSCarl Huang 	struct ath11k_vif *arvif;
567c3c36bfeSCarl Huang 	int ret;
568c3c36bfeSCarl Huang 
569c3c36bfeSCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
570c3c36bfeSCarl Huang 
571c3c36bfeSCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
572c3c36bfeSCarl Huang 		if (arvif->vdev_type != WMI_VDEV_TYPE_STA)
573c3c36bfeSCarl Huang 			continue;
574c3c36bfeSCarl Huang 
575c3c36bfeSCarl Huang 		ret = ath11k_wmi_arp_ns_offload(ar, arvif, enable);
576c3c36bfeSCarl Huang 
577c3c36bfeSCarl Huang 		if (ret) {
578c3c36bfeSCarl Huang 			ath11k_warn(ar->ab, "failed to set arp ns offload vdev %i: enable %d, ret %d\n",
579c3c36bfeSCarl Huang 				    arvif->vdev_id, enable, ret);
580c3c36bfeSCarl Huang 			return ret;
581c3c36bfeSCarl Huang 		}
582c3c36bfeSCarl Huang 	}
583c3c36bfeSCarl Huang 
584c3c36bfeSCarl Huang 	return 0;
585c3c36bfeSCarl Huang }
586c3c36bfeSCarl Huang 
587a16d9b50SCarl Huang static int ath11k_gtk_rekey_offload(struct ath11k *ar, bool enable)
588a16d9b50SCarl Huang {
589a16d9b50SCarl Huang 	struct ath11k_vif *arvif;
590a16d9b50SCarl Huang 	int ret;
591a16d9b50SCarl Huang 
592a16d9b50SCarl Huang 	lockdep_assert_held(&ar->conf_mutex);
593a16d9b50SCarl Huang 
594a16d9b50SCarl Huang 	list_for_each_entry(arvif, &ar->arvifs, list) {
595a16d9b50SCarl Huang 		if (arvif->vdev_type != WMI_VDEV_TYPE_STA ||
596a16d9b50SCarl Huang 		    !arvif->is_up ||
597a16d9b50SCarl Huang 		    !arvif->rekey_data.enable_offload)
598a16d9b50SCarl Huang 			continue;
599a16d9b50SCarl Huang 
600a16d9b50SCarl Huang 		/* get rekey info before disable rekey offload */
601a16d9b50SCarl Huang 		if (!enable) {
602a16d9b50SCarl Huang 			ret = ath11k_wmi_gtk_rekey_getinfo(ar, arvif);
603a16d9b50SCarl Huang 			if (ret) {
604a16d9b50SCarl Huang 				ath11k_warn(ar->ab, "failed to request rekey info vdev %i, ret %d\n",
605a16d9b50SCarl Huang 					    arvif->vdev_id, ret);
606a16d9b50SCarl Huang 				return ret;
607a16d9b50SCarl Huang 			}
608a16d9b50SCarl Huang 		}
609a16d9b50SCarl Huang 
610a16d9b50SCarl Huang 		ret = ath11k_wmi_gtk_rekey_offload(ar, arvif, enable);
611a16d9b50SCarl Huang 
612a16d9b50SCarl Huang 		if (ret) {
613a16d9b50SCarl Huang 			ath11k_warn(ar->ab, "failed to offload gtk reky vdev %i: enable %d, ret %d\n",
614a16d9b50SCarl Huang 				    arvif->vdev_id, enable, ret);
615a16d9b50SCarl Huang 			return ret;
616a16d9b50SCarl Huang 		}
617a16d9b50SCarl Huang 	}
618a16d9b50SCarl Huang 
619a16d9b50SCarl Huang 	return 0;
620a16d9b50SCarl Huang }
621a16d9b50SCarl Huang 
622c3c36bfeSCarl Huang static int ath11k_wow_protocol_offload(struct ath11k *ar, bool enable)
623c3c36bfeSCarl Huang {
624c3c36bfeSCarl Huang 	int ret;
625c3c36bfeSCarl Huang 
626c3c36bfeSCarl Huang 	ret = ath11k_wow_arp_ns_offload(ar, enable);
627c3c36bfeSCarl Huang 	if (ret) {
628c3c36bfeSCarl Huang 		ath11k_warn(ar->ab, "failed to offload ARP and NS %d %d\n",
629c3c36bfeSCarl Huang 			    enable, ret);
630c3c36bfeSCarl Huang 		return ret;
631c3c36bfeSCarl Huang 	}
632c3c36bfeSCarl Huang 
633a16d9b50SCarl Huang 	ret = ath11k_gtk_rekey_offload(ar, enable);
634a16d9b50SCarl Huang 	if (ret) {
635a16d9b50SCarl Huang 		ath11k_warn(ar->ab, "failed to offload gtk rekey %d %d\n",
636a16d9b50SCarl Huang 			    enable, ret);
637a16d9b50SCarl Huang 		return ret;
638a16d9b50SCarl Huang 	}
639a16d9b50SCarl Huang 
640c3c36bfeSCarl Huang 	return 0;
641c3c36bfeSCarl Huang }
642c3c36bfeSCarl Huang 
643ba9177fcSCarl Huang int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
644ba9177fcSCarl Huang 			  struct cfg80211_wowlan *wowlan)
645ba9177fcSCarl Huang {
646ba9177fcSCarl Huang 	struct ath11k *ar = hw->priv;
647ba9177fcSCarl Huang 	int ret;
648ba9177fcSCarl Huang 
649ba9177fcSCarl Huang 	mutex_lock(&ar->conf_mutex);
650ba9177fcSCarl Huang 
65190bf5c8dSCarl Huang 	ret = ath11k_dp_rx_pktlog_stop(ar->ab, true);
65290bf5c8dSCarl Huang 	if (ret) {
65390bf5c8dSCarl Huang 		ath11k_warn(ar->ab,
65490bf5c8dSCarl Huang 			    "failed to stop dp rx (and timer) pktlog during wow suspend: %d\n",
65590bf5c8dSCarl Huang 			    ret);
65690bf5c8dSCarl Huang 		goto exit;
65790bf5c8dSCarl Huang 	}
65890bf5c8dSCarl Huang 
659ba9177fcSCarl Huang 	ret =  ath11k_wow_cleanup(ar);
660ba9177fcSCarl Huang 	if (ret) {
661ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to clear wow wakeup events: %d\n",
662ba9177fcSCarl Huang 			    ret);
663ba9177fcSCarl Huang 		goto exit;
664ba9177fcSCarl Huang 	}
665ba9177fcSCarl Huang 
666ba9177fcSCarl Huang 	ret = ath11k_wow_set_wakeups(ar, wowlan);
667ba9177fcSCarl Huang 	if (ret) {
668ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to set wow wakeup events: %d\n",
669ba9177fcSCarl Huang 			    ret);
670ba9177fcSCarl Huang 		goto cleanup;
671ba9177fcSCarl Huang 	}
672ba9177fcSCarl Huang 
673c3c36bfeSCarl Huang 	ret = ath11k_wow_protocol_offload(ar, true);
674c3c36bfeSCarl Huang 	if (ret) {
675c3c36bfeSCarl Huang 		ath11k_warn(ar->ab, "failed to set wow protocol offload events: %d\n",
676c3c36bfeSCarl Huang 			    ret);
677c3c36bfeSCarl Huang 		goto cleanup;
678c3c36bfeSCarl Huang 	}
679c3c36bfeSCarl Huang 
680c3c36bfeSCarl Huang 	ath11k_mac_drain_tx(ar);
681ba9177fcSCarl Huang 	ret = ath11k_mac_wait_tx_complete(ar);
682ba9177fcSCarl Huang 	if (ret) {
683ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to wait tx complete: %d\n", ret);
684ba9177fcSCarl Huang 		goto cleanup;
685ba9177fcSCarl Huang 	}
686ba9177fcSCarl Huang 
687c417b247SCarl Huang 	ret = ath11k_wow_set_hw_filter(ar);
688c417b247SCarl Huang 	if (ret) {
689c417b247SCarl Huang 		ath11k_warn(ar->ab, "failed to set hw filter: %d\n",
690c417b247SCarl Huang 			    ret);
691c417b247SCarl Huang 		goto cleanup;
692c417b247SCarl Huang 	}
693c417b247SCarl Huang 
694ba9177fcSCarl Huang 	ret = ath11k_wow_enable(ar->ab);
695ba9177fcSCarl Huang 	if (ret) {
696ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
697ba9177fcSCarl Huang 		goto cleanup;
698ba9177fcSCarl Huang 	}
699ba9177fcSCarl Huang 
70090bf5c8dSCarl Huang 	ret = ath11k_dp_rx_pktlog_stop(ar->ab, false);
70190bf5c8dSCarl Huang 	if (ret) {
70290bf5c8dSCarl Huang 		ath11k_warn(ar->ab,
70390bf5c8dSCarl Huang 			    "failed to stop dp rx pktlog during wow suspend: %d\n",
70490bf5c8dSCarl Huang 			    ret);
70590bf5c8dSCarl Huang 		goto cleanup;
70690bf5c8dSCarl Huang 	}
70790bf5c8dSCarl Huang 
708ba9177fcSCarl Huang 	ath11k_ce_stop_shadow_timers(ar->ab);
709ba9177fcSCarl Huang 	ath11k_dp_stop_shadow_timers(ar->ab);
710ba9177fcSCarl Huang 
711ba9177fcSCarl Huang 	ath11k_hif_irq_disable(ar->ab);
712ba9177fcSCarl Huang 	ath11k_hif_ce_irq_disable(ar->ab);
713ba9177fcSCarl Huang 
714ba9177fcSCarl Huang 	ret = ath11k_hif_suspend(ar->ab);
715ba9177fcSCarl Huang 	if (ret) {
716ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to suspend hif: %d\n", ret);
717ba9177fcSCarl Huang 		goto wakeup;
718ba9177fcSCarl Huang 	}
719ba9177fcSCarl Huang 
720ba9177fcSCarl Huang 	goto exit;
721ba9177fcSCarl Huang 
722ba9177fcSCarl Huang wakeup:
723ba9177fcSCarl Huang 	ath11k_wow_wakeup(ar->ab);
724ba9177fcSCarl Huang 
725ba9177fcSCarl Huang cleanup:
726ba9177fcSCarl Huang 	ath11k_wow_cleanup(ar);
727ba9177fcSCarl Huang 
728ba9177fcSCarl Huang exit:
729ba9177fcSCarl Huang 	mutex_unlock(&ar->conf_mutex);
730ba9177fcSCarl Huang 	return ret ? 1 : 0;
731ba9177fcSCarl Huang }
732ba9177fcSCarl Huang 
733ba9177fcSCarl Huang void ath11k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled)
734ba9177fcSCarl Huang {
735ba9177fcSCarl Huang 	struct ath11k *ar = hw->priv;
736ba9177fcSCarl Huang 
737ba9177fcSCarl Huang 	mutex_lock(&ar->conf_mutex);
738ba9177fcSCarl Huang 	device_set_wakeup_enable(ar->ab->dev, enabled);
739ba9177fcSCarl Huang 	mutex_unlock(&ar->conf_mutex);
740ba9177fcSCarl Huang }
741ba9177fcSCarl Huang 
742ba9177fcSCarl Huang int ath11k_wow_op_resume(struct ieee80211_hw *hw)
743ba9177fcSCarl Huang {
744ba9177fcSCarl Huang 	struct ath11k *ar = hw->priv;
745ba9177fcSCarl Huang 	int ret;
746ba9177fcSCarl Huang 
747ba9177fcSCarl Huang 	mutex_lock(&ar->conf_mutex);
748ba9177fcSCarl Huang 
749ba9177fcSCarl Huang 	ret = ath11k_hif_resume(ar->ab);
750ba9177fcSCarl Huang 	if (ret) {
751ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to resume hif: %d\n", ret);
752ba9177fcSCarl Huang 		goto exit;
753ba9177fcSCarl Huang 	}
754ba9177fcSCarl Huang 
755ba9177fcSCarl Huang 	ath11k_hif_ce_irq_enable(ar->ab);
756ba9177fcSCarl Huang 	ath11k_hif_irq_enable(ar->ab);
757ba9177fcSCarl Huang 
75890bf5c8dSCarl Huang 	ret = ath11k_dp_rx_pktlog_start(ar->ab);
75990bf5c8dSCarl Huang 	if (ret) {
76090bf5c8dSCarl Huang 		ath11k_warn(ar->ab, "failed to start rx pktlog from wow: %d\n", ret);
761*60519441SYang Yingliang 		goto exit;
76290bf5c8dSCarl Huang 	}
76390bf5c8dSCarl Huang 
764ba9177fcSCarl Huang 	ret = ath11k_wow_wakeup(ar->ab);
765fec4b898SCarl Huang 	if (ret) {
766ba9177fcSCarl Huang 		ath11k_warn(ar->ab, "failed to wakeup from wow: %d\n", ret);
767fec4b898SCarl Huang 		goto exit;
768fec4b898SCarl Huang 	}
769fec4b898SCarl Huang 
770fec4b898SCarl Huang 	ret = ath11k_wow_nlo_cleanup(ar);
771fec4b898SCarl Huang 	if (ret) {
772fec4b898SCarl Huang 		ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret);
773fec4b898SCarl Huang 		goto exit;
774fec4b898SCarl Huang 	}
775ba9177fcSCarl Huang 
776c417b247SCarl Huang 	ret = ath11k_wow_clear_hw_filter(ar);
777c417b247SCarl Huang 	if (ret) {
778c417b247SCarl Huang 		ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);
779c417b247SCarl Huang 		goto exit;
780c417b247SCarl Huang 	}
781c417b247SCarl Huang 
782c3c36bfeSCarl Huang 	ret = ath11k_wow_protocol_offload(ar, false);
783c3c36bfeSCarl Huang 	if (ret) {
784c3c36bfeSCarl Huang 		ath11k_warn(ar->ab, "failed to clear wow protocol offload events: %d\n",
785c3c36bfeSCarl Huang 			    ret);
786c3c36bfeSCarl Huang 		goto exit;
787c3c36bfeSCarl Huang 	}
788c3c36bfeSCarl Huang 
789ba9177fcSCarl Huang exit:
790ba9177fcSCarl Huang 	if (ret) {
791ba9177fcSCarl Huang 		switch (ar->state) {
792ba9177fcSCarl Huang 		case ATH11K_STATE_ON:
793ba9177fcSCarl Huang 			ar->state = ATH11K_STATE_RESTARTING;
794ba9177fcSCarl Huang 			ret = 1;
795ba9177fcSCarl Huang 			break;
796ba9177fcSCarl Huang 		case ATH11K_STATE_OFF:
797ba9177fcSCarl Huang 		case ATH11K_STATE_RESTARTING:
798ba9177fcSCarl Huang 		case ATH11K_STATE_RESTARTED:
799ba9177fcSCarl Huang 		case ATH11K_STATE_WEDGED:
800ba9177fcSCarl Huang 			ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n",
801ba9177fcSCarl Huang 				    ar->state);
802ba9177fcSCarl Huang 			ret = -EIO;
803ba9177fcSCarl Huang 			break;
804ba9177fcSCarl Huang 		}
805ba9177fcSCarl Huang 	}
806ba9177fcSCarl Huang 
807ba9177fcSCarl Huang 	mutex_unlock(&ar->conf_mutex);
808ba9177fcSCarl Huang 	return ret;
809ba9177fcSCarl Huang }
810ba9177fcSCarl Huang 
811ba9177fcSCarl Huang int ath11k_wow_init(struct ath11k *ar)
812ba9177fcSCarl Huang {
813633469e3SNagarajan Maran 	if (!test_bit(WMI_TLV_SERVICE_WOW, ar->wmi->wmi_ab->svc_map))
814633469e3SNagarajan Maran 		return 0;
815ba9177fcSCarl Huang 
816ba9177fcSCarl Huang 	ar->wow.wowlan_support = ath11k_wowlan_support;
817ba9177fcSCarl Huang 
818ba9177fcSCarl Huang 	if (ar->wmi->wmi_ab->wlan_resource_config.rx_decap_mode ==
819ba9177fcSCarl Huang 	    ATH11K_HW_TXRX_NATIVE_WIFI) {
820ba9177fcSCarl Huang 		ar->wow.wowlan_support.pattern_max_len -= WOW_MAX_REDUCE;
821ba9177fcSCarl Huang 		ar->wow.wowlan_support.max_pkt_offset -= WOW_MAX_REDUCE;
822ba9177fcSCarl Huang 	}
823ba9177fcSCarl Huang 
824fec4b898SCarl Huang 	if (test_bit(WMI_TLV_SERVICE_NLO, ar->wmi->wmi_ab->svc_map)) {
825fec4b898SCarl Huang 		ar->wow.wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
826fec4b898SCarl Huang 		ar->wow.wowlan_support.max_nd_match_sets = WMI_PNO_MAX_SUPP_NETWORKS;
827fec4b898SCarl Huang 	}
828fec4b898SCarl Huang 
829ba9177fcSCarl Huang 	ar->wow.max_num_patterns = ATH11K_WOW_PATTERNS;
830ba9177fcSCarl Huang 	ar->wow.wowlan_support.n_patterns = ar->wow.max_num_patterns;
831ba9177fcSCarl Huang 	ar->hw->wiphy->wowlan = &ar->wow.wowlan_support;
832ba9177fcSCarl Huang 
833ba9177fcSCarl Huang 	device_set_wakeup_capable(ar->ab->dev, true);
834ba9177fcSCarl Huang 
835ba9177fcSCarl Huang 	return 0;
836ba9177fcSCarl Huang }
837