xref: /freebsd/sys/contrib/dev/rtw88/wow.c (revision 90aac0d8)
12774f206SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
22774f206SBjoern A. Zeeb /* Copyright(c) 2018-2019  Realtek Corporation
32774f206SBjoern A. Zeeb  */
42774f206SBjoern A. Zeeb 
52774f206SBjoern A. Zeeb #include "main.h"
62774f206SBjoern A. Zeeb #include "fw.h"
72774f206SBjoern A. Zeeb #include "wow.h"
82774f206SBjoern A. Zeeb #include "reg.h"
92774f206SBjoern A. Zeeb #include "debug.h"
102774f206SBjoern A. Zeeb #include "mac.h"
112774f206SBjoern A. Zeeb #include "ps.h"
122774f206SBjoern A. Zeeb 
rtw_wow_show_wakeup_reason(struct rtw_dev * rtwdev)132774f206SBjoern A. Zeeb static void rtw_wow_show_wakeup_reason(struct rtw_dev *rtwdev)
142774f206SBjoern A. Zeeb {
152774f206SBjoern A. Zeeb 	struct cfg80211_wowlan_nd_info nd_info;
162774f206SBjoern A. Zeeb 	struct cfg80211_wowlan_wakeup wakeup = {
172774f206SBjoern A. Zeeb 		.pattern_idx = -1,
182774f206SBjoern A. Zeeb 	};
192774f206SBjoern A. Zeeb 	u8 reason;
202774f206SBjoern A. Zeeb 
212774f206SBjoern A. Zeeb 	reason = rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON);
222774f206SBjoern A. Zeeb 
232774f206SBjoern A. Zeeb 	switch (reason) {
242774f206SBjoern A. Zeeb 	case RTW_WOW_RSN_RX_DEAUTH:
252774f206SBjoern A. Zeeb 		wakeup.disconnect = true;
262774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx deauth\n");
272774f206SBjoern A. Zeeb 		break;
282774f206SBjoern A. Zeeb 	case RTW_WOW_RSN_DISCONNECT:
292774f206SBjoern A. Zeeb 		wakeup.disconnect = true;
302774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: AP is off\n");
312774f206SBjoern A. Zeeb 		break;
322774f206SBjoern A. Zeeb 	case RTW_WOW_RSN_RX_MAGIC_PKT:
332774f206SBjoern A. Zeeb 		wakeup.magic_pkt = true;
342774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx magic packet\n");
352774f206SBjoern A. Zeeb 		break;
362774f206SBjoern A. Zeeb 	case RTW_WOW_RSN_RX_GTK_REKEY:
372774f206SBjoern A. Zeeb 		wakeup.gtk_rekey_failure = true;
382774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx gtk rekey\n");
392774f206SBjoern A. Zeeb 		break;
402774f206SBjoern A. Zeeb 	case RTW_WOW_RSN_RX_PATTERN_MATCH:
412774f206SBjoern A. Zeeb 		/* Current firmware and driver don't report pattern index
422774f206SBjoern A. Zeeb 		 * Use pattern_idx to 0 defaultly.
432774f206SBjoern A. Zeeb 		 */
442774f206SBjoern A. Zeeb 		wakeup.pattern_idx = 0;
452774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx pattern match packet\n");
462774f206SBjoern A. Zeeb 		break;
472774f206SBjoern A. Zeeb 	case RTW_WOW_RSN_RX_NLO:
482774f206SBjoern A. Zeeb 		/* Current firmware and driver don't report ssid index.
492774f206SBjoern A. Zeeb 		 * Use 0 for n_matches based on its comment.
502774f206SBjoern A. Zeeb 		 */
512774f206SBjoern A. Zeeb 		nd_info.n_matches = 0;
522774f206SBjoern A. Zeeb 		wakeup.net_detect = &nd_info;
532774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_WOW, "Rx NLO\n");
542774f206SBjoern A. Zeeb 		break;
552774f206SBjoern A. Zeeb 	default:
562774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Unknown wakeup reason %x\n", reason);
572774f206SBjoern A. Zeeb 		ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, NULL,
582774f206SBjoern A. Zeeb 					       GFP_KERNEL);
592774f206SBjoern A. Zeeb 		return;
602774f206SBjoern A. Zeeb 	}
612774f206SBjoern A. Zeeb 	ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, &wakeup,
622774f206SBjoern A. Zeeb 				       GFP_KERNEL);
632774f206SBjoern A. Zeeb }
642774f206SBjoern A. Zeeb 
rtw_wow_pattern_write_cam(struct rtw_dev * rtwdev,u8 addr,u32 wdata)652774f206SBjoern A. Zeeb static void rtw_wow_pattern_write_cam(struct rtw_dev *rtwdev, u8 addr,
662774f206SBjoern A. Zeeb 				      u32 wdata)
672774f206SBjoern A. Zeeb {
682774f206SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_WKFMCAM_RWD, wdata);
692774f206SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 |
702774f206SBjoern A. Zeeb 		    BIT_WKFCAM_WE | BIT_WKFCAM_ADDR_V2(addr));
712774f206SBjoern A. Zeeb 
722774f206SBjoern A. Zeeb 	if (!check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0))
732774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to write pattern cam\n");
742774f206SBjoern A. Zeeb }
752774f206SBjoern A. Zeeb 
rtw_wow_pattern_write_cam_ent(struct rtw_dev * rtwdev,u8 id,struct rtw_wow_pattern * rtw_pattern)762774f206SBjoern A. Zeeb static void rtw_wow_pattern_write_cam_ent(struct rtw_dev *rtwdev, u8 id,
772774f206SBjoern A. Zeeb 					  struct rtw_wow_pattern *rtw_pattern)
782774f206SBjoern A. Zeeb {
792774f206SBjoern A. Zeeb 	int i;
802774f206SBjoern A. Zeeb 	u8 addr;
812774f206SBjoern A. Zeeb 	u32 wdata;
822774f206SBjoern A. Zeeb 
832774f206SBjoern A. Zeeb 	for (i = 0; i < RTW_MAX_PATTERN_MASK_SIZE / 4; i++) {
842774f206SBjoern A. Zeeb 		addr = (id << 3) + i;
852774f206SBjoern A. Zeeb 		wdata = rtw_pattern->mask[i * 4];
862774f206SBjoern A. Zeeb 		wdata |= rtw_pattern->mask[i * 4 + 1] << 8;
872774f206SBjoern A. Zeeb 		wdata |= rtw_pattern->mask[i * 4 + 2] << 16;
882774f206SBjoern A. Zeeb 		wdata |= rtw_pattern->mask[i * 4 + 3] << 24;
892774f206SBjoern A. Zeeb 		rtw_wow_pattern_write_cam(rtwdev, addr, wdata);
902774f206SBjoern A. Zeeb 	}
912774f206SBjoern A. Zeeb 
922774f206SBjoern A. Zeeb 	wdata = rtw_pattern->crc;
932774f206SBjoern A. Zeeb 	addr = (id << 3) + RTW_MAX_PATTERN_MASK_SIZE / 4;
942774f206SBjoern A. Zeeb 
952774f206SBjoern A. Zeeb 	switch (rtw_pattern->type) {
962774f206SBjoern A. Zeeb 	case RTW_PATTERN_BROADCAST:
972774f206SBjoern A. Zeeb 		wdata |= BIT_WKFMCAM_BC | BIT_WKFMCAM_VALID;
982774f206SBjoern A. Zeeb 		break;
992774f206SBjoern A. Zeeb 	case RTW_PATTERN_MULTICAST:
1002774f206SBjoern A. Zeeb 		wdata |= BIT_WKFMCAM_MC | BIT_WKFMCAM_VALID;
1012774f206SBjoern A. Zeeb 		break;
1022774f206SBjoern A. Zeeb 	case RTW_PATTERN_UNICAST:
1032774f206SBjoern A. Zeeb 		wdata |= BIT_WKFMCAM_UC | BIT_WKFMCAM_VALID;
1042774f206SBjoern A. Zeeb 		break;
1052774f206SBjoern A. Zeeb 	default:
1062774f206SBjoern A. Zeeb 		break;
1072774f206SBjoern A. Zeeb 	}
1082774f206SBjoern A. Zeeb 	rtw_wow_pattern_write_cam(rtwdev, addr, wdata);
1092774f206SBjoern A. Zeeb }
1102774f206SBjoern A. Zeeb 
1112774f206SBjoern A. Zeeb /* RTK internal CRC16 for Pattern Cam */
__rtw_cal_crc16(u8 data,u16 crc)1122774f206SBjoern A. Zeeb static u16 __rtw_cal_crc16(u8 data, u16 crc)
1132774f206SBjoern A. Zeeb {
1142774f206SBjoern A. Zeeb 	u8 shift_in, data_bit;
1152774f206SBjoern A. Zeeb 	u8 crc_bit4, crc_bit11, crc_bit15;
1162774f206SBjoern A. Zeeb 	u16 crc_result;
1172774f206SBjoern A. Zeeb 	int index;
1182774f206SBjoern A. Zeeb 
1192774f206SBjoern A. Zeeb 	for (index = 0; index < 8; index++) {
1202774f206SBjoern A. Zeeb 		crc_bit15 = ((crc & BIT(15)) ? 1 : 0);
1212774f206SBjoern A. Zeeb 		data_bit = (data & (BIT(0) << index) ? 1 : 0);
1222774f206SBjoern A. Zeeb 		shift_in = crc_bit15 ^ data_bit;
1232774f206SBjoern A. Zeeb 
1242774f206SBjoern A. Zeeb 		crc_result = crc << 1;
1252774f206SBjoern A. Zeeb 
1262774f206SBjoern A. Zeeb 		if (shift_in == 0)
1272774f206SBjoern A. Zeeb 			crc_result &= (~BIT(0));
1282774f206SBjoern A. Zeeb 		else
1292774f206SBjoern A. Zeeb 			crc_result |= BIT(0);
1302774f206SBjoern A. Zeeb 
1312774f206SBjoern A. Zeeb 		crc_bit11 = ((crc & BIT(11)) ? 1 : 0) ^ shift_in;
1322774f206SBjoern A. Zeeb 
1332774f206SBjoern A. Zeeb 		if (crc_bit11 == 0)
1342774f206SBjoern A. Zeeb 			crc_result &= (~BIT(12));
1352774f206SBjoern A. Zeeb 		else
1362774f206SBjoern A. Zeeb 			crc_result |= BIT(12);
1372774f206SBjoern A. Zeeb 
1382774f206SBjoern A. Zeeb 		crc_bit4 = ((crc & BIT(4)) ? 1 : 0) ^ shift_in;
1392774f206SBjoern A. Zeeb 
1402774f206SBjoern A. Zeeb 		if (crc_bit4 == 0)
1412774f206SBjoern A. Zeeb 			crc_result &= (~BIT(5));
1422774f206SBjoern A. Zeeb 		else
1432774f206SBjoern A. Zeeb 			crc_result |= BIT(5);
1442774f206SBjoern A. Zeeb 
1452774f206SBjoern A. Zeeb 		crc = crc_result;
1462774f206SBjoern A. Zeeb 	}
1472774f206SBjoern A. Zeeb 	return crc;
1482774f206SBjoern A. Zeeb }
1492774f206SBjoern A. Zeeb 
rtw_calc_crc(u8 * pdata,int length)1502774f206SBjoern A. Zeeb static u16 rtw_calc_crc(u8 *pdata, int length)
1512774f206SBjoern A. Zeeb {
1522774f206SBjoern A. Zeeb 	u16 crc = 0xffff;
1532774f206SBjoern A. Zeeb 	int i;
1542774f206SBjoern A. Zeeb 
1552774f206SBjoern A. Zeeb 	for (i = 0; i < length; i++)
1562774f206SBjoern A. Zeeb 		crc = __rtw_cal_crc16(pdata[i], crc);
1572774f206SBjoern A. Zeeb 
1582774f206SBjoern A. Zeeb 	/* get 1' complement */
1592774f206SBjoern A. Zeeb 	return ~crc;
1602774f206SBjoern A. Zeeb }
1612774f206SBjoern A. Zeeb 
rtw_wow_pattern_generate(struct rtw_dev * rtwdev,struct rtw_vif * rtwvif,const struct cfg80211_pkt_pattern * pkt_pattern,struct rtw_wow_pattern * rtw_pattern)1622774f206SBjoern A. Zeeb static void rtw_wow_pattern_generate(struct rtw_dev *rtwdev,
1632774f206SBjoern A. Zeeb 				     struct rtw_vif *rtwvif,
1642774f206SBjoern A. Zeeb 				     const struct cfg80211_pkt_pattern *pkt_pattern,
1652774f206SBjoern A. Zeeb 				     struct rtw_wow_pattern *rtw_pattern)
1662774f206SBjoern A. Zeeb {
1672774f206SBjoern A. Zeeb 	const u8 *mask;
1682774f206SBjoern A. Zeeb 	const u8 *pattern;
1692774f206SBjoern A. Zeeb 	u8 mask_hw[RTW_MAX_PATTERN_MASK_SIZE] = {0};
1702774f206SBjoern A. Zeeb 	u8 content[RTW_MAX_PATTERN_SIZE] = {0};
1712774f206SBjoern A. Zeeb 	u8 mac_addr[ETH_ALEN] = {0};
1722774f206SBjoern A. Zeeb 	u8 mask_len;
1732774f206SBjoern A. Zeeb 	u16 count;
1742774f206SBjoern A. Zeeb 	int len;
1752774f206SBjoern A. Zeeb 	int i;
1762774f206SBjoern A. Zeeb 
1772774f206SBjoern A. Zeeb 	pattern = pkt_pattern->pattern;
1782774f206SBjoern A. Zeeb 	len = pkt_pattern->pattern_len;
1792774f206SBjoern A. Zeeb 	mask = pkt_pattern->mask;
1802774f206SBjoern A. Zeeb 
1812774f206SBjoern A. Zeeb 	ether_addr_copy(mac_addr, rtwvif->mac_addr);
1822774f206SBjoern A. Zeeb 	memset(rtw_pattern, 0, sizeof(*rtw_pattern));
1832774f206SBjoern A. Zeeb 
1842774f206SBjoern A. Zeeb 	mask_len = DIV_ROUND_UP(len, 8);
1852774f206SBjoern A. Zeeb 
1862774f206SBjoern A. Zeeb 	if (is_broadcast_ether_addr(pattern))
1872774f206SBjoern A. Zeeb 		rtw_pattern->type = RTW_PATTERN_BROADCAST;
1882774f206SBjoern A. Zeeb 	else if (is_multicast_ether_addr(pattern))
1892774f206SBjoern A. Zeeb 		rtw_pattern->type = RTW_PATTERN_MULTICAST;
1902774f206SBjoern A. Zeeb 	else if (ether_addr_equal(pattern, mac_addr))
1912774f206SBjoern A. Zeeb 		rtw_pattern->type = RTW_PATTERN_UNICAST;
1922774f206SBjoern A. Zeeb 	else
1932774f206SBjoern A. Zeeb 		rtw_pattern->type = RTW_PATTERN_INVALID;
1942774f206SBjoern A. Zeeb 
1952774f206SBjoern A. Zeeb 	/* translate mask from os to mask for hw
1962774f206SBjoern A. Zeeb 	 * pattern from OS uses 'ethenet frame', like this:
1972774f206SBjoern A. Zeeb 	 * |    6   |    6   |   2  |     20    |  Variable  |  4  |
1982774f206SBjoern A. Zeeb 	 * |--------+--------+------+-----------+------------+-----|
1992774f206SBjoern A. Zeeb 	 * |    802.3 Mac Header    | IP Header | TCP Packet | FCS |
2002774f206SBjoern A. Zeeb 	 * |   DA   |   SA   | Type |
2012774f206SBjoern A. Zeeb 	 *
2022774f206SBjoern A. Zeeb 	 * BUT, packet catched by our HW is in '802.11 frame', begin from LLC
2032774f206SBjoern A. Zeeb 	 * |     24 or 30      |    6   |   2  |     20    |  Variable  |  4  |
2042774f206SBjoern A. Zeeb 	 * |-------------------+--------+------+-----------+------------+-----|
2052774f206SBjoern A. Zeeb 	 * | 802.11 MAC Header |       LLC     | IP Header | TCP Packet | FCS |
2062774f206SBjoern A. Zeeb 	 *		       | Others | Tpye |
2072774f206SBjoern A. Zeeb 	 *
2082774f206SBjoern A. Zeeb 	 * Therefore, we need translate mask_from_OS to mask_to_hw.
2092774f206SBjoern A. Zeeb 	 * We should left-shift mask by 6 bits, then set the new bit[0~5] = 0,
2102774f206SBjoern A. Zeeb 	 * because new mask[0~5] means 'SA', but our HW packet begins from LLC,
2112774f206SBjoern A. Zeeb 	 * bit[0~5] corresponds to first 6 Bytes in LLC, they just don't match.
2122774f206SBjoern A. Zeeb 	 */
2132774f206SBjoern A. Zeeb 
2142774f206SBjoern A. Zeeb 	/* Shift 6 bits */
2152774f206SBjoern A. Zeeb 	for (i = 0; i < mask_len - 1; i++) {
2162774f206SBjoern A. Zeeb 		mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6));
2172774f206SBjoern A. Zeeb 		mask_hw[i] |= u8_get_bits(mask[i + 1], GENMASK(5, 0)) << 2;
2182774f206SBjoern A. Zeeb 	}
2192774f206SBjoern A. Zeeb 	mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6));
2202774f206SBjoern A. Zeeb 
2212774f206SBjoern A. Zeeb 	/* Set bit 0-5 to zero */
2222774f206SBjoern A. Zeeb 	mask_hw[0] &= (~GENMASK(5, 0));
2232774f206SBjoern A. Zeeb 
2242774f206SBjoern A. Zeeb 	memcpy(rtw_pattern->mask, mask_hw, RTW_MAX_PATTERN_MASK_SIZE);
2252774f206SBjoern A. Zeeb 
2262774f206SBjoern A. Zeeb 	/* To get the wake up pattern from the mask.
2272774f206SBjoern A. Zeeb 	 * We do not count first 12 bits which means
2282774f206SBjoern A. Zeeb 	 * DA[6] and SA[6] in the pattern to match HW design.
2292774f206SBjoern A. Zeeb 	 */
2302774f206SBjoern A. Zeeb 	count = 0;
2312774f206SBjoern A. Zeeb 	for (i = 12; i < len; i++) {
2322774f206SBjoern A. Zeeb 		if ((mask[i / 8] >> (i % 8)) & 0x01) {
2332774f206SBjoern A. Zeeb 			content[count] = pattern[i];
2342774f206SBjoern A. Zeeb 			count++;
2352774f206SBjoern A. Zeeb 		}
2362774f206SBjoern A. Zeeb 	}
2372774f206SBjoern A. Zeeb 
2382774f206SBjoern A. Zeeb 	rtw_pattern->crc = rtw_calc_crc(content, count);
2392774f206SBjoern A. Zeeb }
2402774f206SBjoern A. Zeeb 
rtw_wow_pattern_clear_cam(struct rtw_dev * rtwdev)2412774f206SBjoern A. Zeeb static void rtw_wow_pattern_clear_cam(struct rtw_dev *rtwdev)
2422774f206SBjoern A. Zeeb {
2432774f206SBjoern A. Zeeb 	bool ret;
2442774f206SBjoern A. Zeeb 
2452774f206SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 |
2462774f206SBjoern A. Zeeb 		    BIT_WKFCAM_CLR_V1);
2472774f206SBjoern A. Zeeb 
2482774f206SBjoern A. Zeeb 	ret = check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0);
2492774f206SBjoern A. Zeeb 	if (!ret)
2502774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to clean pattern cam\n");
2512774f206SBjoern A. Zeeb }
2522774f206SBjoern A. Zeeb 
rtw_wow_pattern_write(struct rtw_dev * rtwdev)2532774f206SBjoern A. Zeeb static void rtw_wow_pattern_write(struct rtw_dev *rtwdev)
2542774f206SBjoern A. Zeeb {
2552774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2562774f206SBjoern A. Zeeb 	struct rtw_wow_pattern *rtw_pattern = rtw_wow->patterns;
2572774f206SBjoern A. Zeeb 	int i = 0;
2582774f206SBjoern A. Zeeb 
2592774f206SBjoern A. Zeeb 	for (i = 0; i < rtw_wow->pattern_cnt; i++)
2602774f206SBjoern A. Zeeb 		rtw_wow_pattern_write_cam_ent(rtwdev, i, rtw_pattern + i);
2612774f206SBjoern A. Zeeb }
2622774f206SBjoern A. Zeeb 
rtw_wow_pattern_clear(struct rtw_dev * rtwdev)2632774f206SBjoern A. Zeeb static void rtw_wow_pattern_clear(struct rtw_dev *rtwdev)
2642774f206SBjoern A. Zeeb {
2652774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2662774f206SBjoern A. Zeeb 
2672774f206SBjoern A. Zeeb 	rtw_wow_pattern_clear_cam(rtwdev);
2682774f206SBjoern A. Zeeb 
2692774f206SBjoern A. Zeeb 	rtw_wow->pattern_cnt = 0;
2702774f206SBjoern A. Zeeb 	memset(rtw_wow->patterns, 0, sizeof(rtw_wow->patterns));
2712774f206SBjoern A. Zeeb }
2722774f206SBjoern A. Zeeb 
rtw_wow_bb_stop(struct rtw_dev * rtwdev)2732774f206SBjoern A. Zeeb static void rtw_wow_bb_stop(struct rtw_dev *rtwdev)
2742774f206SBjoern A. Zeeb {
2752774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2762774f206SBjoern A. Zeeb 
2772774f206SBjoern A. Zeeb 	/* wait 100ms for firmware to finish TX */
2782774f206SBjoern A. Zeeb 	msleep(100);
2792774f206SBjoern A. Zeeb 
2802774f206SBjoern A. Zeeb 	if (!rtw_read32_mask(rtwdev, REG_BCNQ_INFO, BIT_MGQ_CPU_EMPTY))
2812774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Wrong status of MGQ_CPU empty!\n");
2822774f206SBjoern A. Zeeb 
2832774f206SBjoern A. Zeeb 	rtw_wow->txpause = rtw_read8(rtwdev, REG_TXPAUSE);
2842774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_TXPAUSE, 0xff);
2852774f206SBjoern A. Zeeb 	rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
2862774f206SBjoern A. Zeeb }
2872774f206SBjoern A. Zeeb 
rtw_wow_bb_start(struct rtw_dev * rtwdev)2882774f206SBjoern A. Zeeb static void rtw_wow_bb_start(struct rtw_dev *rtwdev)
2892774f206SBjoern A. Zeeb {
2902774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2912774f206SBjoern A. Zeeb 
2922774f206SBjoern A. Zeeb 	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
2932774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_TXPAUSE, rtw_wow->txpause);
2942774f206SBjoern A. Zeeb }
2952774f206SBjoern A. Zeeb 
rtw_wow_rx_dma_stop(struct rtw_dev * rtwdev)2962774f206SBjoern A. Zeeb static void rtw_wow_rx_dma_stop(struct rtw_dev *rtwdev)
2972774f206SBjoern A. Zeeb {
2982774f206SBjoern A. Zeeb 	/* wait 100ms for HW to finish rx dma */
2992774f206SBjoern A. Zeeb 	msleep(100);
3002774f206SBjoern A. Zeeb 
3012774f206SBjoern A. Zeeb 	rtw_write32_set(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE);
3022774f206SBjoern A. Zeeb 
3032774f206SBjoern A. Zeeb 	if (!check_hw_ready(rtwdev, REG_RXPKT_NUM, BIT_RXDMA_IDLE, 1))
3042774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to stop rx dma\n");
3052774f206SBjoern A. Zeeb }
3062774f206SBjoern A. Zeeb 
rtw_wow_rx_dma_start(struct rtw_dev * rtwdev)3072774f206SBjoern A. Zeeb static void rtw_wow_rx_dma_start(struct rtw_dev *rtwdev)
3082774f206SBjoern A. Zeeb {
3092774f206SBjoern A. Zeeb 	rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE);
3102774f206SBjoern A. Zeeb }
3112774f206SBjoern A. Zeeb 
rtw_wow_check_fw_status(struct rtw_dev * rtwdev,bool wow_enable)3122774f206SBjoern A. Zeeb static int rtw_wow_check_fw_status(struct rtw_dev *rtwdev, bool wow_enable)
3132774f206SBjoern A. Zeeb {
3142774f206SBjoern A. Zeeb 	int ret;
3152774f206SBjoern A. Zeeb 	u8 check;
3162774f206SBjoern A. Zeeb 	u32 check_dis;
3172774f206SBjoern A. Zeeb 
3182774f206SBjoern A. Zeeb 	if (wow_enable) {
3192774f206SBjoern A. Zeeb 		ret = read_poll_timeout(rtw_read8, check, !check, 1000,
3202774f206SBjoern A. Zeeb 					100000, true, rtwdev,
3212774f206SBjoern A. Zeeb 					REG_WOWLAN_WAKE_REASON);
3222774f206SBjoern A. Zeeb 		if (ret)
3232774f206SBjoern A. Zeeb 			goto wow_fail;
3242774f206SBjoern A. Zeeb 	} else {
3252774f206SBjoern A. Zeeb 		ret = read_poll_timeout(rtw_read32_mask, check_dis,
3262774f206SBjoern A. Zeeb 					!check_dis, 1000, 100000, true, rtwdev,
3272774f206SBjoern A. Zeeb 					REG_FE1IMR, BIT_FS_RXDONE);
3282774f206SBjoern A. Zeeb 		if (ret)
3292774f206SBjoern A. Zeeb 			goto wow_fail;
3302774f206SBjoern A. Zeeb 		ret = read_poll_timeout(rtw_read32_mask, check_dis,
3312774f206SBjoern A. Zeeb 					!check_dis, 1000, 100000, false, rtwdev,
3322774f206SBjoern A. Zeeb 					REG_RXPKT_NUM, BIT_RW_RELEASE);
3332774f206SBjoern A. Zeeb 		if (ret)
3342774f206SBjoern A. Zeeb 			goto wow_fail;
3352774f206SBjoern A. Zeeb 	}
3362774f206SBjoern A. Zeeb 
3372774f206SBjoern A. Zeeb 	return 0;
3382774f206SBjoern A. Zeeb 
3392774f206SBjoern A. Zeeb wow_fail:
3402774f206SBjoern A. Zeeb 	rtw_err(rtwdev, "failed to check wow status %s\n",
3412774f206SBjoern A. Zeeb 		wow_enable ? "enabled" : "disabled");
3422774f206SBjoern A. Zeeb 	return -EBUSY;
3432774f206SBjoern A. Zeeb }
3442774f206SBjoern A. Zeeb 
rtw_wow_fw_security_type_iter(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key,void * data)3452774f206SBjoern A. Zeeb static void rtw_wow_fw_security_type_iter(struct ieee80211_hw *hw,
3462774f206SBjoern A. Zeeb 					  struct ieee80211_vif *vif,
3472774f206SBjoern A. Zeeb 					  struct ieee80211_sta *sta,
3482774f206SBjoern A. Zeeb 					  struct ieee80211_key_conf *key,
3492774f206SBjoern A. Zeeb 					  void *data)
3502774f206SBjoern A. Zeeb {
3512774f206SBjoern A. Zeeb 	struct rtw_fw_key_type_iter_data *iter_data = data;
3522774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
3532774f206SBjoern A. Zeeb 	u8 hw_key_type;
3542774f206SBjoern A. Zeeb 
3552774f206SBjoern A. Zeeb 	if (vif != rtwdev->wow.wow_vif)
3562774f206SBjoern A. Zeeb 		return;
3572774f206SBjoern A. Zeeb 
3582774f206SBjoern A. Zeeb 	switch (key->cipher) {
3592774f206SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP40:
3602774f206SBjoern A. Zeeb 		hw_key_type = RTW_CAM_WEP40;
3612774f206SBjoern A. Zeeb 		break;
3622774f206SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP104:
3632774f206SBjoern A. Zeeb 		hw_key_type = RTW_CAM_WEP104;
3642774f206SBjoern A. Zeeb 		break;
3652774f206SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_TKIP:
3662774f206SBjoern A. Zeeb 		hw_key_type = RTW_CAM_TKIP;
3672774f206SBjoern A. Zeeb 		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
3682774f206SBjoern A. Zeeb 		break;
3692774f206SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP:
3702774f206SBjoern A. Zeeb 		hw_key_type = RTW_CAM_AES;
3712774f206SBjoern A. Zeeb 		key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
3722774f206SBjoern A. Zeeb 		break;
3732774f206SBjoern A. Zeeb 	default:
3742774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "Unsupported key type for wowlan mode: %#x\n",
3752774f206SBjoern A. Zeeb 			key->cipher);
3762774f206SBjoern A. Zeeb 		hw_key_type = 0;
3772774f206SBjoern A. Zeeb 		break;
3782774f206SBjoern A. Zeeb 	}
3792774f206SBjoern A. Zeeb 
3802774f206SBjoern A. Zeeb 	if (sta)
3812774f206SBjoern A. Zeeb 		iter_data->pairwise_key_type = hw_key_type;
3822774f206SBjoern A. Zeeb 	else
3832774f206SBjoern A. Zeeb 		iter_data->group_key_type = hw_key_type;
3842774f206SBjoern A. Zeeb }
3852774f206SBjoern A. Zeeb 
rtw_wow_fw_security_type(struct rtw_dev * rtwdev)3862774f206SBjoern A. Zeeb static void rtw_wow_fw_security_type(struct rtw_dev *rtwdev)
3872774f206SBjoern A. Zeeb {
3882774f206SBjoern A. Zeeb 	struct rtw_fw_key_type_iter_data data = {};
3892774f206SBjoern A. Zeeb 	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
3902774f206SBjoern A. Zeeb 
3912774f206SBjoern A. Zeeb 	data.rtwdev = rtwdev;
3922774f206SBjoern A. Zeeb 	rtw_iterate_keys(rtwdev, wow_vif,
3932774f206SBjoern A. Zeeb 			 rtw_wow_fw_security_type_iter, &data);
3942774f206SBjoern A. Zeeb 	rtw_fw_set_aoac_global_info_cmd(rtwdev, data.pairwise_key_type,
3952774f206SBjoern A. Zeeb 					data.group_key_type);
3962774f206SBjoern A. Zeeb }
3972774f206SBjoern A. Zeeb 
rtw_wow_fw_start(struct rtw_dev * rtwdev)3982774f206SBjoern A. Zeeb static int rtw_wow_fw_start(struct rtw_dev *rtwdev)
3992774f206SBjoern A. Zeeb {
4002774f206SBjoern A. Zeeb 	if (rtw_wow_mgd_linked(rtwdev)) {
4012774f206SBjoern A. Zeeb 		rtw_send_rsvd_page_h2c(rtwdev);
4022774f206SBjoern A. Zeeb 		rtw_wow_pattern_write(rtwdev);
4032774f206SBjoern A. Zeeb 		rtw_wow_fw_security_type(rtwdev);
4042774f206SBjoern A. Zeeb 		rtw_fw_set_disconnect_decision_cmd(rtwdev, true);
4052774f206SBjoern A. Zeeb 		rtw_fw_set_keep_alive_cmd(rtwdev, true);
4062774f206SBjoern A. Zeeb 	} else if (rtw_wow_no_link(rtwdev)) {
4072774f206SBjoern A. Zeeb 		rtw_fw_set_nlo_info(rtwdev, true);
4082774f206SBjoern A. Zeeb 		rtw_fw_update_pkt_probe_req(rtwdev, NULL);
4092774f206SBjoern A. Zeeb 		rtw_fw_channel_switch(rtwdev, true);
4102774f206SBjoern A. Zeeb 	}
4112774f206SBjoern A. Zeeb 
4122774f206SBjoern A. Zeeb 	rtw_fw_set_wowlan_ctrl_cmd(rtwdev, true);
4132774f206SBjoern A. Zeeb 	rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, true);
4142774f206SBjoern A. Zeeb 
4152774f206SBjoern A. Zeeb 	return rtw_wow_check_fw_status(rtwdev, true);
4162774f206SBjoern A. Zeeb }
4172774f206SBjoern A. Zeeb 
rtw_wow_fw_stop(struct rtw_dev * rtwdev)4182774f206SBjoern A. Zeeb static int rtw_wow_fw_stop(struct rtw_dev *rtwdev)
4192774f206SBjoern A. Zeeb {
4202774f206SBjoern A. Zeeb 	if (rtw_wow_mgd_linked(rtwdev)) {
4212774f206SBjoern A. Zeeb 		rtw_fw_set_disconnect_decision_cmd(rtwdev, false);
4222774f206SBjoern A. Zeeb 		rtw_fw_set_keep_alive_cmd(rtwdev, false);
4232774f206SBjoern A. Zeeb 		rtw_wow_pattern_clear(rtwdev);
4242774f206SBjoern A. Zeeb 	} else if (rtw_wow_no_link(rtwdev)) {
4252774f206SBjoern A. Zeeb 		rtw_fw_channel_switch(rtwdev, false);
4262774f206SBjoern A. Zeeb 		rtw_fw_set_nlo_info(rtwdev, false);
4272774f206SBjoern A. Zeeb 	}
4282774f206SBjoern A. Zeeb 
4292774f206SBjoern A. Zeeb 	rtw_fw_set_wowlan_ctrl_cmd(rtwdev, false);
4302774f206SBjoern A. Zeeb 	rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, false);
4312774f206SBjoern A. Zeeb 
4322774f206SBjoern A. Zeeb 	return rtw_wow_check_fw_status(rtwdev, false);
4332774f206SBjoern A. Zeeb }
4342774f206SBjoern A. Zeeb 
rtw_wow_avoid_reset_mac(struct rtw_dev * rtwdev)4352774f206SBjoern A. Zeeb static void rtw_wow_avoid_reset_mac(struct rtw_dev *rtwdev)
4362774f206SBjoern A. Zeeb {
4372774f206SBjoern A. Zeeb 	/* When resuming from wowlan mode, some hosts issue signal
4382774f206SBjoern A. Zeeb 	 * (PCIE: PREST, USB: SE0RST) to device, and lead to reset
4392774f206SBjoern A. Zeeb 	 * mac core. If it happens, the connection to AP will be lost.
4402774f206SBjoern A. Zeeb 	 * Setting REG_RSV_CTRL Register can avoid this process.
4412774f206SBjoern A. Zeeb 	 */
4422774f206SBjoern A. Zeeb 	switch (rtw_hci_type(rtwdev)) {
4432774f206SBjoern A. Zeeb 	case RTW_HCI_TYPE_PCIE:
4442774f206SBjoern A. Zeeb 	case RTW_HCI_TYPE_USB:
4452774f206SBjoern A. Zeeb 		rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6);
4462774f206SBjoern A. Zeeb 		rtw_write8(rtwdev, REG_RSV_CTRL,
4472774f206SBjoern A. Zeeb 			   BIT_WLOCK_1C_B6 | BIT_R_DIS_PRST);
4482774f206SBjoern A. Zeeb 		break;
4492774f206SBjoern A. Zeeb 	default:
4502774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Unsupported hci type to disable reset MAC\n");
4512774f206SBjoern A. Zeeb 		break;
4522774f206SBjoern A. Zeeb 	}
4532774f206SBjoern A. Zeeb }
4542774f206SBjoern A. Zeeb 
rtw_wow_fw_media_status_iter(void * data,struct ieee80211_sta * sta)4552774f206SBjoern A. Zeeb static void rtw_wow_fw_media_status_iter(void *data, struct ieee80211_sta *sta)
4562774f206SBjoern A. Zeeb {
4572774f206SBjoern A. Zeeb 	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
4582774f206SBjoern A. Zeeb 	struct rtw_fw_media_status_iter_data *iter_data = data;
4592774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = iter_data->rtwdev;
4602774f206SBjoern A. Zeeb 
4612774f206SBjoern A. Zeeb 	rtw_fw_media_status_report(rtwdev, si->mac_id, iter_data->connect);
4622774f206SBjoern A. Zeeb }
4632774f206SBjoern A. Zeeb 
rtw_wow_fw_media_status(struct rtw_dev * rtwdev,bool connect)4642774f206SBjoern A. Zeeb static void rtw_wow_fw_media_status(struct rtw_dev *rtwdev, bool connect)
4652774f206SBjoern A. Zeeb {
4662774f206SBjoern A. Zeeb 	struct rtw_fw_media_status_iter_data data;
4672774f206SBjoern A. Zeeb 
4682774f206SBjoern A. Zeeb 	data.rtwdev = rtwdev;
4692774f206SBjoern A. Zeeb 	data.connect = connect;
4702774f206SBjoern A. Zeeb 
4712774f206SBjoern A. Zeeb 	rtw_iterate_stas_atomic(rtwdev, rtw_wow_fw_media_status_iter, &data);
4722774f206SBjoern A. Zeeb }
4732774f206SBjoern A. Zeeb 
rtw_wow_config_wow_fw_rsvd_page(struct rtw_dev * rtwdev)4742774f206SBjoern A. Zeeb static int rtw_wow_config_wow_fw_rsvd_page(struct rtw_dev *rtwdev)
4752774f206SBjoern A. Zeeb {
4762774f206SBjoern A. Zeeb 	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
4772774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
4782774f206SBjoern A. Zeeb 
4792774f206SBjoern A. Zeeb 	rtw_remove_rsvd_page(rtwdev, rtwvif);
4802774f206SBjoern A. Zeeb 
4812774f206SBjoern A. Zeeb 	if (rtw_wow_no_link(rtwdev))
4822774f206SBjoern A. Zeeb 		rtw_add_rsvd_page_pno(rtwdev, rtwvif);
4832774f206SBjoern A. Zeeb 	else
4842774f206SBjoern A. Zeeb 		rtw_add_rsvd_page_sta(rtwdev, rtwvif);
4852774f206SBjoern A. Zeeb 
4862774f206SBjoern A. Zeeb 	return rtw_fw_download_rsvd_page(rtwdev);
4872774f206SBjoern A. Zeeb }
4882774f206SBjoern A. Zeeb 
rtw_wow_config_normal_fw_rsvd_page(struct rtw_dev * rtwdev)4892774f206SBjoern A. Zeeb static int rtw_wow_config_normal_fw_rsvd_page(struct rtw_dev *rtwdev)
4902774f206SBjoern A. Zeeb {
4912774f206SBjoern A. Zeeb 	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
4922774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
4932774f206SBjoern A. Zeeb 
4942774f206SBjoern A. Zeeb 	rtw_remove_rsvd_page(rtwdev, rtwvif);
4952774f206SBjoern A. Zeeb 	rtw_add_rsvd_page_sta(rtwdev, rtwvif);
4962774f206SBjoern A. Zeeb 
4972774f206SBjoern A. Zeeb 	if (rtw_wow_no_link(rtwdev))
4982774f206SBjoern A. Zeeb 		return 0;
4992774f206SBjoern A. Zeeb 
5002774f206SBjoern A. Zeeb 	return rtw_fw_download_rsvd_page(rtwdev);
5012774f206SBjoern A. Zeeb }
5022774f206SBjoern A. Zeeb 
rtw_wow_swap_fw(struct rtw_dev * rtwdev,enum rtw_fw_type type)5032774f206SBjoern A. Zeeb static int rtw_wow_swap_fw(struct rtw_dev *rtwdev, enum rtw_fw_type type)
5042774f206SBjoern A. Zeeb {
5052774f206SBjoern A. Zeeb 	struct rtw_fw_state *fw;
5062774f206SBjoern A. Zeeb 	int ret;
5072774f206SBjoern A. Zeeb 
5082774f206SBjoern A. Zeeb 	switch (type) {
5092774f206SBjoern A. Zeeb 	case RTW_WOWLAN_FW:
5102774f206SBjoern A. Zeeb 		fw = &rtwdev->wow_fw;
5112774f206SBjoern A. Zeeb 		break;
5122774f206SBjoern A. Zeeb 
5132774f206SBjoern A. Zeeb 	case RTW_NORMAL_FW:
5142774f206SBjoern A. Zeeb 		fw = &rtwdev->fw;
5152774f206SBjoern A. Zeeb 		break;
5162774f206SBjoern A. Zeeb 
5172774f206SBjoern A. Zeeb 	default:
5182774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "unsupported firmware type to swap\n");
5192774f206SBjoern A. Zeeb 		return -ENOENT;
5202774f206SBjoern A. Zeeb 	}
5212774f206SBjoern A. Zeeb 
5222774f206SBjoern A. Zeeb 	ret = rtw_download_firmware(rtwdev, fw);
5232774f206SBjoern A. Zeeb 	if (ret)
5242774f206SBjoern A. Zeeb 		goto out;
5252774f206SBjoern A. Zeeb 
5262774f206SBjoern A. Zeeb 	rtw_fw_send_general_info(rtwdev);
5272774f206SBjoern A. Zeeb 	rtw_fw_send_phydm_info(rtwdev);
5282774f206SBjoern A. Zeeb 	rtw_wow_fw_media_status(rtwdev, true);
5292774f206SBjoern A. Zeeb 
5302774f206SBjoern A. Zeeb out:
5312774f206SBjoern A. Zeeb 	return ret;
5322774f206SBjoern A. Zeeb }
5332774f206SBjoern A. Zeeb 
rtw_wow_check_pno(struct rtw_dev * rtwdev,struct cfg80211_sched_scan_request * nd_config)5342774f206SBjoern A. Zeeb static void rtw_wow_check_pno(struct rtw_dev *rtwdev,
5352774f206SBjoern A. Zeeb 			      struct cfg80211_sched_scan_request *nd_config)
5362774f206SBjoern A. Zeeb {
5372774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
5382774f206SBjoern A. Zeeb 	struct rtw_pno_request *pno_req = &rtw_wow->pno_req;
5392774f206SBjoern A. Zeeb 	struct ieee80211_channel *channel;
5402774f206SBjoern A. Zeeb 	int i, size;
5412774f206SBjoern A. Zeeb 
5422774f206SBjoern A. Zeeb 	if (!nd_config->n_match_sets || !nd_config->n_channels)
5432774f206SBjoern A. Zeeb 		goto err;
5442774f206SBjoern A. Zeeb 
5452774f206SBjoern A. Zeeb 	pno_req->match_set_cnt = nd_config->n_match_sets;
5462774f206SBjoern A. Zeeb 	size = sizeof(*pno_req->match_sets) * pno_req->match_set_cnt;
5472774f206SBjoern A. Zeeb 	pno_req->match_sets = kmemdup(nd_config->match_sets, size, GFP_KERNEL);
5482774f206SBjoern A. Zeeb 	if (!pno_req->match_sets)
5492774f206SBjoern A. Zeeb 		goto err;
5502774f206SBjoern A. Zeeb 
5512774f206SBjoern A. Zeeb 	pno_req->channel_cnt = nd_config->n_channels;
5522774f206SBjoern A. Zeeb 	size = sizeof(*nd_config->channels[0]) * nd_config->n_channels;
5532774f206SBjoern A. Zeeb 	pno_req->channels = kmalloc(size, GFP_KERNEL);
5542774f206SBjoern A. Zeeb 	if (!pno_req->channels)
5552774f206SBjoern A. Zeeb 		goto channel_err;
5562774f206SBjoern A. Zeeb 
5572774f206SBjoern A. Zeeb 	for (i = 0 ; i < pno_req->channel_cnt; i++) {
5582774f206SBjoern A. Zeeb 		channel = pno_req->channels + i;
5592774f206SBjoern A. Zeeb 		memcpy(channel, nd_config->channels[i], sizeof(*channel));
5602774f206SBjoern A. Zeeb 	}
5612774f206SBjoern A. Zeeb 
5622774f206SBjoern A. Zeeb 	pno_req->scan_plan = *nd_config->scan_plans;
5632774f206SBjoern A. Zeeb 	pno_req->inited = true;
5642774f206SBjoern A. Zeeb 
5652774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is enabled\n");
5662774f206SBjoern A. Zeeb 
5672774f206SBjoern A. Zeeb 	return;
5682774f206SBjoern A. Zeeb 
5692774f206SBjoern A. Zeeb channel_err:
5702774f206SBjoern A. Zeeb 	kfree(pno_req->match_sets);
5712774f206SBjoern A. Zeeb 
5722774f206SBjoern A. Zeeb err:
5732774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is disabled\n");
5742774f206SBjoern A. Zeeb }
5752774f206SBjoern A. Zeeb 
rtw_wow_leave_linked_ps(struct rtw_dev * rtwdev)5762774f206SBjoern A. Zeeb static int rtw_wow_leave_linked_ps(struct rtw_dev *rtwdev)
5772774f206SBjoern A. Zeeb {
5782774f206SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
5792774f206SBjoern A. Zeeb 		cancel_delayed_work_sync(&rtwdev->watch_dog_work);
5802774f206SBjoern A. Zeeb 
5812774f206SBjoern A. Zeeb 	rtw_leave_lps(rtwdev);
5822774f206SBjoern A. Zeeb 
5832774f206SBjoern A. Zeeb 	return 0;
5842774f206SBjoern A. Zeeb }
5852774f206SBjoern A. Zeeb 
rtw_wow_leave_no_link_ps(struct rtw_dev * rtwdev)5862774f206SBjoern A. Zeeb static int rtw_wow_leave_no_link_ps(struct rtw_dev *rtwdev)
5872774f206SBjoern A. Zeeb {
5882774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
5892774f206SBjoern A. Zeeb 	int ret = 0;
5902774f206SBjoern A. Zeeb 
5912774f206SBjoern A. Zeeb 	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) {
5922774f206SBjoern A. Zeeb 		if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE)
5932774f206SBjoern A. Zeeb 			rtw_leave_lps_deep(rtwdev);
5942774f206SBjoern A. Zeeb 	} else {
59590aac0d8SBjoern A. Zeeb 		if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags)) {
5962774f206SBjoern A. Zeeb 			rtw_wow->ips_enabled = true;
5972774f206SBjoern A. Zeeb 			ret = rtw_leave_ips(rtwdev);
5982774f206SBjoern A. Zeeb 			if (ret)
5992774f206SBjoern A. Zeeb 				return ret;
6002774f206SBjoern A. Zeeb 		}
6012774f206SBjoern A. Zeeb 	}
6022774f206SBjoern A. Zeeb 
6032774f206SBjoern A. Zeeb 	return 0;
6042774f206SBjoern A. Zeeb }
6052774f206SBjoern A. Zeeb 
rtw_wow_leave_ps(struct rtw_dev * rtwdev)6062774f206SBjoern A. Zeeb static int rtw_wow_leave_ps(struct rtw_dev *rtwdev)
6072774f206SBjoern A. Zeeb {
6082774f206SBjoern A. Zeeb 	int ret = 0;
6092774f206SBjoern A. Zeeb 
6102774f206SBjoern A. Zeeb 	if (rtw_wow_mgd_linked(rtwdev))
6112774f206SBjoern A. Zeeb 		ret = rtw_wow_leave_linked_ps(rtwdev);
6122774f206SBjoern A. Zeeb 	else if (rtw_wow_no_link(rtwdev))
6132774f206SBjoern A. Zeeb 		ret = rtw_wow_leave_no_link_ps(rtwdev);
6142774f206SBjoern A. Zeeb 
6152774f206SBjoern A. Zeeb 	return ret;
6162774f206SBjoern A. Zeeb }
6172774f206SBjoern A. Zeeb 
rtw_wow_restore_ps(struct rtw_dev * rtwdev)6182774f206SBjoern A. Zeeb static int rtw_wow_restore_ps(struct rtw_dev *rtwdev)
6192774f206SBjoern A. Zeeb {
6202774f206SBjoern A. Zeeb 	int ret = 0;
6212774f206SBjoern A. Zeeb 
6222774f206SBjoern A. Zeeb 	if (rtw_wow_no_link(rtwdev) && rtwdev->wow.ips_enabled)
6232774f206SBjoern A. Zeeb 		ret = rtw_enter_ips(rtwdev);
6242774f206SBjoern A. Zeeb 
6252774f206SBjoern A. Zeeb 	return ret;
6262774f206SBjoern A. Zeeb }
6272774f206SBjoern A. Zeeb 
rtw_wow_enter_linked_ps(struct rtw_dev * rtwdev)6282774f206SBjoern A. Zeeb static int rtw_wow_enter_linked_ps(struct rtw_dev *rtwdev)
6292774f206SBjoern A. Zeeb {
6302774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
6312774f206SBjoern A. Zeeb 	struct ieee80211_vif *wow_vif = rtw_wow->wow_vif;
6322774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
6332774f206SBjoern A. Zeeb 
6342774f206SBjoern A. Zeeb 	rtw_enter_lps(rtwdev, rtwvif->port);
6352774f206SBjoern A. Zeeb 
6362774f206SBjoern A. Zeeb 	return 0;
6372774f206SBjoern A. Zeeb }
6382774f206SBjoern A. Zeeb 
rtw_wow_enter_no_link_ps(struct rtw_dev * rtwdev)6392774f206SBjoern A. Zeeb static int rtw_wow_enter_no_link_ps(struct rtw_dev *rtwdev)
6402774f206SBjoern A. Zeeb {
6412774f206SBjoern A. Zeeb 	/* firmware enters deep ps by itself if supported */
6422774f206SBjoern A. Zeeb 	set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
6432774f206SBjoern A. Zeeb 
6442774f206SBjoern A. Zeeb 	return 0;
6452774f206SBjoern A. Zeeb }
6462774f206SBjoern A. Zeeb 
rtw_wow_enter_ps(struct rtw_dev * rtwdev)6472774f206SBjoern A. Zeeb static int rtw_wow_enter_ps(struct rtw_dev *rtwdev)
6482774f206SBjoern A. Zeeb {
6492774f206SBjoern A. Zeeb 	int ret = 0;
6502774f206SBjoern A. Zeeb 
6512774f206SBjoern A. Zeeb 	if (rtw_wow_mgd_linked(rtwdev))
6522774f206SBjoern A. Zeeb 		ret = rtw_wow_enter_linked_ps(rtwdev);
6532774f206SBjoern A. Zeeb 	else if (rtw_wow_no_link(rtwdev) &&
6542774f206SBjoern A. Zeeb 		 rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE)
6552774f206SBjoern A. Zeeb 		ret = rtw_wow_enter_no_link_ps(rtwdev);
6562774f206SBjoern A. Zeeb 
6572774f206SBjoern A. Zeeb 	return ret;
6582774f206SBjoern A. Zeeb }
6592774f206SBjoern A. Zeeb 
rtw_wow_stop_trx(struct rtw_dev * rtwdev)6602774f206SBjoern A. Zeeb static void rtw_wow_stop_trx(struct rtw_dev *rtwdev)
6612774f206SBjoern A. Zeeb {
6622774f206SBjoern A. Zeeb 	rtw_wow_bb_stop(rtwdev);
6632774f206SBjoern A. Zeeb 	rtw_wow_rx_dma_stop(rtwdev);
6642774f206SBjoern A. Zeeb }
6652774f206SBjoern A. Zeeb 
rtw_wow_start(struct rtw_dev * rtwdev)6662774f206SBjoern A. Zeeb static int rtw_wow_start(struct rtw_dev *rtwdev)
6672774f206SBjoern A. Zeeb {
6682774f206SBjoern A. Zeeb 	int ret;
6692774f206SBjoern A. Zeeb 
6702774f206SBjoern A. Zeeb 	ret = rtw_wow_fw_start(rtwdev);
6712774f206SBjoern A. Zeeb 	if (ret)
6722774f206SBjoern A. Zeeb 		goto out;
6732774f206SBjoern A. Zeeb 
6742774f206SBjoern A. Zeeb 	rtw_hci_stop(rtwdev);
6752774f206SBjoern A. Zeeb 	rtw_wow_bb_start(rtwdev);
6762774f206SBjoern A. Zeeb 	rtw_wow_avoid_reset_mac(rtwdev);
6772774f206SBjoern A. Zeeb 
6782774f206SBjoern A. Zeeb out:
6792774f206SBjoern A. Zeeb 	return ret;
6802774f206SBjoern A. Zeeb }
6812774f206SBjoern A. Zeeb 
rtw_wow_enable(struct rtw_dev * rtwdev)6822774f206SBjoern A. Zeeb static int rtw_wow_enable(struct rtw_dev *rtwdev)
6832774f206SBjoern A. Zeeb {
6842774f206SBjoern A. Zeeb 	int ret = 0;
6852774f206SBjoern A. Zeeb 
6862774f206SBjoern A. Zeeb 	rtw_wow_stop_trx(rtwdev);
6872774f206SBjoern A. Zeeb 
6882774f206SBjoern A. Zeeb 	ret = rtw_wow_swap_fw(rtwdev, RTW_WOWLAN_FW);
6892774f206SBjoern A. Zeeb 	if (ret) {
6902774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to swap wow fw\n");
6912774f206SBjoern A. Zeeb 		goto error;
6922774f206SBjoern A. Zeeb 	}
6932774f206SBjoern A. Zeeb 
6942774f206SBjoern A. Zeeb 	set_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
6952774f206SBjoern A. Zeeb 
6962774f206SBjoern A. Zeeb 	ret = rtw_wow_config_wow_fw_rsvd_page(rtwdev);
6972774f206SBjoern A. Zeeb 	if (ret) {
6982774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download wowlan rsvd page\n");
6992774f206SBjoern A. Zeeb 		goto error;
7002774f206SBjoern A. Zeeb 	}
7012774f206SBjoern A. Zeeb 
7022774f206SBjoern A. Zeeb 	ret = rtw_wow_start(rtwdev);
7032774f206SBjoern A. Zeeb 	if (ret) {
7042774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to start wow\n");
7052774f206SBjoern A. Zeeb 		goto error;
7062774f206SBjoern A. Zeeb 	}
7072774f206SBjoern A. Zeeb 
7082774f206SBjoern A. Zeeb 	return ret;
7092774f206SBjoern A. Zeeb 
7102774f206SBjoern A. Zeeb error:
7112774f206SBjoern A. Zeeb 	clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
7122774f206SBjoern A. Zeeb 	return ret;
7132774f206SBjoern A. Zeeb }
7142774f206SBjoern A. Zeeb 
rtw_wow_stop(struct rtw_dev * rtwdev)7152774f206SBjoern A. Zeeb static int rtw_wow_stop(struct rtw_dev *rtwdev)
7162774f206SBjoern A. Zeeb {
7172774f206SBjoern A. Zeeb 	int ret;
7182774f206SBjoern A. Zeeb 
7192774f206SBjoern A. Zeeb 	/* some HCI related registers will be reset after resume,
7202774f206SBjoern A. Zeeb 	 * need to set them again.
7212774f206SBjoern A. Zeeb 	 */
7222774f206SBjoern A. Zeeb 	ret = rtw_hci_setup(rtwdev);
7232774f206SBjoern A. Zeeb 	if (ret) {
7242774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to setup hci\n");
7252774f206SBjoern A. Zeeb 		return ret;
7262774f206SBjoern A. Zeeb 	}
7272774f206SBjoern A. Zeeb 
7282774f206SBjoern A. Zeeb 	ret = rtw_hci_start(rtwdev);
7292774f206SBjoern A. Zeeb 	if (ret) {
7302774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to start hci\n");
7312774f206SBjoern A. Zeeb 		return ret;
7322774f206SBjoern A. Zeeb 	}
7332774f206SBjoern A. Zeeb 
7342774f206SBjoern A. Zeeb 	ret = rtw_wow_fw_stop(rtwdev);
7352774f206SBjoern A. Zeeb 	if (ret)
7362774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to stop wowlan fw\n");
7372774f206SBjoern A. Zeeb 
7382774f206SBjoern A. Zeeb 	rtw_wow_bb_stop(rtwdev);
7392774f206SBjoern A. Zeeb 
7402774f206SBjoern A. Zeeb 	return ret;
7412774f206SBjoern A. Zeeb }
7422774f206SBjoern A. Zeeb 
rtw_wow_resume_trx(struct rtw_dev * rtwdev)7432774f206SBjoern A. Zeeb static void rtw_wow_resume_trx(struct rtw_dev *rtwdev)
7442774f206SBjoern A. Zeeb {
7452774f206SBjoern A. Zeeb 	rtw_wow_rx_dma_start(rtwdev);
7462774f206SBjoern A. Zeeb 	rtw_wow_bb_start(rtwdev);
7472774f206SBjoern A. Zeeb 	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work,
7482774f206SBjoern A. Zeeb 				     RTW_WATCH_DOG_DELAY_TIME);
7492774f206SBjoern A. Zeeb }
7502774f206SBjoern A. Zeeb 
rtw_wow_disable(struct rtw_dev * rtwdev)7512774f206SBjoern A. Zeeb static int rtw_wow_disable(struct rtw_dev *rtwdev)
7522774f206SBjoern A. Zeeb {
7532774f206SBjoern A. Zeeb 	int ret;
7542774f206SBjoern A. Zeeb 
7552774f206SBjoern A. Zeeb 	clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
7562774f206SBjoern A. Zeeb 
7572774f206SBjoern A. Zeeb 	ret = rtw_wow_stop(rtwdev);
7582774f206SBjoern A. Zeeb 	if (ret) {
7592774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to stop wow\n");
7602774f206SBjoern A. Zeeb 		goto out;
7612774f206SBjoern A. Zeeb 	}
7622774f206SBjoern A. Zeeb 
7632774f206SBjoern A. Zeeb 	ret = rtw_wow_swap_fw(rtwdev, RTW_NORMAL_FW);
7642774f206SBjoern A. Zeeb 	if (ret) {
7652774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to swap normal fw\n");
7662774f206SBjoern A. Zeeb 		goto out;
7672774f206SBjoern A. Zeeb 	}
7682774f206SBjoern A. Zeeb 
7692774f206SBjoern A. Zeeb 	ret = rtw_wow_config_normal_fw_rsvd_page(rtwdev);
7702774f206SBjoern A. Zeeb 	if (ret)
7712774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download normal rsvd page\n");
7722774f206SBjoern A. Zeeb 
7732774f206SBjoern A. Zeeb out:
7742774f206SBjoern A. Zeeb 	rtw_wow_resume_trx(rtwdev);
7752774f206SBjoern A. Zeeb 	return ret;
7762774f206SBjoern A. Zeeb }
7772774f206SBjoern A. Zeeb 
rtw_wow_vif_iter(void * data,u8 * mac,struct ieee80211_vif * vif)7782774f206SBjoern A. Zeeb static void rtw_wow_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
7792774f206SBjoern A. Zeeb {
7802774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = data;
7812774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
7822774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
7832774f206SBjoern A. Zeeb 
7842774f206SBjoern A. Zeeb 	/* Current wowlan function support setting of only one STATION vif.
7852774f206SBjoern A. Zeeb 	 * So when one suitable vif is found, stop the iteration.
7862774f206SBjoern A. Zeeb 	 */
7872774f206SBjoern A. Zeeb 	if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION)
7882774f206SBjoern A. Zeeb 		return;
7892774f206SBjoern A. Zeeb 
7902774f206SBjoern A. Zeeb 	switch (rtwvif->net_type) {
7912774f206SBjoern A. Zeeb 	case RTW_NET_MGD_LINKED:
7922774f206SBjoern A. Zeeb 		rtw_wow->wow_vif = vif;
7932774f206SBjoern A. Zeeb 		break;
7942774f206SBjoern A. Zeeb 	case RTW_NET_NO_LINK:
7952774f206SBjoern A. Zeeb 		if (rtw_wow->pno_req.inited)
7962774f206SBjoern A. Zeeb 			rtwdev->wow.wow_vif = vif;
7972774f206SBjoern A. Zeeb 		break;
7982774f206SBjoern A. Zeeb 	default:
7992774f206SBjoern A. Zeeb 		break;
8002774f206SBjoern A. Zeeb 	}
8012774f206SBjoern A. Zeeb }
8022774f206SBjoern A. Zeeb 
rtw_wow_set_wakeups(struct rtw_dev * rtwdev,struct cfg80211_wowlan * wowlan)8032774f206SBjoern A. Zeeb static int rtw_wow_set_wakeups(struct rtw_dev *rtwdev,
8042774f206SBjoern A. Zeeb 			       struct cfg80211_wowlan *wowlan)
8052774f206SBjoern A. Zeeb {
8062774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
8072774f206SBjoern A. Zeeb 	struct rtw_wow_pattern *rtw_patterns = rtw_wow->patterns;
8082774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif;
8092774f206SBjoern A. Zeeb 	int i;
8102774f206SBjoern A. Zeeb 
8112774f206SBjoern A. Zeeb 	if (wowlan->disconnect)
8122774f206SBjoern A. Zeeb 		set_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags);
8132774f206SBjoern A. Zeeb 	if (wowlan->magic_pkt)
8142774f206SBjoern A. Zeeb 		set_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags);
8152774f206SBjoern A. Zeeb 	if (wowlan->gtk_rekey_failure)
8162774f206SBjoern A. Zeeb 		set_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags);
8172774f206SBjoern A. Zeeb 
8182774f206SBjoern A. Zeeb 	if (wowlan->nd_config)
8192774f206SBjoern A. Zeeb 		rtw_wow_check_pno(rtwdev, wowlan->nd_config);
8202774f206SBjoern A. Zeeb 
8212774f206SBjoern A. Zeeb 	rtw_iterate_vifs_atomic(rtwdev, rtw_wow_vif_iter, rtwdev);
8222774f206SBjoern A. Zeeb 	if (!rtw_wow->wow_vif)
8232774f206SBjoern A. Zeeb 		return -EPERM;
8242774f206SBjoern A. Zeeb 
8252774f206SBjoern A. Zeeb 	rtwvif = (struct rtw_vif *)rtw_wow->wow_vif->drv_priv;
8262774f206SBjoern A. Zeeb 	if (wowlan->n_patterns && wowlan->patterns) {
8272774f206SBjoern A. Zeeb 		rtw_wow->pattern_cnt = wowlan->n_patterns;
8282774f206SBjoern A. Zeeb 		for (i = 0; i < wowlan->n_patterns; i++)
8292774f206SBjoern A. Zeeb 			rtw_wow_pattern_generate(rtwdev, rtwvif,
8302774f206SBjoern A. Zeeb 						 wowlan->patterns + i,
8312774f206SBjoern A. Zeeb 						 rtw_patterns + i);
8322774f206SBjoern A. Zeeb 	}
8332774f206SBjoern A. Zeeb 
8342774f206SBjoern A. Zeeb 	return 0;
8352774f206SBjoern A. Zeeb }
8362774f206SBjoern A. Zeeb 
rtw_wow_clear_wakeups(struct rtw_dev * rtwdev)8372774f206SBjoern A. Zeeb static void rtw_wow_clear_wakeups(struct rtw_dev *rtwdev)
8382774f206SBjoern A. Zeeb {
8392774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
8402774f206SBjoern A. Zeeb 	struct rtw_pno_request *pno_req = &rtw_wow->pno_req;
8412774f206SBjoern A. Zeeb 
8422774f206SBjoern A. Zeeb 	if (pno_req->inited) {
8432774f206SBjoern A. Zeeb 		kfree(pno_req->channels);
8442774f206SBjoern A. Zeeb 		kfree(pno_req->match_sets);
8452774f206SBjoern A. Zeeb 	}
8462774f206SBjoern A. Zeeb 
8472774f206SBjoern A. Zeeb 	memset(rtw_wow, 0, sizeof(rtwdev->wow));
8482774f206SBjoern A. Zeeb }
8492774f206SBjoern A. Zeeb 
rtw_wow_suspend(struct rtw_dev * rtwdev,struct cfg80211_wowlan * wowlan)8502774f206SBjoern A. Zeeb int rtw_wow_suspend(struct rtw_dev *rtwdev, struct cfg80211_wowlan *wowlan)
8512774f206SBjoern A. Zeeb {
8522774f206SBjoern A. Zeeb 	int ret = 0;
8532774f206SBjoern A. Zeeb 
8542774f206SBjoern A. Zeeb 	ret = rtw_wow_set_wakeups(rtwdev, wowlan);
8552774f206SBjoern A. Zeeb 	if (ret) {
8562774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to set wakeup event\n");
8572774f206SBjoern A. Zeeb 		goto out;
8582774f206SBjoern A. Zeeb 	}
8592774f206SBjoern A. Zeeb 
8602774f206SBjoern A. Zeeb 	ret = rtw_wow_leave_ps(rtwdev);
8612774f206SBjoern A. Zeeb 	if (ret) {
8622774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to leave ps from normal mode\n");
8632774f206SBjoern A. Zeeb 		goto out;
8642774f206SBjoern A. Zeeb 	}
8652774f206SBjoern A. Zeeb 
8662774f206SBjoern A. Zeeb 	ret = rtw_wow_enable(rtwdev);
8672774f206SBjoern A. Zeeb 	if (ret) {
8682774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to enable wow\n");
8692774f206SBjoern A. Zeeb 		rtw_wow_restore_ps(rtwdev);
8702774f206SBjoern A. Zeeb 		goto out;
8712774f206SBjoern A. Zeeb 	}
8722774f206SBjoern A. Zeeb 
8732774f206SBjoern A. Zeeb 	ret = rtw_wow_enter_ps(rtwdev);
8742774f206SBjoern A. Zeeb 	if (ret)
8752774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to enter ps for wow\n");
8762774f206SBjoern A. Zeeb 
8772774f206SBjoern A. Zeeb out:
8782774f206SBjoern A. Zeeb 	return ret;
8792774f206SBjoern A. Zeeb }
8802774f206SBjoern A. Zeeb 
rtw_wow_resume(struct rtw_dev * rtwdev)8812774f206SBjoern A. Zeeb int rtw_wow_resume(struct rtw_dev *rtwdev)
8822774f206SBjoern A. Zeeb {
8832774f206SBjoern A. Zeeb 	int ret;
8842774f206SBjoern A. Zeeb 
8852774f206SBjoern A. Zeeb 	/* If wowlan mode is not enabled, do nothing */
8862774f206SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) {
8872774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "wow is not enabled\n");
8882774f206SBjoern A. Zeeb 		ret = -EPERM;
8892774f206SBjoern A. Zeeb 		goto out;
8902774f206SBjoern A. Zeeb 	}
8912774f206SBjoern A. Zeeb 
8922774f206SBjoern A. Zeeb 	ret = rtw_wow_leave_ps(rtwdev);
8932774f206SBjoern A. Zeeb 	if (ret) {
8942774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to leave ps from wowlan mode\n");
8952774f206SBjoern A. Zeeb 		goto out;
8962774f206SBjoern A. Zeeb 	}
8972774f206SBjoern A. Zeeb 
8982774f206SBjoern A. Zeeb 	rtw_wow_show_wakeup_reason(rtwdev);
8992774f206SBjoern A. Zeeb 
9002774f206SBjoern A. Zeeb 	ret = rtw_wow_disable(rtwdev);
9012774f206SBjoern A. Zeeb 	if (ret) {
9022774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to disable wow\n");
9032774f206SBjoern A. Zeeb 		goto out;
9042774f206SBjoern A. Zeeb 	}
9052774f206SBjoern A. Zeeb 
9062774f206SBjoern A. Zeeb 	ret = rtw_wow_restore_ps(rtwdev);
9072774f206SBjoern A. Zeeb 	if (ret)
9082774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to restore ps to normal mode\n");
9092774f206SBjoern A. Zeeb 
9102774f206SBjoern A. Zeeb out:
9112774f206SBjoern A. Zeeb 	rtw_wow_clear_wakeups(rtwdev);
9122774f206SBjoern A. Zeeb 	return ret;
9132774f206SBjoern A. Zeeb }
914