18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2013 Qualcomm Atheros, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "ath9k.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = {
208c2ecf20Sopenharmony_ci	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
218c2ecf20Sopenharmony_ci	.n_patterns = MAX_NUM_USER_PATTERN,
228c2ecf20Sopenharmony_ci	.pattern_min_len = 1,
238c2ecf20Sopenharmony_ci	.pattern_max_len = MAX_PATTERN_SIZE,
248c2ecf20Sopenharmony_ci};
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic const struct wiphy_wowlan_support ath9k_wowlan_support = {
278c2ecf20Sopenharmony_ci	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
288c2ecf20Sopenharmony_ci	.n_patterns = MAX_NUM_PATTERN - 2,
298c2ecf20Sopenharmony_ci	.pattern_min_len = 1,
308c2ecf20Sopenharmony_ci	.pattern_max_len = MAX_PATTERN_SIZE,
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic u8 ath9k_wow_map_triggers(struct ath_softc *sc,
348c2ecf20Sopenharmony_ci				 struct cfg80211_wowlan *wowlan)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	u8 wow_triggers = 0;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (wowlan->disconnect)
398c2ecf20Sopenharmony_ci		wow_triggers |= AH_WOW_LINK_CHANGE |
408c2ecf20Sopenharmony_ci				AH_WOW_BEACON_MISS;
418c2ecf20Sopenharmony_ci	if (wowlan->magic_pkt)
428c2ecf20Sopenharmony_ci		wow_triggers |= AH_WOW_MAGIC_PATTERN_EN;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (wowlan->n_patterns)
458c2ecf20Sopenharmony_ci		wow_triggers |= AH_WOW_USER_PATTERN_EN;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	return wow_triggers;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
538c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
548c2ecf20Sopenharmony_ci	int pattern_count = 0;
558c2ecf20Sopenharmony_ci	int ret, i, byte_cnt = 0;
568c2ecf20Sopenharmony_ci	u8 dis_deauth_pattern[MAX_PATTERN_SIZE];
578c2ecf20Sopenharmony_ci	u8 dis_deauth_mask[MAX_PATTERN_SIZE];
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	memset(dis_deauth_pattern, 0, MAX_PATTERN_SIZE);
608c2ecf20Sopenharmony_ci	memset(dis_deauth_mask, 0, MAX_PATTERN_SIZE);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/*
638c2ecf20Sopenharmony_ci	 * Create Dissassociate / Deauthenticate packet filter
648c2ecf20Sopenharmony_ci	 *
658c2ecf20Sopenharmony_ci	 *     2 bytes        2 byte    6 bytes   6 bytes  6 bytes
668c2ecf20Sopenharmony_ci	 *  +--------------+----------+---------+--------+--------+----
678c2ecf20Sopenharmony_ci	 *  + Frame Control+ Duration +   DA    +  SA    +  BSSID +
688c2ecf20Sopenharmony_ci	 *  +--------------+----------+---------+--------+--------+----
698c2ecf20Sopenharmony_ci	 *
708c2ecf20Sopenharmony_ci	 * The above is the management frame format for disassociate/
718c2ecf20Sopenharmony_ci	 * deauthenticate pattern, from this we need to match the first byte
728c2ecf20Sopenharmony_ci	 * of 'Frame Control' and DA, SA, and BSSID fields
738c2ecf20Sopenharmony_ci	 * (skipping 2nd byte of FC and Duration feild.
748c2ecf20Sopenharmony_ci	 *
758c2ecf20Sopenharmony_ci	 * Disassociate pattern
768c2ecf20Sopenharmony_ci	 * --------------------
778c2ecf20Sopenharmony_ci	 * Frame control = 00 00 1010
788c2ecf20Sopenharmony_ci	 * DA, SA, BSSID = x:x:x:x:x:x
798c2ecf20Sopenharmony_ci	 * Pattern will be A0000000 | x:x:x:x:x:x | x:x:x:x:x:x
808c2ecf20Sopenharmony_ci	 *			    | x:x:x:x:x:x  -- 22 bytes
818c2ecf20Sopenharmony_ci	 *
828c2ecf20Sopenharmony_ci	 * Deauthenticate pattern
838c2ecf20Sopenharmony_ci	 * ----------------------
848c2ecf20Sopenharmony_ci	 * Frame control = 00 00 1100
858c2ecf20Sopenharmony_ci	 * DA, SA, BSSID = x:x:x:x:x:x
868c2ecf20Sopenharmony_ci	 * Pattern will be C0000000 | x:x:x:x:x:x | x:x:x:x:x:x
878c2ecf20Sopenharmony_ci	 *			    | x:x:x:x:x:x  -- 22 bytes
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Fill out the mask with all FF's */
918c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++)
928c2ecf20Sopenharmony_ci		dis_deauth_mask[i] = 0xff;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* copy the first byte of frame control field */
958c2ecf20Sopenharmony_ci	dis_deauth_pattern[byte_cnt] = 0xa0;
968c2ecf20Sopenharmony_ci	byte_cnt++;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/* skip 2nd byte of frame control and Duration field */
998c2ecf20Sopenharmony_ci	byte_cnt += 3;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/*
1028c2ecf20Sopenharmony_ci	 * need not match the destination mac address, it can be a broadcast
1038c2ecf20Sopenharmony_ci	 * mac address or an unicast to this station
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	byte_cnt += 6;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* copy the source mac address */
1088c2ecf20Sopenharmony_ci	memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	byte_cnt += 6;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* copy the bssid, its same as the source mac address */
1138c2ecf20Sopenharmony_ci	memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	/* Create Disassociate pattern mask */
1168c2ecf20Sopenharmony_ci	dis_deauth_mask[0] = 0xfe;
1178c2ecf20Sopenharmony_ci	dis_deauth_mask[1] = 0x03;
1188c2ecf20Sopenharmony_ci	dis_deauth_mask[2] = 0xc0;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
1218c2ecf20Sopenharmony_ci					 pattern_count, byte_cnt);
1228c2ecf20Sopenharmony_ci	if (ret)
1238c2ecf20Sopenharmony_ci		goto exit;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	pattern_count++;
1268c2ecf20Sopenharmony_ci	/*
1278c2ecf20Sopenharmony_ci	 * for de-authenticate pattern, only the first byte of the frame
1288c2ecf20Sopenharmony_ci	 * control field gets changed from 0xA0 to 0xC0
1298c2ecf20Sopenharmony_ci	 */
1308c2ecf20Sopenharmony_ci	dis_deauth_pattern[0] = 0xC0;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask,
1338c2ecf20Sopenharmony_ci					 pattern_count, byte_cnt);
1348c2ecf20Sopenharmony_ciexit:
1358c2ecf20Sopenharmony_ci	return ret;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int ath9k_wow_add_pattern(struct ath_softc *sc,
1398c2ecf20Sopenharmony_ci				 struct cfg80211_wowlan *wowlan)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1428c2ecf20Sopenharmony_ci	struct cfg80211_pkt_pattern *patterns = wowlan->patterns;
1438c2ecf20Sopenharmony_ci	u8 wow_pattern[MAX_PATTERN_SIZE];
1448c2ecf20Sopenharmony_ci	u8 wow_mask[MAX_PATTERN_SIZE];
1458c2ecf20Sopenharmony_ci	int mask_len, ret = 0;
1468c2ecf20Sopenharmony_ci	s8 i = 0;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	for (i = 0; i < wowlan->n_patterns; i++) {
1498c2ecf20Sopenharmony_ci		mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8);
1508c2ecf20Sopenharmony_ci		memset(wow_pattern, 0, MAX_PATTERN_SIZE);
1518c2ecf20Sopenharmony_ci		memset(wow_mask, 0, MAX_PATTERN_SIZE);
1528c2ecf20Sopenharmony_ci		memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len);
1538c2ecf20Sopenharmony_ci		memcpy(wow_mask, patterns[i].mask, mask_len);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		ret = ath9k_hw_wow_apply_pattern(ah,
1568c2ecf20Sopenharmony_ci						 wow_pattern,
1578c2ecf20Sopenharmony_ci						 wow_mask,
1588c2ecf20Sopenharmony_ci						 i + 2,
1598c2ecf20Sopenharmony_ci						 patterns[i].pattern_len);
1608c2ecf20Sopenharmony_ci		if (ret)
1618c2ecf20Sopenharmony_ci			break;
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return ret;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ciint ath9k_suspend(struct ieee80211_hw *hw,
1688c2ecf20Sopenharmony_ci		  struct cfg80211_wowlan *wowlan)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct ath_softc *sc = hw->priv;
1718c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1728c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
1738c2ecf20Sopenharmony_ci	u8 triggers;
1748c2ecf20Sopenharmony_ci	int ret = 0;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	ath9k_deinit_channel_context(sc);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	mutex_lock(&sc->mutex);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
1818c2ecf20Sopenharmony_ci		ath_err(common, "Device not present\n");
1828c2ecf20Sopenharmony_ci		ret = -ENODEV;
1838c2ecf20Sopenharmony_ci		goto fail_wow;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (WARN_ON(!wowlan)) {
1878c2ecf20Sopenharmony_ci		ath_err(common, "None of the WoW triggers enabled\n");
1888c2ecf20Sopenharmony_ci		ret = -EINVAL;
1898c2ecf20Sopenharmony_ci		goto fail_wow;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (sc->cur_chan->nvifs > 1) {
1938c2ecf20Sopenharmony_ci		ath_dbg(common, WOW, "WoW for multivif is not yet supported\n");
1948c2ecf20Sopenharmony_ci		ret = 1;
1958c2ecf20Sopenharmony_ci		goto fail_wow;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (ath9k_is_chanctx_enabled()) {
1998c2ecf20Sopenharmony_ci		if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) {
2008c2ecf20Sopenharmony_ci			ath_dbg(common, WOW,
2018c2ecf20Sopenharmony_ci				"Multi-channel WOW is not supported\n");
2028c2ecf20Sopenharmony_ci			ret = 1;
2038c2ecf20Sopenharmony_ci			goto fail_wow;
2048c2ecf20Sopenharmony_ci		}
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
2088c2ecf20Sopenharmony_ci		ath_dbg(common, WOW, "None of the STA vifs are associated\n");
2098c2ecf20Sopenharmony_ci		ret = 1;
2108c2ecf20Sopenharmony_ci		goto fail_wow;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	triggers = ath9k_wow_map_triggers(sc, wowlan);
2148c2ecf20Sopenharmony_ci	if (!triggers) {
2158c2ecf20Sopenharmony_ci		ath_dbg(common, WOW, "No valid WoW triggers\n");
2168c2ecf20Sopenharmony_ci		ret = 1;
2178c2ecf20Sopenharmony_ci		goto fail_wow;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ath_cancel_work(sc);
2218c2ecf20Sopenharmony_ci	ath_stop_ani(sc);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	ath9k_ps_wakeup(sc);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	ath9k_stop_btcoex(sc);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/*
2288c2ecf20Sopenharmony_ci	 * Enable wake up on recieving disassoc/deauth
2298c2ecf20Sopenharmony_ci	 * frame by default.
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	ret = ath9k_wow_add_disassoc_deauth_pattern(sc);
2328c2ecf20Sopenharmony_ci	if (ret) {
2338c2ecf20Sopenharmony_ci		ath_err(common,
2348c2ecf20Sopenharmony_ci			"Unable to add disassoc/deauth pattern: %d\n", ret);
2358c2ecf20Sopenharmony_ci		goto fail_wow;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (triggers & AH_WOW_USER_PATTERN_EN) {
2398c2ecf20Sopenharmony_ci		ret = ath9k_wow_add_pattern(sc, wowlan);
2408c2ecf20Sopenharmony_ci		if (ret) {
2418c2ecf20Sopenharmony_ci			ath_err(common,
2428c2ecf20Sopenharmony_ci				"Unable to add user pattern: %d\n", ret);
2438c2ecf20Sopenharmony_ci			goto fail_wow;
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	spin_lock_bh(&sc->sc_pcu_lock);
2488c2ecf20Sopenharmony_ci	/*
2498c2ecf20Sopenharmony_ci	 * To avoid false wake, we enable beacon miss interrupt only
2508c2ecf20Sopenharmony_ci	 * when we go to sleep. We save the current interrupt mask
2518c2ecf20Sopenharmony_ci	 * so we can restore it after the system wakes up
2528c2ecf20Sopenharmony_ci	 */
2538c2ecf20Sopenharmony_ci	sc->wow_intr_before_sleep = ah->imask;
2548c2ecf20Sopenharmony_ci	ah->imask &= ~ATH9K_INT_GLOBAL;
2558c2ecf20Sopenharmony_ci	ath9k_hw_disable_interrupts(ah);
2568c2ecf20Sopenharmony_ci	ah->imask = ATH9K_INT_BMISS | ATH9K_INT_GLOBAL;
2578c2ecf20Sopenharmony_ci	ath9k_hw_set_interrupts(ah);
2588c2ecf20Sopenharmony_ci	ath9k_hw_enable_interrupts(ah);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	spin_unlock_bh(&sc->sc_pcu_lock);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	/*
2638c2ecf20Sopenharmony_ci	 * we can now sync irq and kill any running tasklets, since we already
2648c2ecf20Sopenharmony_ci	 * disabled interrupts and not holding a spin lock
2658c2ecf20Sopenharmony_ci	 */
2668c2ecf20Sopenharmony_ci	synchronize_irq(sc->irq);
2678c2ecf20Sopenharmony_ci	tasklet_kill(&sc->intr_tq);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	ath9k_hw_wow_enable(ah, triggers);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	ath9k_ps_restore(sc);
2728c2ecf20Sopenharmony_ci	ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	set_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
2758c2ecf20Sopenharmony_cifail_wow:
2768c2ecf20Sopenharmony_ci	mutex_unlock(&sc->mutex);
2778c2ecf20Sopenharmony_ci	return ret;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ciint ath9k_resume(struct ieee80211_hw *hw)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	struct ath_softc *sc = hw->priv;
2838c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
2848c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
2858c2ecf20Sopenharmony_ci	u8 status;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	mutex_lock(&sc->mutex);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	ath9k_ps_wakeup(sc);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	spin_lock_bh(&sc->sc_pcu_lock);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	ath9k_hw_disable_interrupts(ah);
2948c2ecf20Sopenharmony_ci	ah->imask = sc->wow_intr_before_sleep;
2958c2ecf20Sopenharmony_ci	ath9k_hw_set_interrupts(ah);
2968c2ecf20Sopenharmony_ci	ath9k_hw_enable_interrupts(ah);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	spin_unlock_bh(&sc->sc_pcu_lock);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	status = ath9k_hw_wow_wakeup(ah);
3018c2ecf20Sopenharmony_ci	ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	ath_restart_work(sc);
3048c2ecf20Sopenharmony_ci	ath9k_start_btcoex(sc);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	ath9k_ps_restore(sc);
3098c2ecf20Sopenharmony_ci	mutex_unlock(&sc->mutex);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return 0;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_civoid ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct ath_softc *sc = hw->priv;
3178c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	mutex_lock(&sc->mutex);
3208c2ecf20Sopenharmony_ci	device_set_wakeup_enable(sc->dev, enabled);
3218c2ecf20Sopenharmony_ci	mutex_unlock(&sc->mutex);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	ath_dbg(common, WOW, "WoW wakeup source is %s\n",
3248c2ecf20Sopenharmony_ci		(enabled) ? "enabled" : "disabled");
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_civoid ath9k_init_wow(struct ieee80211_hw *hw)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct ath_softc *sc = hw->priv;
3308c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) {
3338c2ecf20Sopenharmony_ci		if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah))
3348c2ecf20Sopenharmony_ci			hw->wiphy->wowlan = &ath9k_wowlan_support;
3358c2ecf20Sopenharmony_ci		else
3368c2ecf20Sopenharmony_ci			hw->wiphy->wowlan = &ath9k_wowlan_support_legacy;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci		device_init_wakeup(sc->dev, 1);
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_civoid ath9k_deinit_wow(struct ieee80211_hw *hw)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	struct ath_softc *sc = hw->priv;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow)
3478c2ecf20Sopenharmony_ci		device_init_wakeup(sc->dev, 0);
3488c2ecf20Sopenharmony_ci}
349