144bc17f7SChin-Yen Lee // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
244bc17f7SChin-Yen Lee /* Copyright(c) 2018-2019  Realtek Corporation
344bc17f7SChin-Yen Lee  */
444bc17f7SChin-Yen Lee 
544bc17f7SChin-Yen Lee #include "main.h"
644bc17f7SChin-Yen Lee #include "fw.h"
744bc17f7SChin-Yen Lee #include "wow.h"
844bc17f7SChin-Yen Lee #include "reg.h"
944bc17f7SChin-Yen Lee #include "debug.h"
1044bc17f7SChin-Yen Lee #include "mac.h"
1144bc17f7SChin-Yen Lee #include "ps.h"
1244bc17f7SChin-Yen Lee 
rtw_wow_show_wakeup_reason(struct rtw_dev * rtwdev)1344bc17f7SChin-Yen Lee static void rtw_wow_show_wakeup_reason(struct rtw_dev *rtwdev)
1444bc17f7SChin-Yen Lee {
154bac10f2SChin-Yen Lee 	struct cfg80211_wowlan_nd_info nd_info;
164bac10f2SChin-Yen Lee 	struct cfg80211_wowlan_wakeup wakeup = {
174bac10f2SChin-Yen Lee 		.pattern_idx = -1,
184bac10f2SChin-Yen Lee 	};
1944bc17f7SChin-Yen Lee 	u8 reason;
2044bc17f7SChin-Yen Lee 
2144bc17f7SChin-Yen Lee 	reason = rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON);
2244bc17f7SChin-Yen Lee 
234bac10f2SChin-Yen Lee 	switch (reason) {
244bac10f2SChin-Yen Lee 	case RTW_WOW_RSN_RX_DEAUTH:
254bac10f2SChin-Yen Lee 		wakeup.disconnect = true;
2644bc17f7SChin-Yen Lee 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx deauth\n");
274bac10f2SChin-Yen Lee 		break;
284bac10f2SChin-Yen Lee 	case RTW_WOW_RSN_DISCONNECT:
294bac10f2SChin-Yen Lee 		wakeup.disconnect = true;
3044bc17f7SChin-Yen Lee 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: AP is off\n");
314bac10f2SChin-Yen Lee 		break;
324bac10f2SChin-Yen Lee 	case RTW_WOW_RSN_RX_MAGIC_PKT:
334bac10f2SChin-Yen Lee 		wakeup.magic_pkt = true;
3444bc17f7SChin-Yen Lee 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx magic packet\n");
354bac10f2SChin-Yen Lee 		break;
364bac10f2SChin-Yen Lee 	case RTW_WOW_RSN_RX_GTK_REKEY:
374bac10f2SChin-Yen Lee 		wakeup.gtk_rekey_failure = true;
3844bc17f7SChin-Yen Lee 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx gtk rekey\n");
394bac10f2SChin-Yen Lee 		break;
404bac10f2SChin-Yen Lee 	case RTW_WOW_RSN_RX_PATTERN_MATCH:
414bac10f2SChin-Yen Lee 		/* Current firmware and driver don't report pattern index
424bac10f2SChin-Yen Lee 		 * Use pattern_idx to 0 defaultly.
434bac10f2SChin-Yen Lee 		 */
444bac10f2SChin-Yen Lee 		wakeup.pattern_idx = 0;
45e3e400dfSChin-Yen Lee 		rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx pattern match packet\n");
464bac10f2SChin-Yen Lee 		break;
474bac10f2SChin-Yen Lee 	case RTW_WOW_RSN_RX_NLO:
484bac10f2SChin-Yen Lee 		/* Current firmware and driver don't report ssid index.
494bac10f2SChin-Yen Lee 		 * Use 0 for n_matches based on its comment.
504bac10f2SChin-Yen Lee 		 */
514bac10f2SChin-Yen Lee 		nd_info.n_matches = 0;
524bac10f2SChin-Yen Lee 		wakeup.net_detect = &nd_info;
53b6c12908SChin-Yen Lee 		rtw_dbg(rtwdev, RTW_DBG_WOW, "Rx NLO\n");
544bac10f2SChin-Yen Lee 		break;
554bac10f2SChin-Yen Lee 	default:
5644bc17f7SChin-Yen Lee 		rtw_warn(rtwdev, "Unknown wakeup reason %x\n", reason);
574bac10f2SChin-Yen Lee 		ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, NULL,
584bac10f2SChin-Yen Lee 					       GFP_KERNEL);
594bac10f2SChin-Yen Lee 		return;
604bac10f2SChin-Yen Lee 	}
614bac10f2SChin-Yen Lee 	ieee80211_report_wowlan_wakeup(rtwdev->wow.wow_vif, &wakeup,
624bac10f2SChin-Yen Lee 				       GFP_KERNEL);
6344bc17f7SChin-Yen Lee }
6444bc17f7SChin-Yen Lee 
rtw_wow_pattern_write_cam(struct rtw_dev * rtwdev,u8 addr,u32 wdata)65e3e400dfSChin-Yen Lee static void rtw_wow_pattern_write_cam(struct rtw_dev *rtwdev, u8 addr,
66e3e400dfSChin-Yen Lee 				      u32 wdata)
67e3e400dfSChin-Yen Lee {
68e3e400dfSChin-Yen Lee 	rtw_write32(rtwdev, REG_WKFMCAM_RWD, wdata);
69e3e400dfSChin-Yen Lee 	rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 |
70e3e400dfSChin-Yen Lee 		    BIT_WKFCAM_WE | BIT_WKFCAM_ADDR_V2(addr));
71e3e400dfSChin-Yen Lee 
72e3e400dfSChin-Yen Lee 	if (!check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0))
73e3e400dfSChin-Yen Lee 		rtw_err(rtwdev, "failed to write pattern cam\n");
74e3e400dfSChin-Yen Lee }
75e3e400dfSChin-Yen Lee 
rtw_wow_pattern_write_cam_ent(struct rtw_dev * rtwdev,u8 id,struct rtw_wow_pattern * rtw_pattern)76e3e400dfSChin-Yen Lee static void rtw_wow_pattern_write_cam_ent(struct rtw_dev *rtwdev, u8 id,
77e3e400dfSChin-Yen Lee 					  struct rtw_wow_pattern *rtw_pattern)
78e3e400dfSChin-Yen Lee {
79e3e400dfSChin-Yen Lee 	int i;
80e3e400dfSChin-Yen Lee 	u8 addr;
81e3e400dfSChin-Yen Lee 	u32 wdata;
82e3e400dfSChin-Yen Lee 
83e3e400dfSChin-Yen Lee 	for (i = 0; i < RTW_MAX_PATTERN_MASK_SIZE / 4; i++) {
84e3e400dfSChin-Yen Lee 		addr = (id << 3) + i;
85e3e400dfSChin-Yen Lee 		wdata = rtw_pattern->mask[i * 4];
86e3e400dfSChin-Yen Lee 		wdata |= rtw_pattern->mask[i * 4 + 1] << 8;
87e3e400dfSChin-Yen Lee 		wdata |= rtw_pattern->mask[i * 4 + 2] << 16;
88e3e400dfSChin-Yen Lee 		wdata |= rtw_pattern->mask[i * 4 + 3] << 24;
89e3e400dfSChin-Yen Lee 		rtw_wow_pattern_write_cam(rtwdev, addr, wdata);
90e3e400dfSChin-Yen Lee 	}
91e3e400dfSChin-Yen Lee 
92e3e400dfSChin-Yen Lee 	wdata = rtw_pattern->crc;
93e3e400dfSChin-Yen Lee 	addr = (id << 3) + RTW_MAX_PATTERN_MASK_SIZE / 4;
94e3e400dfSChin-Yen Lee 
95e3e400dfSChin-Yen Lee 	switch (rtw_pattern->type) {
96e3e400dfSChin-Yen Lee 	case RTW_PATTERN_BROADCAST:
97e3e400dfSChin-Yen Lee 		wdata |= BIT_WKFMCAM_BC | BIT_WKFMCAM_VALID;
98e3e400dfSChin-Yen Lee 		break;
99e3e400dfSChin-Yen Lee 	case RTW_PATTERN_MULTICAST:
100e3e400dfSChin-Yen Lee 		wdata |= BIT_WKFMCAM_MC | BIT_WKFMCAM_VALID;
101e3e400dfSChin-Yen Lee 		break;
102e3e400dfSChin-Yen Lee 	case RTW_PATTERN_UNICAST:
103e3e400dfSChin-Yen Lee 		wdata |= BIT_WKFMCAM_UC | BIT_WKFMCAM_VALID;
104e3e400dfSChin-Yen Lee 		break;
105e3e400dfSChin-Yen Lee 	default:
106e3e400dfSChin-Yen Lee 		break;
107e3e400dfSChin-Yen Lee 	}
108e3e400dfSChin-Yen Lee 	rtw_wow_pattern_write_cam(rtwdev, addr, wdata);
109e3e400dfSChin-Yen Lee }
110e3e400dfSChin-Yen Lee 
111e3e400dfSChin-Yen Lee /* RTK internal CRC16 for Pattern Cam */
__rtw_cal_crc16(u8 data,u16 crc)112e3e400dfSChin-Yen Lee static u16 __rtw_cal_crc16(u8 data, u16 crc)
113e3e400dfSChin-Yen Lee {
114e3e400dfSChin-Yen Lee 	u8 shift_in, data_bit;
115e3e400dfSChin-Yen Lee 	u8 crc_bit4, crc_bit11, crc_bit15;
116e3e400dfSChin-Yen Lee 	u16 crc_result;
117e3e400dfSChin-Yen Lee 	int index;
118e3e400dfSChin-Yen Lee 
119e3e400dfSChin-Yen Lee 	for (index = 0; index < 8; index++) {
120e3e400dfSChin-Yen Lee 		crc_bit15 = ((crc & BIT(15)) ? 1 : 0);
121e3e400dfSChin-Yen Lee 		data_bit = (data & (BIT(0) << index) ? 1 : 0);
122e3e400dfSChin-Yen Lee 		shift_in = crc_bit15 ^ data_bit;
123e3e400dfSChin-Yen Lee 
124e3e400dfSChin-Yen Lee 		crc_result = crc << 1;
125e3e400dfSChin-Yen Lee 
126e3e400dfSChin-Yen Lee 		if (shift_in == 0)
127e3e400dfSChin-Yen Lee 			crc_result &= (~BIT(0));
128e3e400dfSChin-Yen Lee 		else
129e3e400dfSChin-Yen Lee 			crc_result |= BIT(0);
130e3e400dfSChin-Yen Lee 
131e3e400dfSChin-Yen Lee 		crc_bit11 = ((crc & BIT(11)) ? 1 : 0) ^ shift_in;
132e3e400dfSChin-Yen Lee 
133e3e400dfSChin-Yen Lee 		if (crc_bit11 == 0)
134e3e400dfSChin-Yen Lee 			crc_result &= (~BIT(12));
135e3e400dfSChin-Yen Lee 		else
136e3e400dfSChin-Yen Lee 			crc_result |= BIT(12);
137e3e400dfSChin-Yen Lee 
138e3e400dfSChin-Yen Lee 		crc_bit4 = ((crc & BIT(4)) ? 1 : 0) ^ shift_in;
139e3e400dfSChin-Yen Lee 
140e3e400dfSChin-Yen Lee 		if (crc_bit4 == 0)
141e3e400dfSChin-Yen Lee 			crc_result &= (~BIT(5));
142e3e400dfSChin-Yen Lee 		else
143e3e400dfSChin-Yen Lee 			crc_result |= BIT(5);
144e3e400dfSChin-Yen Lee 
145e3e400dfSChin-Yen Lee 		crc = crc_result;
146e3e400dfSChin-Yen Lee 	}
147e3e400dfSChin-Yen Lee 	return crc;
148e3e400dfSChin-Yen Lee }
149e3e400dfSChin-Yen Lee 
rtw_calc_crc(u8 * pdata,int length)150e3e400dfSChin-Yen Lee static u16 rtw_calc_crc(u8 *pdata, int length)
151e3e400dfSChin-Yen Lee {
152e3e400dfSChin-Yen Lee 	u16 crc = 0xffff;
153e3e400dfSChin-Yen Lee 	int i;
154e3e400dfSChin-Yen Lee 
155e3e400dfSChin-Yen Lee 	for (i = 0; i < length; i++)
156e3e400dfSChin-Yen Lee 		crc = __rtw_cal_crc16(pdata[i], crc);
157e3e400dfSChin-Yen Lee 
158e3e400dfSChin-Yen Lee 	/* get 1' complement */
159e3e400dfSChin-Yen Lee 	return ~crc;
160e3e400dfSChin-Yen Lee }
161e3e400dfSChin-Yen Lee 
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)162e3e400dfSChin-Yen Lee static void rtw_wow_pattern_generate(struct rtw_dev *rtwdev,
163e3e400dfSChin-Yen Lee 				     struct rtw_vif *rtwvif,
164e3e400dfSChin-Yen Lee 				     const struct cfg80211_pkt_pattern *pkt_pattern,
165e3e400dfSChin-Yen Lee 				     struct rtw_wow_pattern *rtw_pattern)
166e3e400dfSChin-Yen Lee {
167e3e400dfSChin-Yen Lee 	const u8 *mask;
168e3e400dfSChin-Yen Lee 	const u8 *pattern;
169e3e400dfSChin-Yen Lee 	u8 mask_hw[RTW_MAX_PATTERN_MASK_SIZE] = {0};
170e3e400dfSChin-Yen Lee 	u8 content[RTW_MAX_PATTERN_SIZE] = {0};
171e3e400dfSChin-Yen Lee 	u8 mac_addr[ETH_ALEN] = {0};
172e3e400dfSChin-Yen Lee 	u8 mask_len;
173e3e400dfSChin-Yen Lee 	u16 count;
174e3e400dfSChin-Yen Lee 	int len;
175e3e400dfSChin-Yen Lee 	int i;
176e3e400dfSChin-Yen Lee 
177e3e400dfSChin-Yen Lee 	pattern = pkt_pattern->pattern;
178e3e400dfSChin-Yen Lee 	len = pkt_pattern->pattern_len;
179e3e400dfSChin-Yen Lee 	mask = pkt_pattern->mask;
180e3e400dfSChin-Yen Lee 
181e3e400dfSChin-Yen Lee 	ether_addr_copy(mac_addr, rtwvif->mac_addr);
182e3e400dfSChin-Yen Lee 	memset(rtw_pattern, 0, sizeof(*rtw_pattern));
183e3e400dfSChin-Yen Lee 
184e3e400dfSChin-Yen Lee 	mask_len = DIV_ROUND_UP(len, 8);
185e3e400dfSChin-Yen Lee 
186e3e400dfSChin-Yen Lee 	if (is_broadcast_ether_addr(pattern))
187e3e400dfSChin-Yen Lee 		rtw_pattern->type = RTW_PATTERN_BROADCAST;
188e3e400dfSChin-Yen Lee 	else if (is_multicast_ether_addr(pattern))
189e3e400dfSChin-Yen Lee 		rtw_pattern->type = RTW_PATTERN_MULTICAST;
190e3e400dfSChin-Yen Lee 	else if (ether_addr_equal(pattern, mac_addr))
191e3e400dfSChin-Yen Lee 		rtw_pattern->type = RTW_PATTERN_UNICAST;
192e3e400dfSChin-Yen Lee 	else
193e3e400dfSChin-Yen Lee 		rtw_pattern->type = RTW_PATTERN_INVALID;
194e3e400dfSChin-Yen Lee 
195e3e400dfSChin-Yen Lee 	/* translate mask from os to mask for hw
196e3e400dfSChin-Yen Lee 	 * pattern from OS uses 'ethenet frame', like this:
197e3e400dfSChin-Yen Lee 	 * |    6   |    6   |   2  |     20    |  Variable  |  4  |
198e3e400dfSChin-Yen Lee 	 * |--------+--------+------+-----------+------------+-----|
199e3e400dfSChin-Yen Lee 	 * |    802.3 Mac Header    | IP Header | TCP Packet | FCS |
200e3e400dfSChin-Yen Lee 	 * |   DA   |   SA   | Type |
201e3e400dfSChin-Yen Lee 	 *
202e3e400dfSChin-Yen Lee 	 * BUT, packet catched by our HW is in '802.11 frame', begin from LLC
203e3e400dfSChin-Yen Lee 	 * |     24 or 30      |    6   |   2  |     20    |  Variable  |  4  |
204e3e400dfSChin-Yen Lee 	 * |-------------------+--------+------+-----------+------------+-----|
205e3e400dfSChin-Yen Lee 	 * | 802.11 MAC Header |       LLC     | IP Header | TCP Packet | FCS |
206e3e400dfSChin-Yen Lee 	 *		       | Others | Tpye |
207e3e400dfSChin-Yen Lee 	 *
208e3e400dfSChin-Yen Lee 	 * Therefore, we need translate mask_from_OS to mask_to_hw.
209e3e400dfSChin-Yen Lee 	 * We should left-shift mask by 6 bits, then set the new bit[0~5] = 0,
210e3e400dfSChin-Yen Lee 	 * because new mask[0~5] means 'SA', but our HW packet begins from LLC,
211e3e400dfSChin-Yen Lee 	 * bit[0~5] corresponds to first 6 Bytes in LLC, they just don't match.
212e3e400dfSChin-Yen Lee 	 */
213e3e400dfSChin-Yen Lee 
214e3e400dfSChin-Yen Lee 	/* Shift 6 bits */
215e3e400dfSChin-Yen Lee 	for (i = 0; i < mask_len - 1; i++) {
216e3e400dfSChin-Yen Lee 		mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6));
217e3e400dfSChin-Yen Lee 		mask_hw[i] |= u8_get_bits(mask[i + 1], GENMASK(5, 0)) << 2;
218e3e400dfSChin-Yen Lee 	}
219e3e400dfSChin-Yen Lee 	mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6));
220e3e400dfSChin-Yen Lee 
221e3e400dfSChin-Yen Lee 	/* Set bit 0-5 to zero */
222e3e400dfSChin-Yen Lee 	mask_hw[0] &= (~GENMASK(5, 0));
223e3e400dfSChin-Yen Lee 
224e3e400dfSChin-Yen Lee 	memcpy(rtw_pattern->mask, mask_hw, RTW_MAX_PATTERN_MASK_SIZE);
225e3e400dfSChin-Yen Lee 
226e3e400dfSChin-Yen Lee 	/* To get the wake up pattern from the mask.
227e3e400dfSChin-Yen Lee 	 * We do not count first 12 bits which means
228e3e400dfSChin-Yen Lee 	 * DA[6] and SA[6] in the pattern to match HW design.
229e3e400dfSChin-Yen Lee 	 */
230e3e400dfSChin-Yen Lee 	count = 0;
231e3e400dfSChin-Yen Lee 	for (i = 12; i < len; i++) {
232e3e400dfSChin-Yen Lee 		if ((mask[i / 8] >> (i % 8)) & 0x01) {
233e3e400dfSChin-Yen Lee 			content[count] = pattern[i];
234e3e400dfSChin-Yen Lee 			count++;
235e3e400dfSChin-Yen Lee 		}
236e3e400dfSChin-Yen Lee 	}
237e3e400dfSChin-Yen Lee 
238e3e400dfSChin-Yen Lee 	rtw_pattern->crc = rtw_calc_crc(content, count);
239e3e400dfSChin-Yen Lee }
240e3e400dfSChin-Yen Lee 
rtw_wow_pattern_clear_cam(struct rtw_dev * rtwdev)241e3e400dfSChin-Yen Lee static void rtw_wow_pattern_clear_cam(struct rtw_dev *rtwdev)
242e3e400dfSChin-Yen Lee {
243e3e400dfSChin-Yen Lee 	bool ret;
244e3e400dfSChin-Yen Lee 
245e3e400dfSChin-Yen Lee 	rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 |
246e3e400dfSChin-Yen Lee 		    BIT_WKFCAM_CLR_V1);
247e3e400dfSChin-Yen Lee 
248e3e400dfSChin-Yen Lee 	ret = check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0);
249e3e400dfSChin-Yen Lee 	if (!ret)
250e3e400dfSChin-Yen Lee 		rtw_err(rtwdev, "failed to clean pattern cam\n");
251e3e400dfSChin-Yen Lee }
252e3e400dfSChin-Yen Lee 
rtw_wow_pattern_write(struct rtw_dev * rtwdev)253e3e400dfSChin-Yen Lee static void rtw_wow_pattern_write(struct rtw_dev *rtwdev)
254e3e400dfSChin-Yen Lee {
255e3e400dfSChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
256e3e400dfSChin-Yen Lee 	struct rtw_wow_pattern *rtw_pattern = rtw_wow->patterns;
257e3e400dfSChin-Yen Lee 	int i = 0;
258e3e400dfSChin-Yen Lee 
259e3e400dfSChin-Yen Lee 	for (i = 0; i < rtw_wow->pattern_cnt; i++)
260e3e400dfSChin-Yen Lee 		rtw_wow_pattern_write_cam_ent(rtwdev, i, rtw_pattern + i);
261e3e400dfSChin-Yen Lee }
262e3e400dfSChin-Yen Lee 
rtw_wow_pattern_clear(struct rtw_dev * rtwdev)263e3e400dfSChin-Yen Lee static void rtw_wow_pattern_clear(struct rtw_dev *rtwdev)
264e3e400dfSChin-Yen Lee {
265e3e400dfSChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
266e3e400dfSChin-Yen Lee 
267e3e400dfSChin-Yen Lee 	rtw_wow_pattern_clear_cam(rtwdev);
268e3e400dfSChin-Yen Lee 
269e3e400dfSChin-Yen Lee 	rtw_wow->pattern_cnt = 0;
270e3e400dfSChin-Yen Lee 	memset(rtw_wow->patterns, 0, sizeof(rtw_wow->patterns));
271e3e400dfSChin-Yen Lee }
272e3e400dfSChin-Yen Lee 
rtw_wow_bb_stop(struct rtw_dev * rtwdev)27344bc17f7SChin-Yen Lee static void rtw_wow_bb_stop(struct rtw_dev *rtwdev)
27444bc17f7SChin-Yen Lee {
27544bc17f7SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
27644bc17f7SChin-Yen Lee 
27744bc17f7SChin-Yen Lee 	/* wait 100ms for firmware to finish TX */
27844bc17f7SChin-Yen Lee 	msleep(100);
27944bc17f7SChin-Yen Lee 
28044bc17f7SChin-Yen Lee 	if (!rtw_read32_mask(rtwdev, REG_BCNQ_INFO, BIT_MGQ_CPU_EMPTY))
28144bc17f7SChin-Yen Lee 		rtw_warn(rtwdev, "Wrong status of MGQ_CPU empty!\n");
28244bc17f7SChin-Yen Lee 
28344bc17f7SChin-Yen Lee 	rtw_wow->txpause = rtw_read8(rtwdev, REG_TXPAUSE);
28444bc17f7SChin-Yen Lee 	rtw_write8(rtwdev, REG_TXPAUSE, 0xff);
28544bc17f7SChin-Yen Lee 	rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
28644bc17f7SChin-Yen Lee }
28744bc17f7SChin-Yen Lee 
rtw_wow_bb_start(struct rtw_dev * rtwdev)28844bc17f7SChin-Yen Lee static void rtw_wow_bb_start(struct rtw_dev *rtwdev)
28944bc17f7SChin-Yen Lee {
29044bc17f7SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
29144bc17f7SChin-Yen Lee 
29244bc17f7SChin-Yen Lee 	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB);
29344bc17f7SChin-Yen Lee 	rtw_write8(rtwdev, REG_TXPAUSE, rtw_wow->txpause);
29444bc17f7SChin-Yen Lee }
29544bc17f7SChin-Yen Lee 
rtw_wow_rx_dma_stop(struct rtw_dev * rtwdev)29644bc17f7SChin-Yen Lee static void rtw_wow_rx_dma_stop(struct rtw_dev *rtwdev)
29744bc17f7SChin-Yen Lee {
29844bc17f7SChin-Yen Lee 	/* wait 100ms for HW to finish rx dma */
29944bc17f7SChin-Yen Lee 	msleep(100);
30044bc17f7SChin-Yen Lee 
30144bc17f7SChin-Yen Lee 	rtw_write32_set(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE);
30244bc17f7SChin-Yen Lee 
30344bc17f7SChin-Yen Lee 	if (!check_hw_ready(rtwdev, REG_RXPKT_NUM, BIT_RXDMA_IDLE, 1))
30444bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to stop rx dma\n");
30544bc17f7SChin-Yen Lee }
30644bc17f7SChin-Yen Lee 
rtw_wow_rx_dma_start(struct rtw_dev * rtwdev)30744bc17f7SChin-Yen Lee static void rtw_wow_rx_dma_start(struct rtw_dev *rtwdev)
30844bc17f7SChin-Yen Lee {
30944bc17f7SChin-Yen Lee 	rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE);
31044bc17f7SChin-Yen Lee }
31144bc17f7SChin-Yen Lee 
rtw_wow_check_fw_status(struct rtw_dev * rtwdev,bool wow_enable)312ebe8e611SChin-Yen Lee static int rtw_wow_check_fw_status(struct rtw_dev *rtwdev, bool wow_enable)
31344bc17f7SChin-Yen Lee {
31402a55c00SChin-Yen Lee 	int ret;
31502a55c00SChin-Yen Lee 	u8 check;
31602a55c00SChin-Yen Lee 	u32 check_dis;
31744bc17f7SChin-Yen Lee 
31844bc17f7SChin-Yen Lee 	if (wow_enable) {
31902a55c00SChin-Yen Lee 		ret = read_poll_timeout(rtw_read8, check, !check, 1000,
32002a55c00SChin-Yen Lee 					100000, true, rtwdev,
32102a55c00SChin-Yen Lee 					REG_WOWLAN_WAKE_REASON);
32202a55c00SChin-Yen Lee 		if (ret)
323ebe8e611SChin-Yen Lee 			goto wow_fail;
32444bc17f7SChin-Yen Lee 	} else {
32502a55c00SChin-Yen Lee 		ret = read_poll_timeout(rtw_read32_mask, check_dis,
32602a55c00SChin-Yen Lee 					!check_dis, 1000, 100000, true, rtwdev,
32702a55c00SChin-Yen Lee 					REG_FE1IMR, BIT_FS_RXDONE);
32802a55c00SChin-Yen Lee 		if (ret)
32902a55c00SChin-Yen Lee 			goto wow_fail;
33002a55c00SChin-Yen Lee 		ret = read_poll_timeout(rtw_read32_mask, check_dis,
33102a55c00SChin-Yen Lee 					!check_dis, 1000, 100000, false, rtwdev,
33202a55c00SChin-Yen Lee 					REG_RXPKT_NUM, BIT_RW_RELEASE);
33302a55c00SChin-Yen Lee 		if (ret)
334ebe8e611SChin-Yen Lee 			goto wow_fail;
33544bc17f7SChin-Yen Lee 	}
33644bc17f7SChin-Yen Lee 
337ebe8e611SChin-Yen Lee 	return 0;
338ebe8e611SChin-Yen Lee 
339ebe8e611SChin-Yen Lee wow_fail:
34044bc17f7SChin-Yen Lee 	rtw_err(rtwdev, "failed to check wow status %s\n",
34144bc17f7SChin-Yen Lee 		wow_enable ? "enabled" : "disabled");
342ebe8e611SChin-Yen Lee 	return -EBUSY;
34344bc17f7SChin-Yen Lee }
34444bc17f7SChin-Yen Lee 
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)34544bc17f7SChin-Yen Lee static void rtw_wow_fw_security_type_iter(struct ieee80211_hw *hw,
34644bc17f7SChin-Yen Lee 					  struct ieee80211_vif *vif,
34744bc17f7SChin-Yen Lee 					  struct ieee80211_sta *sta,
34844bc17f7SChin-Yen Lee 					  struct ieee80211_key_conf *key,
34944bc17f7SChin-Yen Lee 					  void *data)
35044bc17f7SChin-Yen Lee {
35144bc17f7SChin-Yen Lee 	struct rtw_fw_key_type_iter_data *iter_data = data;
35244bc17f7SChin-Yen Lee 	struct rtw_dev *rtwdev = hw->priv;
35344bc17f7SChin-Yen Lee 	u8 hw_key_type;
35444bc17f7SChin-Yen Lee 
35544bc17f7SChin-Yen Lee 	if (vif != rtwdev->wow.wow_vif)
35644bc17f7SChin-Yen Lee 		return;
35744bc17f7SChin-Yen Lee 
35844bc17f7SChin-Yen Lee 	switch (key->cipher) {
35944bc17f7SChin-Yen Lee 	case WLAN_CIPHER_SUITE_WEP40:
36044bc17f7SChin-Yen Lee 		hw_key_type = RTW_CAM_WEP40;
36144bc17f7SChin-Yen Lee 		break;
36244bc17f7SChin-Yen Lee 	case WLAN_CIPHER_SUITE_WEP104:
36344bc17f7SChin-Yen Lee 		hw_key_type = RTW_CAM_WEP104;
36444bc17f7SChin-Yen Lee 		break;
36544bc17f7SChin-Yen Lee 	case WLAN_CIPHER_SUITE_TKIP:
36644bc17f7SChin-Yen Lee 		hw_key_type = RTW_CAM_TKIP;
36744bc17f7SChin-Yen Lee 		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
36844bc17f7SChin-Yen Lee 		break;
36944bc17f7SChin-Yen Lee 	case WLAN_CIPHER_SUITE_CCMP:
37044bc17f7SChin-Yen Lee 		hw_key_type = RTW_CAM_AES;
37144bc17f7SChin-Yen Lee 		key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
37244bc17f7SChin-Yen Lee 		break;
37344bc17f7SChin-Yen Lee 	default:
3748d201d36SBrian Norris 		rtw_err(rtwdev, "Unsupported key type for wowlan mode: %#x\n",
3758d201d36SBrian Norris 			key->cipher);
37644bc17f7SChin-Yen Lee 		hw_key_type = 0;
37744bc17f7SChin-Yen Lee 		break;
37844bc17f7SChin-Yen Lee 	}
37944bc17f7SChin-Yen Lee 
38044bc17f7SChin-Yen Lee 	if (sta)
38144bc17f7SChin-Yen Lee 		iter_data->pairwise_key_type = hw_key_type;
38244bc17f7SChin-Yen Lee 	else
38344bc17f7SChin-Yen Lee 		iter_data->group_key_type = hw_key_type;
38444bc17f7SChin-Yen Lee }
38544bc17f7SChin-Yen Lee 
rtw_wow_fw_security_type(struct rtw_dev * rtwdev)38644bc17f7SChin-Yen Lee static void rtw_wow_fw_security_type(struct rtw_dev *rtwdev)
38744bc17f7SChin-Yen Lee {
38844bc17f7SChin-Yen Lee 	struct rtw_fw_key_type_iter_data data = {};
38944bc17f7SChin-Yen Lee 	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
39044bc17f7SChin-Yen Lee 
39144bc17f7SChin-Yen Lee 	data.rtwdev = rtwdev;
39244bc17f7SChin-Yen Lee 	rtw_iterate_keys(rtwdev, wow_vif,
39344bc17f7SChin-Yen Lee 			 rtw_wow_fw_security_type_iter, &data);
39444bc17f7SChin-Yen Lee 	rtw_fw_set_aoac_global_info_cmd(rtwdev, data.pairwise_key_type,
39544bc17f7SChin-Yen Lee 					data.group_key_type);
39644bc17f7SChin-Yen Lee }
39744bc17f7SChin-Yen Lee 
rtw_wow_fw_start(struct rtw_dev * rtwdev)39844bc17f7SChin-Yen Lee static int rtw_wow_fw_start(struct rtw_dev *rtwdev)
39944bc17f7SChin-Yen Lee {
40044bc17f7SChin-Yen Lee 	if (rtw_wow_mgd_linked(rtwdev)) {
40144bc17f7SChin-Yen Lee 		rtw_send_rsvd_page_h2c(rtwdev);
402e3e400dfSChin-Yen Lee 		rtw_wow_pattern_write(rtwdev);
40344bc17f7SChin-Yen Lee 		rtw_wow_fw_security_type(rtwdev);
40444bc17f7SChin-Yen Lee 		rtw_fw_set_disconnect_decision_cmd(rtwdev, true);
40544bc17f7SChin-Yen Lee 		rtw_fw_set_keep_alive_cmd(rtwdev, true);
406b6c12908SChin-Yen Lee 	} else if (rtw_wow_no_link(rtwdev)) {
407b6c12908SChin-Yen Lee 		rtw_fw_set_nlo_info(rtwdev, true);
408b6c12908SChin-Yen Lee 		rtw_fw_update_pkt_probe_req(rtwdev, NULL);
409b6c12908SChin-Yen Lee 		rtw_fw_channel_switch(rtwdev, true);
41044bc17f7SChin-Yen Lee 	}
41144bc17f7SChin-Yen Lee 
41244bc17f7SChin-Yen Lee 	rtw_fw_set_wowlan_ctrl_cmd(rtwdev, true);
41344bc17f7SChin-Yen Lee 	rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, true);
41444bc17f7SChin-Yen Lee 
41544bc17f7SChin-Yen Lee 	return rtw_wow_check_fw_status(rtwdev, true);
41644bc17f7SChin-Yen Lee }
41744bc17f7SChin-Yen Lee 
rtw_wow_fw_stop(struct rtw_dev * rtwdev)41844bc17f7SChin-Yen Lee static int rtw_wow_fw_stop(struct rtw_dev *rtwdev)
41944bc17f7SChin-Yen Lee {
42044bc17f7SChin-Yen Lee 	if (rtw_wow_mgd_linked(rtwdev)) {
42144bc17f7SChin-Yen Lee 		rtw_fw_set_disconnect_decision_cmd(rtwdev, false);
42244bc17f7SChin-Yen Lee 		rtw_fw_set_keep_alive_cmd(rtwdev, false);
423e3e400dfSChin-Yen Lee 		rtw_wow_pattern_clear(rtwdev);
424b6c12908SChin-Yen Lee 	} else if (rtw_wow_no_link(rtwdev)) {
425b6c12908SChin-Yen Lee 		rtw_fw_channel_switch(rtwdev, false);
426b6c12908SChin-Yen Lee 		rtw_fw_set_nlo_info(rtwdev, false);
42744bc17f7SChin-Yen Lee 	}
42844bc17f7SChin-Yen Lee 
42944bc17f7SChin-Yen Lee 	rtw_fw_set_wowlan_ctrl_cmd(rtwdev, false);
43044bc17f7SChin-Yen Lee 	rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, false);
43144bc17f7SChin-Yen Lee 
43244bc17f7SChin-Yen Lee 	return rtw_wow_check_fw_status(rtwdev, false);
43344bc17f7SChin-Yen Lee }
43444bc17f7SChin-Yen Lee 
rtw_wow_avoid_reset_mac(struct rtw_dev * rtwdev)43544bc17f7SChin-Yen Lee static void rtw_wow_avoid_reset_mac(struct rtw_dev *rtwdev)
43644bc17f7SChin-Yen Lee {
43744bc17f7SChin-Yen Lee 	/* When resuming from wowlan mode, some hosts issue signal
43844bc17f7SChin-Yen Lee 	 * (PCIE: PREST, USB: SE0RST) to device, and lead to reset
43944bc17f7SChin-Yen Lee 	 * mac core. If it happens, the connection to AP will be lost.
44044bc17f7SChin-Yen Lee 	 * Setting REG_RSV_CTRL Register can avoid this process.
44144bc17f7SChin-Yen Lee 	 */
44244bc17f7SChin-Yen Lee 	switch (rtw_hci_type(rtwdev)) {
44344bc17f7SChin-Yen Lee 	case RTW_HCI_TYPE_PCIE:
44444bc17f7SChin-Yen Lee 	case RTW_HCI_TYPE_USB:
44544bc17f7SChin-Yen Lee 		rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6);
44644bc17f7SChin-Yen Lee 		rtw_write8(rtwdev, REG_RSV_CTRL,
44744bc17f7SChin-Yen Lee 			   BIT_WLOCK_1C_B6 | BIT_R_DIS_PRST);
44844bc17f7SChin-Yen Lee 		break;
44944bc17f7SChin-Yen Lee 	default:
45044bc17f7SChin-Yen Lee 		rtw_warn(rtwdev, "Unsupported hci type to disable reset MAC\n");
45144bc17f7SChin-Yen Lee 		break;
45244bc17f7SChin-Yen Lee 	}
45344bc17f7SChin-Yen Lee }
45444bc17f7SChin-Yen Lee 
rtw_wow_fw_media_status_iter(void * data,struct ieee80211_sta * sta)45544bc17f7SChin-Yen Lee static void rtw_wow_fw_media_status_iter(void *data, struct ieee80211_sta *sta)
45644bc17f7SChin-Yen Lee {
45744bc17f7SChin-Yen Lee 	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
45844bc17f7SChin-Yen Lee 	struct rtw_fw_media_status_iter_data *iter_data = data;
45944bc17f7SChin-Yen Lee 	struct rtw_dev *rtwdev = iter_data->rtwdev;
46044bc17f7SChin-Yen Lee 
46144bc17f7SChin-Yen Lee 	rtw_fw_media_status_report(rtwdev, si->mac_id, iter_data->connect);
46244bc17f7SChin-Yen Lee }
46344bc17f7SChin-Yen Lee 
rtw_wow_fw_media_status(struct rtw_dev * rtwdev,bool connect)46444bc17f7SChin-Yen Lee static void rtw_wow_fw_media_status(struct rtw_dev *rtwdev, bool connect)
46544bc17f7SChin-Yen Lee {
46644bc17f7SChin-Yen Lee 	struct rtw_fw_media_status_iter_data data;
46744bc17f7SChin-Yen Lee 
46844bc17f7SChin-Yen Lee 	data.rtwdev = rtwdev;
46944bc17f7SChin-Yen Lee 	data.connect = connect;
47044bc17f7SChin-Yen Lee 
47144bc17f7SChin-Yen Lee 	rtw_iterate_stas_atomic(rtwdev, rtw_wow_fw_media_status_iter, &data);
47244bc17f7SChin-Yen Lee }
47344bc17f7SChin-Yen Lee 
rtw_wow_config_wow_fw_rsvd_page(struct rtw_dev * rtwdev)47467368f14SChin-Yen Lee static int rtw_wow_config_wow_fw_rsvd_page(struct rtw_dev *rtwdev)
47544bc17f7SChin-Yen Lee {
47644bc17f7SChin-Yen Lee 	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
477895c096dSYan-Hsuan Chuang 	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
47844bc17f7SChin-Yen Lee 
47967368f14SChin-Yen Lee 	rtw_remove_rsvd_page(rtwdev, rtwvif);
48067368f14SChin-Yen Lee 
48167368f14SChin-Yen Lee 	if (rtw_wow_no_link(rtwdev))
48267368f14SChin-Yen Lee 		rtw_add_rsvd_page_pno(rtwdev, rtwvif);
48367368f14SChin-Yen Lee 	else
48467368f14SChin-Yen Lee 		rtw_add_rsvd_page_sta(rtwdev, rtwvif);
48567368f14SChin-Yen Lee 
48667368f14SChin-Yen Lee 	return rtw_fw_download_rsvd_page(rtwdev);
48767368f14SChin-Yen Lee }
48867368f14SChin-Yen Lee 
rtw_wow_config_normal_fw_rsvd_page(struct rtw_dev * rtwdev)48967368f14SChin-Yen Lee static int rtw_wow_config_normal_fw_rsvd_page(struct rtw_dev *rtwdev)
49067368f14SChin-Yen Lee {
49167368f14SChin-Yen Lee 	struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif;
49267368f14SChin-Yen Lee 	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
49367368f14SChin-Yen Lee 
49467368f14SChin-Yen Lee 	rtw_remove_rsvd_page(rtwdev, rtwvif);
49567368f14SChin-Yen Lee 	rtw_add_rsvd_page_sta(rtwdev, rtwvif);
49667368f14SChin-Yen Lee 
49767368f14SChin-Yen Lee 	if (rtw_wow_no_link(rtwdev))
49867368f14SChin-Yen Lee 		return 0;
49944bc17f7SChin-Yen Lee 
500895c096dSYan-Hsuan Chuang 	return rtw_fw_download_rsvd_page(rtwdev);
50144bc17f7SChin-Yen Lee }
50244bc17f7SChin-Yen Lee 
rtw_wow_swap_fw(struct rtw_dev * rtwdev,enum rtw_fw_type type)50344bc17f7SChin-Yen Lee static int rtw_wow_swap_fw(struct rtw_dev *rtwdev, enum rtw_fw_type type)
50444bc17f7SChin-Yen Lee {
50544bc17f7SChin-Yen Lee 	struct rtw_fw_state *fw;
50644bc17f7SChin-Yen Lee 	int ret;
50744bc17f7SChin-Yen Lee 
50844bc17f7SChin-Yen Lee 	switch (type) {
50944bc17f7SChin-Yen Lee 	case RTW_WOWLAN_FW:
51044bc17f7SChin-Yen Lee 		fw = &rtwdev->wow_fw;
51144bc17f7SChin-Yen Lee 		break;
51244bc17f7SChin-Yen Lee 
51344bc17f7SChin-Yen Lee 	case RTW_NORMAL_FW:
51444bc17f7SChin-Yen Lee 		fw = &rtwdev->fw;
51544bc17f7SChin-Yen Lee 		break;
51644bc17f7SChin-Yen Lee 
51744bc17f7SChin-Yen Lee 	default:
51844bc17f7SChin-Yen Lee 		rtw_warn(rtwdev, "unsupported firmware type to swap\n");
51944bc17f7SChin-Yen Lee 		return -ENOENT;
52044bc17f7SChin-Yen Lee 	}
52144bc17f7SChin-Yen Lee 
52244bc17f7SChin-Yen Lee 	ret = rtw_download_firmware(rtwdev, fw);
52344bc17f7SChin-Yen Lee 	if (ret)
52444bc17f7SChin-Yen Lee 		goto out;
52544bc17f7SChin-Yen Lee 
52644bc17f7SChin-Yen Lee 	rtw_fw_send_general_info(rtwdev);
52744bc17f7SChin-Yen Lee 	rtw_fw_send_phydm_info(rtwdev);
52844bc17f7SChin-Yen Lee 	rtw_wow_fw_media_status(rtwdev, true);
52944bc17f7SChin-Yen Lee 
53044bc17f7SChin-Yen Lee out:
53144bc17f7SChin-Yen Lee 	return ret;
53244bc17f7SChin-Yen Lee }
53344bc17f7SChin-Yen Lee 
rtw_wow_check_pno(struct rtw_dev * rtwdev,struct cfg80211_sched_scan_request * nd_config)534b6c12908SChin-Yen Lee static void rtw_wow_check_pno(struct rtw_dev *rtwdev,
535b6c12908SChin-Yen Lee 			      struct cfg80211_sched_scan_request *nd_config)
536b6c12908SChin-Yen Lee {
537b6c12908SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
538b6c12908SChin-Yen Lee 	struct rtw_pno_request *pno_req = &rtw_wow->pno_req;
539b6c12908SChin-Yen Lee 	struct ieee80211_channel *channel;
540b6c12908SChin-Yen Lee 	int i, size;
541b6c12908SChin-Yen Lee 
542b6c12908SChin-Yen Lee 	if (!nd_config->n_match_sets || !nd_config->n_channels)
543b6c12908SChin-Yen Lee 		goto err;
544b6c12908SChin-Yen Lee 
545b6c12908SChin-Yen Lee 	pno_req->match_set_cnt = nd_config->n_match_sets;
546b6c12908SChin-Yen Lee 	size = sizeof(*pno_req->match_sets) * pno_req->match_set_cnt;
547b6c12908SChin-Yen Lee 	pno_req->match_sets = kmemdup(nd_config->match_sets, size, GFP_KERNEL);
548b6c12908SChin-Yen Lee 	if (!pno_req->match_sets)
549b6c12908SChin-Yen Lee 		goto err;
550b6c12908SChin-Yen Lee 
551b6c12908SChin-Yen Lee 	pno_req->channel_cnt = nd_config->n_channels;
552b6c12908SChin-Yen Lee 	size = sizeof(*nd_config->channels[0]) * nd_config->n_channels;
553b6c12908SChin-Yen Lee 	pno_req->channels = kmalloc(size, GFP_KERNEL);
554b6c12908SChin-Yen Lee 	if (!pno_req->channels)
555b6c12908SChin-Yen Lee 		goto channel_err;
556b6c12908SChin-Yen Lee 
557b6c12908SChin-Yen Lee 	for (i = 0 ; i < pno_req->channel_cnt; i++) {
558b6c12908SChin-Yen Lee 		channel = pno_req->channels + i;
559b6c12908SChin-Yen Lee 		memcpy(channel, nd_config->channels[i], sizeof(*channel));
560b6c12908SChin-Yen Lee 	}
561b6c12908SChin-Yen Lee 
562b6c12908SChin-Yen Lee 	pno_req->scan_plan = *nd_config->scan_plans;
563b6c12908SChin-Yen Lee 	pno_req->inited = true;
564b6c12908SChin-Yen Lee 
565b6c12908SChin-Yen Lee 	rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is enabled\n");
566b6c12908SChin-Yen Lee 
567b6c12908SChin-Yen Lee 	return;
568b6c12908SChin-Yen Lee 
569b6c12908SChin-Yen Lee channel_err:
570b6c12908SChin-Yen Lee 	kfree(pno_req->match_sets);
571b6c12908SChin-Yen Lee 
572b6c12908SChin-Yen Lee err:
573b6c12908SChin-Yen Lee 	rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is disabled\n");
574b6c12908SChin-Yen Lee }
575b6c12908SChin-Yen Lee 
rtw_wow_leave_linked_ps(struct rtw_dev * rtwdev)57644bc17f7SChin-Yen Lee static int rtw_wow_leave_linked_ps(struct rtw_dev *rtwdev)
57744bc17f7SChin-Yen Lee {
57844bc17f7SChin-Yen Lee 	if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
57944bc17f7SChin-Yen Lee 		cancel_delayed_work_sync(&rtwdev->watch_dog_work);
58044bc17f7SChin-Yen Lee 
581b6c12908SChin-Yen Lee 	rtw_leave_lps(rtwdev);
582b6c12908SChin-Yen Lee 
583b6c12908SChin-Yen Lee 	return 0;
584b6c12908SChin-Yen Lee }
585b6c12908SChin-Yen Lee 
rtw_wow_leave_no_link_ps(struct rtw_dev * rtwdev)586b6c12908SChin-Yen Lee static int rtw_wow_leave_no_link_ps(struct rtw_dev *rtwdev)
587b6c12908SChin-Yen Lee {
588b6c12908SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
589b6c12908SChin-Yen Lee 	int ret = 0;
590b6c12908SChin-Yen Lee 
591b6c12908SChin-Yen Lee 	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) {
592fc3ac64aSChin-Yen Lee 		if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE)
593b6c12908SChin-Yen Lee 			rtw_leave_lps_deep(rtwdev);
594b6c12908SChin-Yen Lee 	} else {
595*4a267bc5SPing-Ke Shih 		if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags)) {
596b6c12908SChin-Yen Lee 			rtw_wow->ips_enabled = true;
597b6c12908SChin-Yen Lee 			ret = rtw_leave_ips(rtwdev);
598b6c12908SChin-Yen Lee 			if (ret)
599b6c12908SChin-Yen Lee 				return ret;
600b6c12908SChin-Yen Lee 		}
601b6c12908SChin-Yen Lee 	}
602b6c12908SChin-Yen Lee 
60344bc17f7SChin-Yen Lee 	return 0;
60444bc17f7SChin-Yen Lee }
60544bc17f7SChin-Yen Lee 
rtw_wow_leave_ps(struct rtw_dev * rtwdev)60644bc17f7SChin-Yen Lee static int rtw_wow_leave_ps(struct rtw_dev *rtwdev)
60744bc17f7SChin-Yen Lee {
60844bc17f7SChin-Yen Lee 	int ret = 0;
60944bc17f7SChin-Yen Lee 
61044bc17f7SChin-Yen Lee 	if (rtw_wow_mgd_linked(rtwdev))
61144bc17f7SChin-Yen Lee 		ret = rtw_wow_leave_linked_ps(rtwdev);
612b6c12908SChin-Yen Lee 	else if (rtw_wow_no_link(rtwdev))
613b6c12908SChin-Yen Lee 		ret = rtw_wow_leave_no_link_ps(rtwdev);
614b6c12908SChin-Yen Lee 
615b6c12908SChin-Yen Lee 	return ret;
616b6c12908SChin-Yen Lee }
617b6c12908SChin-Yen Lee 
rtw_wow_restore_ps(struct rtw_dev * rtwdev)618b6c12908SChin-Yen Lee static int rtw_wow_restore_ps(struct rtw_dev *rtwdev)
619b6c12908SChin-Yen Lee {
620b6c12908SChin-Yen Lee 	int ret = 0;
621b6c12908SChin-Yen Lee 
622b6c12908SChin-Yen Lee 	if (rtw_wow_no_link(rtwdev) && rtwdev->wow.ips_enabled)
623b6c12908SChin-Yen Lee 		ret = rtw_enter_ips(rtwdev);
62444bc17f7SChin-Yen Lee 
62544bc17f7SChin-Yen Lee 	return ret;
62644bc17f7SChin-Yen Lee }
62744bc17f7SChin-Yen Lee 
rtw_wow_enter_linked_ps(struct rtw_dev * rtwdev)62844bc17f7SChin-Yen Lee static int rtw_wow_enter_linked_ps(struct rtw_dev *rtwdev)
62944bc17f7SChin-Yen Lee {
63044bc17f7SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
63144bc17f7SChin-Yen Lee 	struct ieee80211_vif *wow_vif = rtw_wow->wow_vif;
63244bc17f7SChin-Yen Lee 	struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv;
63344bc17f7SChin-Yen Lee 
63444bc17f7SChin-Yen Lee 	rtw_enter_lps(rtwdev, rtwvif->port);
63544bc17f7SChin-Yen Lee 
63644bc17f7SChin-Yen Lee 	return 0;
63744bc17f7SChin-Yen Lee }
63844bc17f7SChin-Yen Lee 
rtw_wow_enter_no_link_ps(struct rtw_dev * rtwdev)639b6c12908SChin-Yen Lee static int rtw_wow_enter_no_link_ps(struct rtw_dev *rtwdev)
640b6c12908SChin-Yen Lee {
641b6c12908SChin-Yen Lee 	/* firmware enters deep ps by itself if supported */
642b6c12908SChin-Yen Lee 	set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags);
643b6c12908SChin-Yen Lee 
644b6c12908SChin-Yen Lee 	return 0;
645b6c12908SChin-Yen Lee }
646b6c12908SChin-Yen Lee 
rtw_wow_enter_ps(struct rtw_dev * rtwdev)64744bc17f7SChin-Yen Lee static int rtw_wow_enter_ps(struct rtw_dev *rtwdev)
64844bc17f7SChin-Yen Lee {
64944bc17f7SChin-Yen Lee 	int ret = 0;
65044bc17f7SChin-Yen Lee 
65144bc17f7SChin-Yen Lee 	if (rtw_wow_mgd_linked(rtwdev))
65244bc17f7SChin-Yen Lee 		ret = rtw_wow_enter_linked_ps(rtwdev);
653fc3ac64aSChin-Yen Lee 	else if (rtw_wow_no_link(rtwdev) &&
654fc3ac64aSChin-Yen Lee 		 rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE)
655b6c12908SChin-Yen Lee 		ret = rtw_wow_enter_no_link_ps(rtwdev);
65644bc17f7SChin-Yen Lee 
65744bc17f7SChin-Yen Lee 	return ret;
65844bc17f7SChin-Yen Lee }
65944bc17f7SChin-Yen Lee 
rtw_wow_stop_trx(struct rtw_dev * rtwdev)66044bc17f7SChin-Yen Lee static void rtw_wow_stop_trx(struct rtw_dev *rtwdev)
66144bc17f7SChin-Yen Lee {
66244bc17f7SChin-Yen Lee 	rtw_wow_bb_stop(rtwdev);
66344bc17f7SChin-Yen Lee 	rtw_wow_rx_dma_stop(rtwdev);
66444bc17f7SChin-Yen Lee }
66544bc17f7SChin-Yen Lee 
rtw_wow_start(struct rtw_dev * rtwdev)66644bc17f7SChin-Yen Lee static int rtw_wow_start(struct rtw_dev *rtwdev)
66744bc17f7SChin-Yen Lee {
66844bc17f7SChin-Yen Lee 	int ret;
66944bc17f7SChin-Yen Lee 
67044bc17f7SChin-Yen Lee 	ret = rtw_wow_fw_start(rtwdev);
67144bc17f7SChin-Yen Lee 	if (ret)
67244bc17f7SChin-Yen Lee 		goto out;
67344bc17f7SChin-Yen Lee 
67444bc17f7SChin-Yen Lee 	rtw_hci_stop(rtwdev);
67544bc17f7SChin-Yen Lee 	rtw_wow_bb_start(rtwdev);
67644bc17f7SChin-Yen Lee 	rtw_wow_avoid_reset_mac(rtwdev);
67744bc17f7SChin-Yen Lee 
67844bc17f7SChin-Yen Lee out:
67944bc17f7SChin-Yen Lee 	return ret;
68044bc17f7SChin-Yen Lee }
68144bc17f7SChin-Yen Lee 
rtw_wow_enable(struct rtw_dev * rtwdev)68244bc17f7SChin-Yen Lee static int rtw_wow_enable(struct rtw_dev *rtwdev)
68344bc17f7SChin-Yen Lee {
68444bc17f7SChin-Yen Lee 	int ret = 0;
68544bc17f7SChin-Yen Lee 
68644bc17f7SChin-Yen Lee 	rtw_wow_stop_trx(rtwdev);
68744bc17f7SChin-Yen Lee 
68844bc17f7SChin-Yen Lee 	ret = rtw_wow_swap_fw(rtwdev, RTW_WOWLAN_FW);
68944bc17f7SChin-Yen Lee 	if (ret) {
69044bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to swap wow fw\n");
69144bc17f7SChin-Yen Lee 		goto error;
69244bc17f7SChin-Yen Lee 	}
69344bc17f7SChin-Yen Lee 
69444bc17f7SChin-Yen Lee 	set_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
69544bc17f7SChin-Yen Lee 
69667368f14SChin-Yen Lee 	ret = rtw_wow_config_wow_fw_rsvd_page(rtwdev);
69744bc17f7SChin-Yen Lee 	if (ret) {
69844bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to download wowlan rsvd page\n");
69944bc17f7SChin-Yen Lee 		goto error;
70044bc17f7SChin-Yen Lee 	}
70144bc17f7SChin-Yen Lee 
70244bc17f7SChin-Yen Lee 	ret = rtw_wow_start(rtwdev);
70344bc17f7SChin-Yen Lee 	if (ret) {
70444bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to start wow\n");
70544bc17f7SChin-Yen Lee 		goto error;
70644bc17f7SChin-Yen Lee 	}
70744bc17f7SChin-Yen Lee 
70844bc17f7SChin-Yen Lee 	return ret;
70944bc17f7SChin-Yen Lee 
71044bc17f7SChin-Yen Lee error:
71144bc17f7SChin-Yen Lee 	clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
71244bc17f7SChin-Yen Lee 	return ret;
71344bc17f7SChin-Yen Lee }
71444bc17f7SChin-Yen Lee 
rtw_wow_stop(struct rtw_dev * rtwdev)71544bc17f7SChin-Yen Lee static int rtw_wow_stop(struct rtw_dev *rtwdev)
71644bc17f7SChin-Yen Lee {
71744bc17f7SChin-Yen Lee 	int ret;
71844bc17f7SChin-Yen Lee 
71944bc17f7SChin-Yen Lee 	/* some HCI related registers will be reset after resume,
72044bc17f7SChin-Yen Lee 	 * need to set them again.
72144bc17f7SChin-Yen Lee 	 */
72244bc17f7SChin-Yen Lee 	ret = rtw_hci_setup(rtwdev);
72344bc17f7SChin-Yen Lee 	if (ret) {
72444bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to setup hci\n");
72544bc17f7SChin-Yen Lee 		return ret;
72644bc17f7SChin-Yen Lee 	}
72744bc17f7SChin-Yen Lee 
72844bc17f7SChin-Yen Lee 	ret = rtw_hci_start(rtwdev);
72944bc17f7SChin-Yen Lee 	if (ret) {
73044bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to start hci\n");
73144bc17f7SChin-Yen Lee 		return ret;
73244bc17f7SChin-Yen Lee 	}
73344bc17f7SChin-Yen Lee 
73444bc17f7SChin-Yen Lee 	ret = rtw_wow_fw_stop(rtwdev);
73544bc17f7SChin-Yen Lee 	if (ret)
73644bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to stop wowlan fw\n");
73744bc17f7SChin-Yen Lee 
73844bc17f7SChin-Yen Lee 	rtw_wow_bb_stop(rtwdev);
73944bc17f7SChin-Yen Lee 
74044bc17f7SChin-Yen Lee 	return ret;
74144bc17f7SChin-Yen Lee }
74244bc17f7SChin-Yen Lee 
rtw_wow_resume_trx(struct rtw_dev * rtwdev)74344bc17f7SChin-Yen Lee static void rtw_wow_resume_trx(struct rtw_dev *rtwdev)
74444bc17f7SChin-Yen Lee {
74544bc17f7SChin-Yen Lee 	rtw_wow_rx_dma_start(rtwdev);
74644bc17f7SChin-Yen Lee 	rtw_wow_bb_start(rtwdev);
74744bc17f7SChin-Yen Lee 	ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work,
74844bc17f7SChin-Yen Lee 				     RTW_WATCH_DOG_DELAY_TIME);
74944bc17f7SChin-Yen Lee }
75044bc17f7SChin-Yen Lee 
rtw_wow_disable(struct rtw_dev * rtwdev)75144bc17f7SChin-Yen Lee static int rtw_wow_disable(struct rtw_dev *rtwdev)
75244bc17f7SChin-Yen Lee {
75344bc17f7SChin-Yen Lee 	int ret;
75444bc17f7SChin-Yen Lee 
75544bc17f7SChin-Yen Lee 	clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags);
75644bc17f7SChin-Yen Lee 
75744bc17f7SChin-Yen Lee 	ret = rtw_wow_stop(rtwdev);
75844bc17f7SChin-Yen Lee 	if (ret) {
75944bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to stop wow\n");
76044bc17f7SChin-Yen Lee 		goto out;
76144bc17f7SChin-Yen Lee 	}
76244bc17f7SChin-Yen Lee 
76344bc17f7SChin-Yen Lee 	ret = rtw_wow_swap_fw(rtwdev, RTW_NORMAL_FW);
76444bc17f7SChin-Yen Lee 	if (ret) {
76544bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to swap normal fw\n");
76644bc17f7SChin-Yen Lee 		goto out;
76744bc17f7SChin-Yen Lee 	}
76844bc17f7SChin-Yen Lee 
76967368f14SChin-Yen Lee 	ret = rtw_wow_config_normal_fw_rsvd_page(rtwdev);
77044bc17f7SChin-Yen Lee 	if (ret)
77144bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to download normal rsvd page\n");
77244bc17f7SChin-Yen Lee 
77344bc17f7SChin-Yen Lee out:
77444bc17f7SChin-Yen Lee 	rtw_wow_resume_trx(rtwdev);
77544bc17f7SChin-Yen Lee 	return ret;
77644bc17f7SChin-Yen Lee }
77744bc17f7SChin-Yen Lee 
rtw_wow_vif_iter(void * data,u8 * mac,struct ieee80211_vif * vif)77844bc17f7SChin-Yen Lee static void rtw_wow_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
77944bc17f7SChin-Yen Lee {
78044bc17f7SChin-Yen Lee 	struct rtw_dev *rtwdev = data;
78144bc17f7SChin-Yen Lee 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
78244bc17f7SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
78344bc17f7SChin-Yen Lee 
78444bc17f7SChin-Yen Lee 	/* Current wowlan function support setting of only one STATION vif.
78544bc17f7SChin-Yen Lee 	 * So when one suitable vif is found, stop the iteration.
78644bc17f7SChin-Yen Lee 	 */
78744bc17f7SChin-Yen Lee 	if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION)
78844bc17f7SChin-Yen Lee 		return;
78944bc17f7SChin-Yen Lee 
79044bc17f7SChin-Yen Lee 	switch (rtwvif->net_type) {
79144bc17f7SChin-Yen Lee 	case RTW_NET_MGD_LINKED:
79244bc17f7SChin-Yen Lee 		rtw_wow->wow_vif = vif;
79344bc17f7SChin-Yen Lee 		break;
794b6c12908SChin-Yen Lee 	case RTW_NET_NO_LINK:
795b6c12908SChin-Yen Lee 		if (rtw_wow->pno_req.inited)
796b6c12908SChin-Yen Lee 			rtwdev->wow.wow_vif = vif;
797b6c12908SChin-Yen Lee 		break;
79844bc17f7SChin-Yen Lee 	default:
79944bc17f7SChin-Yen Lee 		break;
80044bc17f7SChin-Yen Lee 	}
80144bc17f7SChin-Yen Lee }
80244bc17f7SChin-Yen Lee 
rtw_wow_set_wakeups(struct rtw_dev * rtwdev,struct cfg80211_wowlan * wowlan)80344bc17f7SChin-Yen Lee static int rtw_wow_set_wakeups(struct rtw_dev *rtwdev,
80444bc17f7SChin-Yen Lee 			       struct cfg80211_wowlan *wowlan)
80544bc17f7SChin-Yen Lee {
80644bc17f7SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
807e3e400dfSChin-Yen Lee 	struct rtw_wow_pattern *rtw_patterns = rtw_wow->patterns;
808e3e400dfSChin-Yen Lee 	struct rtw_vif *rtwvif;
809e3e400dfSChin-Yen Lee 	int i;
81044bc17f7SChin-Yen Lee 
81144bc17f7SChin-Yen Lee 	if (wowlan->disconnect)
81244bc17f7SChin-Yen Lee 		set_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags);
81344bc17f7SChin-Yen Lee 	if (wowlan->magic_pkt)
81444bc17f7SChin-Yen Lee 		set_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags);
81544bc17f7SChin-Yen Lee 	if (wowlan->gtk_rekey_failure)
81644bc17f7SChin-Yen Lee 		set_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags);
81744bc17f7SChin-Yen Lee 
818b6c12908SChin-Yen Lee 	if (wowlan->nd_config)
819b6c12908SChin-Yen Lee 		rtw_wow_check_pno(rtwdev, wowlan->nd_config);
820b6c12908SChin-Yen Lee 
82144bc17f7SChin-Yen Lee 	rtw_iterate_vifs_atomic(rtwdev, rtw_wow_vif_iter, rtwdev);
82244bc17f7SChin-Yen Lee 	if (!rtw_wow->wow_vif)
82344bc17f7SChin-Yen Lee 		return -EPERM;
82444bc17f7SChin-Yen Lee 
825e3e400dfSChin-Yen Lee 	rtwvif = (struct rtw_vif *)rtw_wow->wow_vif->drv_priv;
826e3e400dfSChin-Yen Lee 	if (wowlan->n_patterns && wowlan->patterns) {
827e3e400dfSChin-Yen Lee 		rtw_wow->pattern_cnt = wowlan->n_patterns;
828e3e400dfSChin-Yen Lee 		for (i = 0; i < wowlan->n_patterns; i++)
829e3e400dfSChin-Yen Lee 			rtw_wow_pattern_generate(rtwdev, rtwvif,
830e3e400dfSChin-Yen Lee 						 wowlan->patterns + i,
831e3e400dfSChin-Yen Lee 						 rtw_patterns + i);
832e3e400dfSChin-Yen Lee 	}
833e3e400dfSChin-Yen Lee 
83444bc17f7SChin-Yen Lee 	return 0;
83544bc17f7SChin-Yen Lee }
83644bc17f7SChin-Yen Lee 
rtw_wow_clear_wakeups(struct rtw_dev * rtwdev)83744bc17f7SChin-Yen Lee static void rtw_wow_clear_wakeups(struct rtw_dev *rtwdev)
83844bc17f7SChin-Yen Lee {
83944bc17f7SChin-Yen Lee 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
840b6c12908SChin-Yen Lee 	struct rtw_pno_request *pno_req = &rtw_wow->pno_req;
841b6c12908SChin-Yen Lee 
842b6c12908SChin-Yen Lee 	if (pno_req->inited) {
843b6c12908SChin-Yen Lee 		kfree(pno_req->channels);
844b6c12908SChin-Yen Lee 		kfree(pno_req->match_sets);
845b6c12908SChin-Yen Lee 	}
84644bc17f7SChin-Yen Lee 
84744bc17f7SChin-Yen Lee 	memset(rtw_wow, 0, sizeof(rtwdev->wow));
84844bc17f7SChin-Yen Lee }
84944bc17f7SChin-Yen Lee 
rtw_wow_suspend(struct rtw_dev * rtwdev,struct cfg80211_wowlan * wowlan)85044bc17f7SChin-Yen Lee int rtw_wow_suspend(struct rtw_dev *rtwdev, struct cfg80211_wowlan *wowlan)
85144bc17f7SChin-Yen Lee {
85244bc17f7SChin-Yen Lee 	int ret = 0;
85344bc17f7SChin-Yen Lee 
85444bc17f7SChin-Yen Lee 	ret = rtw_wow_set_wakeups(rtwdev, wowlan);
85544bc17f7SChin-Yen Lee 	if (ret) {
85644bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to set wakeup event\n");
85744bc17f7SChin-Yen Lee 		goto out;
85844bc17f7SChin-Yen Lee 	}
85944bc17f7SChin-Yen Lee 
86044bc17f7SChin-Yen Lee 	ret = rtw_wow_leave_ps(rtwdev);
86144bc17f7SChin-Yen Lee 	if (ret) {
86244bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to leave ps from normal mode\n");
86344bc17f7SChin-Yen Lee 		goto out;
86444bc17f7SChin-Yen Lee 	}
86544bc17f7SChin-Yen Lee 
86644bc17f7SChin-Yen Lee 	ret = rtw_wow_enable(rtwdev);
86744bc17f7SChin-Yen Lee 	if (ret) {
86844bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to enable wow\n");
869b6c12908SChin-Yen Lee 		rtw_wow_restore_ps(rtwdev);
87044bc17f7SChin-Yen Lee 		goto out;
87144bc17f7SChin-Yen Lee 	}
87244bc17f7SChin-Yen Lee 
87344bc17f7SChin-Yen Lee 	ret = rtw_wow_enter_ps(rtwdev);
87444bc17f7SChin-Yen Lee 	if (ret)
87544bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to enter ps for wow\n");
87644bc17f7SChin-Yen Lee 
87744bc17f7SChin-Yen Lee out:
87844bc17f7SChin-Yen Lee 	return ret;
87944bc17f7SChin-Yen Lee }
88044bc17f7SChin-Yen Lee 
rtw_wow_resume(struct rtw_dev * rtwdev)88144bc17f7SChin-Yen Lee int rtw_wow_resume(struct rtw_dev *rtwdev)
88244bc17f7SChin-Yen Lee {
88344bc17f7SChin-Yen Lee 	int ret;
88444bc17f7SChin-Yen Lee 
88544bc17f7SChin-Yen Lee 	/* If wowlan mode is not enabled, do nothing */
88644bc17f7SChin-Yen Lee 	if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) {
88744bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "wow is not enabled\n");
88844bc17f7SChin-Yen Lee 		ret = -EPERM;
88944bc17f7SChin-Yen Lee 		goto out;
89044bc17f7SChin-Yen Lee 	}
89144bc17f7SChin-Yen Lee 
89244bc17f7SChin-Yen Lee 	ret = rtw_wow_leave_ps(rtwdev);
89344bc17f7SChin-Yen Lee 	if (ret) {
89444bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to leave ps from wowlan mode\n");
89544bc17f7SChin-Yen Lee 		goto out;
89644bc17f7SChin-Yen Lee 	}
89744bc17f7SChin-Yen Lee 
89844bc17f7SChin-Yen Lee 	rtw_wow_show_wakeup_reason(rtwdev);
89944bc17f7SChin-Yen Lee 
90044bc17f7SChin-Yen Lee 	ret = rtw_wow_disable(rtwdev);
901b6c12908SChin-Yen Lee 	if (ret) {
90244bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to disable wow\n");
903b6c12908SChin-Yen Lee 		goto out;
904b6c12908SChin-Yen Lee 	}
905b6c12908SChin-Yen Lee 
906b6c12908SChin-Yen Lee 	ret = rtw_wow_restore_ps(rtwdev);
907b6c12908SChin-Yen Lee 	if (ret)
908b6c12908SChin-Yen Lee 		rtw_err(rtwdev, "failed to restore ps to normal mode\n");
90944bc17f7SChin-Yen Lee 
91044bc17f7SChin-Yen Lee out:
91144bc17f7SChin-Yen Lee 	rtw_wow_clear_wakeups(rtwdev);
91244bc17f7SChin-Yen Lee 	return ret;
91344bc17f7SChin-Yen Lee }
914