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