18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28c2ecf20Sopenharmony_ci/* Copyright(c) 2018-2019  Realtek Corporation
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include "main.h"
68c2ecf20Sopenharmony_ci#include "fw.h"
78c2ecf20Sopenharmony_ci#include "wow.h"
88c2ecf20Sopenharmony_ci#include "reg.h"
98c2ecf20Sopenharmony_ci#include "debug.h"
108c2ecf20Sopenharmony_ci#include "mac.h"
118c2ecf20Sopenharmony_ci#include "ps.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic void rtw_wow_show_wakeup_reason(struct rtw_dev *rtwdev)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	u8 reason;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	reason = rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	if (reason == RTW_WOW_RSN_RX_DEAUTH)
208c2ecf20Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx deauth\n");
218c2ecf20Sopenharmony_ci	else if (reason == RTW_WOW_RSN_DISCONNECT)
228c2ecf20Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: AP is off\n");
238c2ecf20Sopenharmony_ci	else if (reason == RTW_WOW_RSN_RX_MAGIC_PKT)
248c2ecf20Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx magic packet\n");
258c2ecf20Sopenharmony_ci	else if (reason == RTW_WOW_RSN_RX_GTK_REKEY)
268c2ecf20Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx gtk rekey\n");
278c2ecf20Sopenharmony_ci	else if (reason == RTW_WOW_RSN_RX_PTK_REKEY)
288c2ecf20Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx ptk rekey\n");
298c2ecf20Sopenharmony_ci	else if (reason == RTW_WOW_RSN_RX_PATTERN_MATCH)
308c2ecf20Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx pattern match packet\n");
318c2ecf20Sopenharmony_ci	else if (reason == RTW_WOW_RSN_RX_NLO)
328c2ecf20Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_WOW, "Rx NLO\n");
338c2ecf20Sopenharmony_ci	else
348c2ecf20Sopenharmony_ci		rtw_warn(rtwdev, "Unknown wakeup reason %x\n", reason);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void rtw_wow_pattern_write_cam(struct rtw_dev *rtwdev, u8 addr,
388c2ecf20Sopenharmony_ci				      u32 wdata)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	rtw_write32(rtwdev, REG_WKFMCAM_RWD, wdata);
418c2ecf20Sopenharmony_ci	rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 |
428c2ecf20Sopenharmony_ci		    BIT_WKFCAM_WE | BIT_WKFCAM_ADDR_V2(addr));
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	if (!check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0))
458c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to write pattern cam\n");
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void rtw_wow_pattern_write_cam_ent(struct rtw_dev *rtwdev, u8 id,
498c2ecf20Sopenharmony_ci					  struct rtw_wow_pattern *rtw_pattern)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	int i;
528c2ecf20Sopenharmony_ci	u8 addr;
538c2ecf20Sopenharmony_ci	u32 wdata;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	for (i = 0; i < RTW_MAX_PATTERN_MASK_SIZE / 4; i++) {
568c2ecf20Sopenharmony_ci		addr = (id << 3) + i;
578c2ecf20Sopenharmony_ci		wdata = rtw_pattern->mask[i * 4];
588c2ecf20Sopenharmony_ci		wdata |= rtw_pattern->mask[i * 4 + 1] << 8;
598c2ecf20Sopenharmony_ci		wdata |= rtw_pattern->mask[i * 4 + 2] << 16;
608c2ecf20Sopenharmony_ci		wdata |= rtw_pattern->mask[i * 4 + 3] << 24;
618c2ecf20Sopenharmony_ci		rtw_wow_pattern_write_cam(rtwdev, addr, wdata);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	wdata = rtw_pattern->crc;
658c2ecf20Sopenharmony_ci	addr = (id << 3) + RTW_MAX_PATTERN_MASK_SIZE / 4;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	switch (rtw_pattern->type) {
688c2ecf20Sopenharmony_ci	case RTW_PATTERN_BROADCAST:
698c2ecf20Sopenharmony_ci		wdata |= BIT_WKFMCAM_BC | BIT_WKFMCAM_VALID;
708c2ecf20Sopenharmony_ci		break;
718c2ecf20Sopenharmony_ci	case RTW_PATTERN_MULTICAST:
728c2ecf20Sopenharmony_ci		wdata |= BIT_WKFMCAM_MC | BIT_WKFMCAM_VALID;
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci	case RTW_PATTERN_UNICAST:
758c2ecf20Sopenharmony_ci		wdata |= BIT_WKFMCAM_UC | BIT_WKFMCAM_VALID;
768c2ecf20Sopenharmony_ci		break;
778c2ecf20Sopenharmony_ci	default:
788c2ecf20Sopenharmony_ci		break;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	rtw_wow_pattern_write_cam(rtwdev, addr, wdata);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci/* RTK internal CRC16 for Pattern Cam */
848c2ecf20Sopenharmony_cistatic u16 __rtw_cal_crc16(u8 data, u16 crc)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	u8 shift_in, data_bit;
878c2ecf20Sopenharmony_ci	u8 crc_bit4, crc_bit11, crc_bit15;
888c2ecf20Sopenharmony_ci	u16 crc_result;
898c2ecf20Sopenharmony_ci	int index;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	for (index = 0; index < 8; index++) {
928c2ecf20Sopenharmony_ci		crc_bit15 = ((crc & BIT(15)) ? 1 : 0);
938c2ecf20Sopenharmony_ci		data_bit = (data & (BIT(0) << index) ? 1 : 0);
948c2ecf20Sopenharmony_ci		shift_in = crc_bit15 ^ data_bit;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		crc_result = crc << 1;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		if (shift_in == 0)
998c2ecf20Sopenharmony_ci			crc_result &= (~BIT(0));
1008c2ecf20Sopenharmony_ci		else
1018c2ecf20Sopenharmony_ci			crc_result |= BIT(0);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		crc_bit11 = ((crc & BIT(11)) ? 1 : 0) ^ shift_in;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		if (crc_bit11 == 0)
1068c2ecf20Sopenharmony_ci			crc_result &= (~BIT(12));
1078c2ecf20Sopenharmony_ci		else
1088c2ecf20Sopenharmony_ci			crc_result |= BIT(12);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		crc_bit4 = ((crc & BIT(4)) ? 1 : 0) ^ shift_in;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		if (crc_bit4 == 0)
1138c2ecf20Sopenharmony_ci			crc_result &= (~BIT(5));
1148c2ecf20Sopenharmony_ci		else
1158c2ecf20Sopenharmony_ci			crc_result |= BIT(5);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		crc = crc_result;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci	return crc;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic u16 rtw_calc_crc(u8 *pdata, int length)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	u16 crc = 0xffff;
1258c2ecf20Sopenharmony_ci	int i;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	for (i = 0; i < length; i++)
1288c2ecf20Sopenharmony_ci		crc = __rtw_cal_crc16(pdata[i], crc);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* get 1' complement */
1318c2ecf20Sopenharmony_ci	return ~crc;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void rtw_wow_pattern_generate(struct rtw_dev *rtwdev,
1358c2ecf20Sopenharmony_ci				     struct rtw_vif *rtwvif,
1368c2ecf20Sopenharmony_ci				     const struct cfg80211_pkt_pattern *pkt_pattern,
1378c2ecf20Sopenharmony_ci				     struct rtw_wow_pattern *rtw_pattern)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	const u8 *mask;
1408c2ecf20Sopenharmony_ci	const u8 *pattern;
1418c2ecf20Sopenharmony_ci	u8 mask_hw[RTW_MAX_PATTERN_MASK_SIZE] = {0};
1428c2ecf20Sopenharmony_ci	u8 content[RTW_MAX_PATTERN_SIZE] = {0};
1438c2ecf20Sopenharmony_ci	u8 mac_addr[ETH_ALEN] = {0};
1448c2ecf20Sopenharmony_ci	u8 mask_len;
1458c2ecf20Sopenharmony_ci	u16 count;
1468c2ecf20Sopenharmony_ci	int len;
1478c2ecf20Sopenharmony_ci	int i;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	pattern = pkt_pattern->pattern;
1508c2ecf20Sopenharmony_ci	len = pkt_pattern->pattern_len;
1518c2ecf20Sopenharmony_ci	mask = pkt_pattern->mask;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ether_addr_copy(mac_addr, rtwvif->mac_addr);
1548c2ecf20Sopenharmony_ci	memset(rtw_pattern, 0, sizeof(*rtw_pattern));
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	mask_len = DIV_ROUND_UP(len, 8);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (is_broadcast_ether_addr(pattern))
1598c2ecf20Sopenharmony_ci		rtw_pattern->type = RTW_PATTERN_BROADCAST;
1608c2ecf20Sopenharmony_ci	else if (is_multicast_ether_addr(pattern))
1618c2ecf20Sopenharmony_ci		rtw_pattern->type = RTW_PATTERN_MULTICAST;
1628c2ecf20Sopenharmony_ci	else if (ether_addr_equal(pattern, mac_addr))
1638c2ecf20Sopenharmony_ci		rtw_pattern->type = RTW_PATTERN_UNICAST;
1648c2ecf20Sopenharmony_ci	else
1658c2ecf20Sopenharmony_ci		rtw_pattern->type = RTW_PATTERN_INVALID;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* translate mask from os to mask for hw
1688c2ecf20Sopenharmony_ci	 * pattern from OS uses 'ethenet frame', like this:
1698c2ecf20Sopenharmony_ci	 * |    6   |    6   |   2  |     20    |  Variable  |  4  |
1708c2ecf20Sopenharmony_ci	 * |--------+--------+------+-----------+------------+-----|
1718c2ecf20Sopenharmony_ci	 * |    802.3 Mac Header    | IP Header | TCP Packet | FCS |
1728c2ecf20Sopenharmony_ci	 * |   DA   |   SA   | Type |
1738c2ecf20Sopenharmony_ci	 *
1748c2ecf20Sopenharmony_ci	 * BUT, packet catched by our HW is in '802.11 frame', begin from LLC
1758c2ecf20Sopenharmony_ci	 * |     24 or 30      |    6   |   2  |     20    |  Variable  |  4  |
1768c2ecf20Sopenharmony_ci	 * |-------------------+--------+------+-----------+------------+-----|
1778c2ecf20Sopenharmony_ci	 * | 802.11 MAC Header |       LLC     | IP Header | TCP Packet | FCS |
1788c2ecf20Sopenharmony_ci	 *		       | Others | Tpye |
1798c2ecf20Sopenharmony_ci	 *
1808c2ecf20Sopenharmony_ci	 * Therefore, we need translate mask_from_OS to mask_to_hw.
1818c2ecf20Sopenharmony_ci	 * We should left-shift mask by 6 bits, then set the new bit[0~5] = 0,
1828c2ecf20Sopenharmony_ci	 * because new mask[0~5] means 'SA', but our HW packet begins from LLC,
1838c2ecf20Sopenharmony_ci	 * bit[0~5] corresponds to first 6 Bytes in LLC, they just don't match.
1848c2ecf20Sopenharmony_ci	 */
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Shift 6 bits */
1878c2ecf20Sopenharmony_ci	for (i = 0; i < mask_len - 1; i++) {
1888c2ecf20Sopenharmony_ci		mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6));
1898c2ecf20Sopenharmony_ci		mask_hw[i] |= u8_get_bits(mask[i + 1], GENMASK(5, 0)) << 2;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6));
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* Set bit 0-5 to zero */
1948c2ecf20Sopenharmony_ci	mask_hw[0] &= (~GENMASK(5, 0));
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	memcpy(rtw_pattern->mask, mask_hw, RTW_MAX_PATTERN_MASK_SIZE);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* To get the wake up pattern from the mask.
1998c2ecf20Sopenharmony_ci	 * We do not count first 12 bits which means
2008c2ecf20Sopenharmony_ci	 * DA[6] and SA[6] in the pattern to match HW design.
2018c2ecf20Sopenharmony_ci	 */
2028c2ecf20Sopenharmony_ci	count = 0;
2038c2ecf20Sopenharmony_ci	for (i = 12; i < len; i++) {
2048c2ecf20Sopenharmony_ci		if ((mask[i / 8] >> (i % 8)) & 0x01) {
2058c2ecf20Sopenharmony_ci			content[count] = pattern[i];
2068c2ecf20Sopenharmony_ci			count++;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	rtw_pattern->crc = rtw_calc_crc(content, count);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void rtw_wow_pattern_clear_cam(struct rtw_dev *rtwdev)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	bool ret;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 |
2188c2ecf20Sopenharmony_ci		    BIT_WKFCAM_CLR_V1);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ret = check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0);
2218c2ecf20Sopenharmony_ci	if (!ret)
2228c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to clean pattern cam\n");
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void rtw_wow_pattern_write(struct rtw_dev *rtwdev)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2288c2ecf20Sopenharmony_ci	struct rtw_wow_pattern *rtw_pattern = rtw_wow->patterns;
2298c2ecf20Sopenharmony_ci	int i = 0;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	for (i = 0; i < rtw_wow->pattern_cnt; i++)
2328c2ecf20Sopenharmony_ci		rtw_wow_pattern_write_cam_ent(rtwdev, i, rtw_pattern + i);
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic void rtw_wow_pattern_clear(struct rtw_dev *rtwdev)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	rtw_wow_pattern_clear_cam(rtwdev);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	rtw_wow->pattern_cnt = 0;
2428c2ecf20Sopenharmony_ci	memset(rtw_wow->patterns, 0, sizeof(rtw_wow->patterns));
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic void rtw_wow_bb_stop(struct rtw_dev *rtwdev)
2468c2ecf20Sopenharmony_ci{
2478c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	/* wait 100ms for firmware to finish TX */
2508c2ecf20Sopenharmony_ci	msleep(100);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (!rtw_read32_mask(rtwdev, REG_BCNQ_INFO, BIT_MGQ_CPU_EMPTY))
2538c2ecf20Sopenharmony_ci		rtw_warn(rtwdev, "Wrong status of MGQ_CPU empty!\n");
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	rtw_wow->txpause = rtw_read8(rtwdev, REG_TXPAUSE);
2568c2ecf20Sopenharmony_ci	rtw_write8(rtwdev, REG_TXPAUSE, 0xff);
2578c2ecf20Sopenharmony_ci	rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic void rtw_wow_bb_start(struct rtw_dev *rtwdev)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
2658c2ecf20Sopenharmony_ci	rtw_write8(rtwdev, REG_TXPAUSE, rtw_wow->txpause);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void rtw_wow_rx_dma_stop(struct rtw_dev *rtwdev)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	/* wait 100ms for HW to finish rx dma */
2718c2ecf20Sopenharmony_ci	msleep(100);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	rtw_write32_set(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (!check_hw_ready(rtwdev, REG_RXPKT_NUM, BIT_RXDMA_IDLE, 1))
2768c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to stop rx dma\n");
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void rtw_wow_rx_dma_start(struct rtw_dev *rtwdev)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int rtw_wow_check_fw_status(struct rtw_dev *rtwdev, bool wow_enable)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	int ret;
2878c2ecf20Sopenharmony_ci	u8 check;
2888c2ecf20Sopenharmony_ci	u32 check_dis;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (wow_enable) {
2918c2ecf20Sopenharmony_ci		ret = read_poll_timeout(rtw_read8, check, !check, 1000,
2928c2ecf20Sopenharmony_ci					100000, true, rtwdev,
2938c2ecf20Sopenharmony_ci					REG_WOWLAN_WAKE_REASON);
2948c2ecf20Sopenharmony_ci		if (ret)
2958c2ecf20Sopenharmony_ci			goto wow_fail;
2968c2ecf20Sopenharmony_ci	} else {
2978c2ecf20Sopenharmony_ci		ret = read_poll_timeout(rtw_read32_mask, check_dis,
2988c2ecf20Sopenharmony_ci					!check_dis, 1000, 100000, true, rtwdev,
2998c2ecf20Sopenharmony_ci					REG_FE1IMR, BIT_FS_RXDONE);
3008c2ecf20Sopenharmony_ci		if (ret)
3018c2ecf20Sopenharmony_ci			goto wow_fail;
3028c2ecf20Sopenharmony_ci		ret = read_poll_timeout(rtw_read32_mask, check_dis,
3038c2ecf20Sopenharmony_ci					!check_dis, 1000, 100000, false, rtwdev,
3048c2ecf20Sopenharmony_ci					REG_RXPKT_NUM, BIT_RW_RELEASE);
3058c2ecf20Sopenharmony_ci		if (ret)
3068c2ecf20Sopenharmony_ci			goto wow_fail;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	return 0;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ciwow_fail:
3128c2ecf20Sopenharmony_ci	rtw_err(rtwdev, "failed to check wow status %s\n",
3138c2ecf20Sopenharmony_ci		wow_enable ? "enabled" : "disabled");
3148c2ecf20Sopenharmony_ci	return -EBUSY;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic void rtw_wow_fw_security_type_iter(struct ieee80211_hw *hw,
3188c2ecf20Sopenharmony_ci					  struct ieee80211_vif *vif,
3198c2ecf20Sopenharmony_ci					  struct ieee80211_sta *sta,
3208c2ecf20Sopenharmony_ci					  struct ieee80211_key_conf *key,
3218c2ecf20Sopenharmony_ci					  void *data)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	struct rtw_fw_key_type_iter_data *iter_data = data;
3248c2ecf20Sopenharmony_ci	struct rtw_dev *rtwdev = hw->priv;
3258c2ecf20Sopenharmony_ci	u8 hw_key_type;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (vif != rtwdev->wow.wow_vif)
3288c2ecf20Sopenharmony_ci		return;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	switch (key->cipher) {
3318c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
3328c2ecf20Sopenharmony_ci		hw_key_type = RTW_CAM_WEP40;
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
3358c2ecf20Sopenharmony_ci		hw_key_type = RTW_CAM_WEP104;
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
3388c2ecf20Sopenharmony_ci		hw_key_type = RTW_CAM_TKIP;
3398c2ecf20Sopenharmony_ci		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
3428c2ecf20Sopenharmony_ci		hw_key_type = RTW_CAM_AES;
3438c2ecf20Sopenharmony_ci		key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci	default:
3468c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "Unsupported key type for wowlan mode\n");
3478c2ecf20Sopenharmony_ci		hw_key_type = 0;
3488c2ecf20Sopenharmony_ci		break;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	if (sta)
3528c2ecf20Sopenharmony_ci		iter_data->pairwise_key_type = hw_key_type;
3538c2ecf20Sopenharmony_ci	else
3548c2ecf20Sopenharmony_ci		iter_data->group_key_type = hw_key_type;
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic void rtw_wow_fw_security_type(struct rtw_dev *rtwdev)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct rtw_fw_key_type_iter_data data = {};
3608c2ecf20Sopenharmony_ci	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	data.rtwdev = rtwdev;
3638c2ecf20Sopenharmony_ci	rtw_iterate_keys(rtwdev, wow_vif,
3648c2ecf20Sopenharmony_ci			 rtw_wow_fw_security_type_iter, &data);
3658c2ecf20Sopenharmony_ci	rtw_fw_set_aoac_global_info_cmd(rtwdev, data.pairwise_key_type,
3668c2ecf20Sopenharmony_ci					data.group_key_type);
3678c2ecf20Sopenharmony_ci}
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_cistatic int rtw_wow_fw_start(struct rtw_dev *rtwdev)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	if (rtw_wow_mgd_linked(rtwdev)) {
3728c2ecf20Sopenharmony_ci		rtw_send_rsvd_page_h2c(rtwdev);
3738c2ecf20Sopenharmony_ci		rtw_wow_pattern_write(rtwdev);
3748c2ecf20Sopenharmony_ci		rtw_wow_fw_security_type(rtwdev);
3758c2ecf20Sopenharmony_ci		rtw_fw_set_disconnect_decision_cmd(rtwdev, true);
3768c2ecf20Sopenharmony_ci		rtw_fw_set_keep_alive_cmd(rtwdev, true);
3778c2ecf20Sopenharmony_ci	} else if (rtw_wow_no_link(rtwdev)) {
3788c2ecf20Sopenharmony_ci		rtw_fw_set_nlo_info(rtwdev, true);
3798c2ecf20Sopenharmony_ci		rtw_fw_update_pkt_probe_req(rtwdev, NULL);
3808c2ecf20Sopenharmony_ci		rtw_fw_channel_switch(rtwdev, true);
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	rtw_fw_set_wowlan_ctrl_cmd(rtwdev, true);
3848c2ecf20Sopenharmony_ci	rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, true);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return rtw_wow_check_fw_status(rtwdev, true);
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int rtw_wow_fw_stop(struct rtw_dev *rtwdev)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	if (rtw_wow_mgd_linked(rtwdev)) {
3928c2ecf20Sopenharmony_ci		rtw_fw_set_disconnect_decision_cmd(rtwdev, false);
3938c2ecf20Sopenharmony_ci		rtw_fw_set_keep_alive_cmd(rtwdev, false);
3948c2ecf20Sopenharmony_ci		rtw_wow_pattern_clear(rtwdev);
3958c2ecf20Sopenharmony_ci	} else if (rtw_wow_no_link(rtwdev)) {
3968c2ecf20Sopenharmony_ci		rtw_fw_channel_switch(rtwdev, false);
3978c2ecf20Sopenharmony_ci		rtw_fw_set_nlo_info(rtwdev, false);
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	rtw_fw_set_wowlan_ctrl_cmd(rtwdev, false);
4018c2ecf20Sopenharmony_ci	rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, false);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return rtw_wow_check_fw_status(rtwdev, false);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic void rtw_wow_avoid_reset_mac(struct rtw_dev *rtwdev)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	/* When resuming from wowlan mode, some hosts issue signal
4098c2ecf20Sopenharmony_ci	 * (PCIE: PREST, USB: SE0RST) to device, and lead to reset
4108c2ecf20Sopenharmony_ci	 * mac core. If it happens, the connection to AP will be lost.
4118c2ecf20Sopenharmony_ci	 * Setting REG_RSV_CTRL Register can avoid this process.
4128c2ecf20Sopenharmony_ci	 */
4138c2ecf20Sopenharmony_ci	switch (rtw_hci_type(rtwdev)) {
4148c2ecf20Sopenharmony_ci	case RTW_HCI_TYPE_PCIE:
4158c2ecf20Sopenharmony_ci	case RTW_HCI_TYPE_USB:
4168c2ecf20Sopenharmony_ci		rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6);
4178c2ecf20Sopenharmony_ci		rtw_write8(rtwdev, REG_RSV_CTRL,
4188c2ecf20Sopenharmony_ci			   BIT_WLOCK_1C_B6 | BIT_R_DIS_PRST);
4198c2ecf20Sopenharmony_ci		break;
4208c2ecf20Sopenharmony_ci	default:
4218c2ecf20Sopenharmony_ci		rtw_warn(rtwdev, "Unsupported hci type to disable reset MAC\n");
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void rtw_wow_fw_media_status_iter(void *data, struct ieee80211_sta *sta)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
4298c2ecf20Sopenharmony_ci	struct rtw_fw_media_status_iter_data *iter_data = data;
4308c2ecf20Sopenharmony_ci	struct rtw_dev *rtwdev = iter_data->rtwdev;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	rtw_fw_media_status_report(rtwdev, si->mac_id, iter_data->connect);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void rtw_wow_fw_media_status(struct rtw_dev *rtwdev, bool connect)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct rtw_fw_media_status_iter_data data;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	data.rtwdev = rtwdev;
4408c2ecf20Sopenharmony_ci	data.connect = connect;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	rtw_iterate_stas_atomic(rtwdev, rtw_wow_fw_media_status_iter, &data);
4438c2ecf20Sopenharmony_ci}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_cistatic void rtw_wow_config_pno_rsvd_page(struct rtw_dev *rtwdev,
4468c2ecf20Sopenharmony_ci					 struct rtw_vif *rtwvif)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	rtw_add_rsvd_page_pno(rtwdev, rtwvif);
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_cistatic void rtw_wow_config_linked_rsvd_page(struct rtw_dev *rtwdev,
4528c2ecf20Sopenharmony_ci					   struct rtw_vif *rtwvif)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	rtw_add_rsvd_page_sta(rtwdev, rtwvif);
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic void rtw_wow_config_rsvd_page(struct rtw_dev *rtwdev,
4588c2ecf20Sopenharmony_ci				     struct rtw_vif *rtwvif)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	rtw_remove_rsvd_page(rtwdev, rtwvif);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (rtw_wow_mgd_linked(rtwdev)) {
4638c2ecf20Sopenharmony_ci		rtw_wow_config_linked_rsvd_page(rtwdev, rtwvif);
4648c2ecf20Sopenharmony_ci	} else if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags) &&
4658c2ecf20Sopenharmony_ci		   rtw_wow_no_link(rtwdev)) {
4668c2ecf20Sopenharmony_ci		rtw_wow_config_pno_rsvd_page(rtwdev, rtwvif);
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic int rtw_wow_dl_fw_rsvd_page(struct rtw_dev *rtwdev)
4718c2ecf20Sopenharmony_ci{
4728c2ecf20Sopenharmony_ci	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
4738c2ecf20Sopenharmony_ci	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	rtw_wow_config_rsvd_page(rtwdev, rtwvif);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	return rtw_fw_download_rsvd_page(rtwdev);
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic int rtw_wow_swap_fw(struct rtw_dev *rtwdev, enum rtw_fw_type type)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct rtw_fw_state *fw;
4838c2ecf20Sopenharmony_ci	int ret;
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	switch (type) {
4868c2ecf20Sopenharmony_ci	case RTW_WOWLAN_FW:
4878c2ecf20Sopenharmony_ci		fw = &rtwdev->wow_fw;
4888c2ecf20Sopenharmony_ci		break;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	case RTW_NORMAL_FW:
4918c2ecf20Sopenharmony_ci		fw = &rtwdev->fw;
4928c2ecf20Sopenharmony_ci		break;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	default:
4958c2ecf20Sopenharmony_ci		rtw_warn(rtwdev, "unsupported firmware type to swap\n");
4968c2ecf20Sopenharmony_ci		return -ENOENT;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	ret = rtw_download_firmware(rtwdev, fw);
5008c2ecf20Sopenharmony_ci	if (ret)
5018c2ecf20Sopenharmony_ci		goto out;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	rtw_fw_send_general_info(rtwdev);
5048c2ecf20Sopenharmony_ci	rtw_fw_send_phydm_info(rtwdev);
5058c2ecf20Sopenharmony_ci	rtw_wow_fw_media_status(rtwdev, true);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ciout:
5088c2ecf20Sopenharmony_ci	return ret;
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic void rtw_wow_check_pno(struct rtw_dev *rtwdev,
5128c2ecf20Sopenharmony_ci			      struct cfg80211_sched_scan_request *nd_config)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
5158c2ecf20Sopenharmony_ci	struct rtw_pno_request *pno_req = &rtw_wow->pno_req;
5168c2ecf20Sopenharmony_ci	struct ieee80211_channel *channel;
5178c2ecf20Sopenharmony_ci	int i, size;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (!nd_config->n_match_sets || !nd_config->n_channels)
5208c2ecf20Sopenharmony_ci		goto err;
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	pno_req->match_set_cnt = nd_config->n_match_sets;
5238c2ecf20Sopenharmony_ci	size = sizeof(*pno_req->match_sets) * pno_req->match_set_cnt;
5248c2ecf20Sopenharmony_ci	pno_req->match_sets = kmemdup(nd_config->match_sets, size, GFP_KERNEL);
5258c2ecf20Sopenharmony_ci	if (!pno_req->match_sets)
5268c2ecf20Sopenharmony_ci		goto err;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	pno_req->channel_cnt = nd_config->n_channels;
5298c2ecf20Sopenharmony_ci	size = sizeof(*nd_config->channels[0]) * nd_config->n_channels;
5308c2ecf20Sopenharmony_ci	pno_req->channels = kmalloc(size, GFP_KERNEL);
5318c2ecf20Sopenharmony_ci	if (!pno_req->channels)
5328c2ecf20Sopenharmony_ci		goto channel_err;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	for (i = 0 ; i < pno_req->channel_cnt; i++) {
5358c2ecf20Sopenharmony_ci		channel = pno_req->channels + i;
5368c2ecf20Sopenharmony_ci		memcpy(channel, nd_config->channels[i], sizeof(*channel));
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	pno_req->scan_plan = *nd_config->scan_plans;
5408c2ecf20Sopenharmony_ci	pno_req->inited = true;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is enabled\n");
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	return;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_cichannel_err:
5478c2ecf20Sopenharmony_ci	kfree(pno_req->match_sets);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cierr:
5508c2ecf20Sopenharmony_ci	rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is disabled\n");
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic int rtw_wow_leave_linked_ps(struct rtw_dev *rtwdev)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
5568c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&rtwdev->watch_dog_work);
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	rtw_leave_lps(rtwdev);
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	return 0;
5618c2ecf20Sopenharmony_ci}
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_cistatic int rtw_wow_leave_no_link_ps(struct rtw_dev *rtwdev)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
5668c2ecf20Sopenharmony_ci	int ret = 0;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) {
5698c2ecf20Sopenharmony_ci		if (rtw_fw_lps_deep_mode)
5708c2ecf20Sopenharmony_ci			rtw_leave_lps_deep(rtwdev);
5718c2ecf20Sopenharmony_ci	} else {
5728c2ecf20Sopenharmony_ci		if (test_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags)) {
5738c2ecf20Sopenharmony_ci			rtw_wow->ips_enabled = true;
5748c2ecf20Sopenharmony_ci			ret = rtw_leave_ips(rtwdev);
5758c2ecf20Sopenharmony_ci			if (ret)
5768c2ecf20Sopenharmony_ci				return ret;
5778c2ecf20Sopenharmony_ci		}
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_cistatic int rtw_wow_leave_ps(struct rtw_dev *rtwdev)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	int ret = 0;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (rtw_wow_mgd_linked(rtwdev))
5888c2ecf20Sopenharmony_ci		ret = rtw_wow_leave_linked_ps(rtwdev);
5898c2ecf20Sopenharmony_ci	else if (rtw_wow_no_link(rtwdev))
5908c2ecf20Sopenharmony_ci		ret = rtw_wow_leave_no_link_ps(rtwdev);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return ret;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic int rtw_wow_restore_ps(struct rtw_dev *rtwdev)
5968c2ecf20Sopenharmony_ci{
5978c2ecf20Sopenharmony_ci	int ret = 0;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	if (rtw_wow_no_link(rtwdev) && rtwdev->wow.ips_enabled)
6008c2ecf20Sopenharmony_ci		ret = rtw_enter_ips(rtwdev);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return ret;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int rtw_wow_enter_linked_ps(struct rtw_dev *rtwdev)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
6088c2ecf20Sopenharmony_ci	struct ieee80211_vif *wow_vif = rtw_wow->wow_vif;
6098c2ecf20Sopenharmony_ci	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	rtw_enter_lps(rtwdev, rtwvif->port);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return 0;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int rtw_wow_enter_no_link_ps(struct rtw_dev *rtwdev)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	/* firmware enters deep ps by itself if supported */
6198c2ecf20Sopenharmony_ci	set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	return 0;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic int rtw_wow_enter_ps(struct rtw_dev *rtwdev)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	int ret = 0;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (rtw_wow_mgd_linked(rtwdev))
6298c2ecf20Sopenharmony_ci		ret = rtw_wow_enter_linked_ps(rtwdev);
6308c2ecf20Sopenharmony_ci	else if (rtw_wow_no_link(rtwdev) && rtw_fw_lps_deep_mode)
6318c2ecf20Sopenharmony_ci		ret = rtw_wow_enter_no_link_ps(rtwdev);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	return ret;
6348c2ecf20Sopenharmony_ci}
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_cistatic void rtw_wow_stop_trx(struct rtw_dev *rtwdev)
6378c2ecf20Sopenharmony_ci{
6388c2ecf20Sopenharmony_ci	rtw_wow_bb_stop(rtwdev);
6398c2ecf20Sopenharmony_ci	rtw_wow_rx_dma_stop(rtwdev);
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic int rtw_wow_start(struct rtw_dev *rtwdev)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	int ret;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	ret = rtw_wow_fw_start(rtwdev);
6478c2ecf20Sopenharmony_ci	if (ret)
6488c2ecf20Sopenharmony_ci		goto out;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	rtw_hci_stop(rtwdev);
6518c2ecf20Sopenharmony_ci	rtw_wow_bb_start(rtwdev);
6528c2ecf20Sopenharmony_ci	rtw_wow_avoid_reset_mac(rtwdev);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ciout:
6558c2ecf20Sopenharmony_ci	return ret;
6568c2ecf20Sopenharmony_ci}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_cistatic int rtw_wow_enable(struct rtw_dev *rtwdev)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	int ret = 0;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	rtw_wow_stop_trx(rtwdev);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	ret = rtw_wow_swap_fw(rtwdev, RTW_WOWLAN_FW);
6658c2ecf20Sopenharmony_ci	if (ret) {
6668c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to swap wow fw\n");
6678c2ecf20Sopenharmony_ci		goto error;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	set_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	ret = rtw_wow_dl_fw_rsvd_page(rtwdev);
6738c2ecf20Sopenharmony_ci	if (ret) {
6748c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to download wowlan rsvd page\n");
6758c2ecf20Sopenharmony_ci		goto error;
6768c2ecf20Sopenharmony_ci	}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	ret = rtw_wow_start(rtwdev);
6798c2ecf20Sopenharmony_ci	if (ret) {
6808c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to start wow\n");
6818c2ecf20Sopenharmony_ci		goto error;
6828c2ecf20Sopenharmony_ci	}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci	return ret;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_cierror:
6878c2ecf20Sopenharmony_ci	clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
6888c2ecf20Sopenharmony_ci	return ret;
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_cistatic int rtw_wow_stop(struct rtw_dev *rtwdev)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	int ret;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* some HCI related registers will be reset after resume,
6968c2ecf20Sopenharmony_ci	 * need to set them again.
6978c2ecf20Sopenharmony_ci	 */
6988c2ecf20Sopenharmony_ci	ret = rtw_hci_setup(rtwdev);
6998c2ecf20Sopenharmony_ci	if (ret) {
7008c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to setup hci\n");
7018c2ecf20Sopenharmony_ci		return ret;
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	ret = rtw_hci_start(rtwdev);
7058c2ecf20Sopenharmony_ci	if (ret) {
7068c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to start hci\n");
7078c2ecf20Sopenharmony_ci		return ret;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	ret = rtw_wow_fw_stop(rtwdev);
7118c2ecf20Sopenharmony_ci	if (ret)
7128c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to stop wowlan fw\n");
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	rtw_wow_bb_stop(rtwdev);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	return ret;
7178c2ecf20Sopenharmony_ci}
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_cistatic void rtw_wow_resume_trx(struct rtw_dev *rtwdev)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	rtw_wow_rx_dma_start(rtwdev);
7228c2ecf20Sopenharmony_ci	rtw_wow_bb_start(rtwdev);
7238c2ecf20Sopenharmony_ci	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work,
7248c2ecf20Sopenharmony_ci				     RTW_WATCH_DOG_DELAY_TIME);
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic int rtw_wow_disable(struct rtw_dev *rtwdev)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	int ret;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	ret = rtw_wow_stop(rtwdev);
7348c2ecf20Sopenharmony_ci	if (ret) {
7358c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to stop wow\n");
7368c2ecf20Sopenharmony_ci		goto out;
7378c2ecf20Sopenharmony_ci	}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci	ret = rtw_wow_swap_fw(rtwdev, RTW_NORMAL_FW);
7408c2ecf20Sopenharmony_ci	if (ret) {
7418c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to swap normal fw\n");
7428c2ecf20Sopenharmony_ci		goto out;
7438c2ecf20Sopenharmony_ci	}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	ret = rtw_wow_dl_fw_rsvd_page(rtwdev);
7468c2ecf20Sopenharmony_ci	if (ret)
7478c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to download normal rsvd page\n");
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ciout:
7508c2ecf20Sopenharmony_ci	rtw_wow_resume_trx(rtwdev);
7518c2ecf20Sopenharmony_ci	return ret;
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic void rtw_wow_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	struct rtw_dev *rtwdev = data;
7578c2ecf20Sopenharmony_ci	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
7588c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	/* Current wowlan function support setting of only one STATION vif.
7618c2ecf20Sopenharmony_ci	 * So when one suitable vif is found, stop the iteration.
7628c2ecf20Sopenharmony_ci	 */
7638c2ecf20Sopenharmony_ci	if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION)
7648c2ecf20Sopenharmony_ci		return;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	switch (rtwvif->net_type) {
7678c2ecf20Sopenharmony_ci	case RTW_NET_MGD_LINKED:
7688c2ecf20Sopenharmony_ci		rtw_wow->wow_vif = vif;
7698c2ecf20Sopenharmony_ci		break;
7708c2ecf20Sopenharmony_ci	case RTW_NET_NO_LINK:
7718c2ecf20Sopenharmony_ci		if (rtw_wow->pno_req.inited)
7728c2ecf20Sopenharmony_ci			rtwdev->wow.wow_vif = vif;
7738c2ecf20Sopenharmony_ci		break;
7748c2ecf20Sopenharmony_ci	default:
7758c2ecf20Sopenharmony_ci		break;
7768c2ecf20Sopenharmony_ci	}
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_cistatic int rtw_wow_set_wakeups(struct rtw_dev *rtwdev,
7808c2ecf20Sopenharmony_ci			       struct cfg80211_wowlan *wowlan)
7818c2ecf20Sopenharmony_ci{
7828c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
7838c2ecf20Sopenharmony_ci	struct rtw_wow_pattern *rtw_patterns = rtw_wow->patterns;
7848c2ecf20Sopenharmony_ci	struct rtw_vif *rtwvif;
7858c2ecf20Sopenharmony_ci	int i;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	if (wowlan->disconnect)
7888c2ecf20Sopenharmony_ci		set_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags);
7898c2ecf20Sopenharmony_ci	if (wowlan->magic_pkt)
7908c2ecf20Sopenharmony_ci		set_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags);
7918c2ecf20Sopenharmony_ci	if (wowlan->gtk_rekey_failure)
7928c2ecf20Sopenharmony_ci		set_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	if (wowlan->nd_config)
7958c2ecf20Sopenharmony_ci		rtw_wow_check_pno(rtwdev, wowlan->nd_config);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	rtw_iterate_vifs_atomic(rtwdev, rtw_wow_vif_iter, rtwdev);
7988c2ecf20Sopenharmony_ci	if (!rtw_wow->wow_vif)
7998c2ecf20Sopenharmony_ci		return -EPERM;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci	rtwvif = (struct rtw_vif *)rtw_wow->wow_vif->drv_priv;
8028c2ecf20Sopenharmony_ci	if (wowlan->n_patterns && wowlan->patterns) {
8038c2ecf20Sopenharmony_ci		rtw_wow->pattern_cnt = wowlan->n_patterns;
8048c2ecf20Sopenharmony_ci		for (i = 0; i < wowlan->n_patterns; i++)
8058c2ecf20Sopenharmony_ci			rtw_wow_pattern_generate(rtwdev, rtwvif,
8068c2ecf20Sopenharmony_ci						 wowlan->patterns + i,
8078c2ecf20Sopenharmony_ci						 rtw_patterns + i);
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	return 0;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic void rtw_wow_clear_wakeups(struct rtw_dev *rtwdev)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
8168c2ecf20Sopenharmony_ci	struct rtw_pno_request *pno_req = &rtw_wow->pno_req;
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	if (pno_req->inited) {
8198c2ecf20Sopenharmony_ci		kfree(pno_req->channels);
8208c2ecf20Sopenharmony_ci		kfree(pno_req->match_sets);
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	memset(rtw_wow, 0, sizeof(rtwdev->wow));
8248c2ecf20Sopenharmony_ci}
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ciint rtw_wow_suspend(struct rtw_dev *rtwdev, struct cfg80211_wowlan *wowlan)
8278c2ecf20Sopenharmony_ci{
8288c2ecf20Sopenharmony_ci	int ret = 0;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	ret = rtw_wow_set_wakeups(rtwdev, wowlan);
8318c2ecf20Sopenharmony_ci	if (ret) {
8328c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to set wakeup event\n");
8338c2ecf20Sopenharmony_ci		goto out;
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	ret = rtw_wow_leave_ps(rtwdev);
8378c2ecf20Sopenharmony_ci	if (ret) {
8388c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to leave ps from normal mode\n");
8398c2ecf20Sopenharmony_ci		goto out;
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	ret = rtw_wow_enable(rtwdev);
8438c2ecf20Sopenharmony_ci	if (ret) {
8448c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to enable wow\n");
8458c2ecf20Sopenharmony_ci		rtw_wow_restore_ps(rtwdev);
8468c2ecf20Sopenharmony_ci		goto out;
8478c2ecf20Sopenharmony_ci	}
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	ret = rtw_wow_enter_ps(rtwdev);
8508c2ecf20Sopenharmony_ci	if (ret)
8518c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to enter ps for wow\n");
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ciout:
8548c2ecf20Sopenharmony_ci	return ret;
8558c2ecf20Sopenharmony_ci}
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ciint rtw_wow_resume(struct rtw_dev *rtwdev)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	int ret;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	/* If wowlan mode is not enabled, do nothing */
8628c2ecf20Sopenharmony_ci	if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) {
8638c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "wow is not enabled\n");
8648c2ecf20Sopenharmony_ci		ret = -EPERM;
8658c2ecf20Sopenharmony_ci		goto out;
8668c2ecf20Sopenharmony_ci	}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	ret = rtw_wow_leave_ps(rtwdev);
8698c2ecf20Sopenharmony_ci	if (ret) {
8708c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to leave ps from wowlan mode\n");
8718c2ecf20Sopenharmony_ci		goto out;
8728c2ecf20Sopenharmony_ci	}
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_ci	rtw_wow_show_wakeup_reason(rtwdev);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	ret = rtw_wow_disable(rtwdev);
8778c2ecf20Sopenharmony_ci	if (ret) {
8788c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to disable wow\n");
8798c2ecf20Sopenharmony_ci		goto out;
8808c2ecf20Sopenharmony_ci	}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	ret = rtw_wow_restore_ps(rtwdev);
8838c2ecf20Sopenharmony_ci	if (ret)
8848c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to restore ps to normal mode\n");
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ciout:
8878c2ecf20Sopenharmony_ci	rtw_wow_clear_wakeups(rtwdev);
8888c2ecf20Sopenharmony_ci	return ret;
8898c2ecf20Sopenharmony_ci}
890