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 1344bc17f7SChin-Yen Lee static void rtw_wow_show_wakeup_reason(struct rtw_dev *rtwdev) 1444bc17f7SChin-Yen Lee { 1544bc17f7SChin-Yen Lee u8 reason; 1644bc17f7SChin-Yen Lee 1744bc17f7SChin-Yen Lee reason = rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON); 1844bc17f7SChin-Yen Lee 1944bc17f7SChin-Yen Lee if (reason == RTW_WOW_RSN_RX_DEAUTH) 2044bc17f7SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx deauth\n"); 2144bc17f7SChin-Yen Lee else if (reason == RTW_WOW_RSN_DISCONNECT) 2244bc17f7SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: AP is off\n"); 2344bc17f7SChin-Yen Lee else if (reason == RTW_WOW_RSN_RX_MAGIC_PKT) 2444bc17f7SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx magic packet\n"); 2544bc17f7SChin-Yen Lee else if (reason == RTW_WOW_RSN_RX_GTK_REKEY) 2644bc17f7SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx gtk rekey\n"); 2744bc17f7SChin-Yen Lee else if (reason == RTW_WOW_RSN_RX_PTK_REKEY) 2844bc17f7SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx ptk rekey\n"); 29e3e400dfSChin-Yen Lee else if (reason == RTW_WOW_RSN_RX_PATTERN_MATCH) 30e3e400dfSChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: Rx pattern match packet\n"); 31b6c12908SChin-Yen Lee else if (reason == RTW_WOW_RSN_RX_NLO) 32b6c12908SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "Rx NLO\n"); 3344bc17f7SChin-Yen Lee else 3444bc17f7SChin-Yen Lee rtw_warn(rtwdev, "Unknown wakeup reason %x\n", reason); 3544bc17f7SChin-Yen Lee } 3644bc17f7SChin-Yen Lee 37e3e400dfSChin-Yen Lee static void rtw_wow_pattern_write_cam(struct rtw_dev *rtwdev, u8 addr, 38e3e400dfSChin-Yen Lee u32 wdata) 39e3e400dfSChin-Yen Lee { 40e3e400dfSChin-Yen Lee rtw_write32(rtwdev, REG_WKFMCAM_RWD, wdata); 41e3e400dfSChin-Yen Lee rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 | 42e3e400dfSChin-Yen Lee BIT_WKFCAM_WE | BIT_WKFCAM_ADDR_V2(addr)); 43e3e400dfSChin-Yen Lee 44e3e400dfSChin-Yen Lee if (!check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0)) 45e3e400dfSChin-Yen Lee rtw_err(rtwdev, "failed to write pattern cam\n"); 46e3e400dfSChin-Yen Lee } 47e3e400dfSChin-Yen Lee 48e3e400dfSChin-Yen Lee static void rtw_wow_pattern_write_cam_ent(struct rtw_dev *rtwdev, u8 id, 49e3e400dfSChin-Yen Lee struct rtw_wow_pattern *rtw_pattern) 50e3e400dfSChin-Yen Lee { 51e3e400dfSChin-Yen Lee int i; 52e3e400dfSChin-Yen Lee u8 addr; 53e3e400dfSChin-Yen Lee u32 wdata; 54e3e400dfSChin-Yen Lee 55e3e400dfSChin-Yen Lee for (i = 0; i < RTW_MAX_PATTERN_MASK_SIZE / 4; i++) { 56e3e400dfSChin-Yen Lee addr = (id << 3) + i; 57e3e400dfSChin-Yen Lee wdata = rtw_pattern->mask[i * 4]; 58e3e400dfSChin-Yen Lee wdata |= rtw_pattern->mask[i * 4 + 1] << 8; 59e3e400dfSChin-Yen Lee wdata |= rtw_pattern->mask[i * 4 + 2] << 16; 60e3e400dfSChin-Yen Lee wdata |= rtw_pattern->mask[i * 4 + 3] << 24; 61e3e400dfSChin-Yen Lee rtw_wow_pattern_write_cam(rtwdev, addr, wdata); 62e3e400dfSChin-Yen Lee } 63e3e400dfSChin-Yen Lee 64e3e400dfSChin-Yen Lee wdata = rtw_pattern->crc; 65e3e400dfSChin-Yen Lee addr = (id << 3) + RTW_MAX_PATTERN_MASK_SIZE / 4; 66e3e400dfSChin-Yen Lee 67e3e400dfSChin-Yen Lee switch (rtw_pattern->type) { 68e3e400dfSChin-Yen Lee case RTW_PATTERN_BROADCAST: 69e3e400dfSChin-Yen Lee wdata |= BIT_WKFMCAM_BC | BIT_WKFMCAM_VALID; 70e3e400dfSChin-Yen Lee break; 71e3e400dfSChin-Yen Lee case RTW_PATTERN_MULTICAST: 72e3e400dfSChin-Yen Lee wdata |= BIT_WKFMCAM_MC | BIT_WKFMCAM_VALID; 73e3e400dfSChin-Yen Lee break; 74e3e400dfSChin-Yen Lee case RTW_PATTERN_UNICAST: 75e3e400dfSChin-Yen Lee wdata |= BIT_WKFMCAM_UC | BIT_WKFMCAM_VALID; 76e3e400dfSChin-Yen Lee break; 77e3e400dfSChin-Yen Lee default: 78e3e400dfSChin-Yen Lee break; 79e3e400dfSChin-Yen Lee } 80e3e400dfSChin-Yen Lee rtw_wow_pattern_write_cam(rtwdev, addr, wdata); 81e3e400dfSChin-Yen Lee } 82e3e400dfSChin-Yen Lee 83e3e400dfSChin-Yen Lee /* RTK internal CRC16 for Pattern Cam */ 84e3e400dfSChin-Yen Lee static u16 __rtw_cal_crc16(u8 data, u16 crc) 85e3e400dfSChin-Yen Lee { 86e3e400dfSChin-Yen Lee u8 shift_in, data_bit; 87e3e400dfSChin-Yen Lee u8 crc_bit4, crc_bit11, crc_bit15; 88e3e400dfSChin-Yen Lee u16 crc_result; 89e3e400dfSChin-Yen Lee int index; 90e3e400dfSChin-Yen Lee 91e3e400dfSChin-Yen Lee for (index = 0; index < 8; index++) { 92e3e400dfSChin-Yen Lee crc_bit15 = ((crc & BIT(15)) ? 1 : 0); 93e3e400dfSChin-Yen Lee data_bit = (data & (BIT(0) << index) ? 1 : 0); 94e3e400dfSChin-Yen Lee shift_in = crc_bit15 ^ data_bit; 95e3e400dfSChin-Yen Lee 96e3e400dfSChin-Yen Lee crc_result = crc << 1; 97e3e400dfSChin-Yen Lee 98e3e400dfSChin-Yen Lee if (shift_in == 0) 99e3e400dfSChin-Yen Lee crc_result &= (~BIT(0)); 100e3e400dfSChin-Yen Lee else 101e3e400dfSChin-Yen Lee crc_result |= BIT(0); 102e3e400dfSChin-Yen Lee 103e3e400dfSChin-Yen Lee crc_bit11 = ((crc & BIT(11)) ? 1 : 0) ^ shift_in; 104e3e400dfSChin-Yen Lee 105e3e400dfSChin-Yen Lee if (crc_bit11 == 0) 106e3e400dfSChin-Yen Lee crc_result &= (~BIT(12)); 107e3e400dfSChin-Yen Lee else 108e3e400dfSChin-Yen Lee crc_result |= BIT(12); 109e3e400dfSChin-Yen Lee 110e3e400dfSChin-Yen Lee crc_bit4 = ((crc & BIT(4)) ? 1 : 0) ^ shift_in; 111e3e400dfSChin-Yen Lee 112e3e400dfSChin-Yen Lee if (crc_bit4 == 0) 113e3e400dfSChin-Yen Lee crc_result &= (~BIT(5)); 114e3e400dfSChin-Yen Lee else 115e3e400dfSChin-Yen Lee crc_result |= BIT(5); 116e3e400dfSChin-Yen Lee 117e3e400dfSChin-Yen Lee crc = crc_result; 118e3e400dfSChin-Yen Lee } 119e3e400dfSChin-Yen Lee return crc; 120e3e400dfSChin-Yen Lee } 121e3e400dfSChin-Yen Lee 122e3e400dfSChin-Yen Lee static u16 rtw_calc_crc(u8 *pdata, int length) 123e3e400dfSChin-Yen Lee { 124e3e400dfSChin-Yen Lee u16 crc = 0xffff; 125e3e400dfSChin-Yen Lee int i; 126e3e400dfSChin-Yen Lee 127e3e400dfSChin-Yen Lee for (i = 0; i < length; i++) 128e3e400dfSChin-Yen Lee crc = __rtw_cal_crc16(pdata[i], crc); 129e3e400dfSChin-Yen Lee 130e3e400dfSChin-Yen Lee /* get 1' complement */ 131e3e400dfSChin-Yen Lee return ~crc; 132e3e400dfSChin-Yen Lee } 133e3e400dfSChin-Yen Lee 134e3e400dfSChin-Yen Lee static void rtw_wow_pattern_generate(struct rtw_dev *rtwdev, 135e3e400dfSChin-Yen Lee struct rtw_vif *rtwvif, 136e3e400dfSChin-Yen Lee const struct cfg80211_pkt_pattern *pkt_pattern, 137e3e400dfSChin-Yen Lee struct rtw_wow_pattern *rtw_pattern) 138e3e400dfSChin-Yen Lee { 139e3e400dfSChin-Yen Lee const u8 *mask; 140e3e400dfSChin-Yen Lee const u8 *pattern; 141e3e400dfSChin-Yen Lee u8 mask_hw[RTW_MAX_PATTERN_MASK_SIZE] = {0}; 142e3e400dfSChin-Yen Lee u8 content[RTW_MAX_PATTERN_SIZE] = {0}; 143e3e400dfSChin-Yen Lee u8 mac_addr[ETH_ALEN] = {0}; 144e3e400dfSChin-Yen Lee u8 mask_len; 145e3e400dfSChin-Yen Lee u16 count; 146e3e400dfSChin-Yen Lee int len; 147e3e400dfSChin-Yen Lee int i; 148e3e400dfSChin-Yen Lee 149e3e400dfSChin-Yen Lee pattern = pkt_pattern->pattern; 150e3e400dfSChin-Yen Lee len = pkt_pattern->pattern_len; 151e3e400dfSChin-Yen Lee mask = pkt_pattern->mask; 152e3e400dfSChin-Yen Lee 153e3e400dfSChin-Yen Lee ether_addr_copy(mac_addr, rtwvif->mac_addr); 154e3e400dfSChin-Yen Lee memset(rtw_pattern, 0, sizeof(*rtw_pattern)); 155e3e400dfSChin-Yen Lee 156e3e400dfSChin-Yen Lee mask_len = DIV_ROUND_UP(len, 8); 157e3e400dfSChin-Yen Lee 158e3e400dfSChin-Yen Lee if (is_broadcast_ether_addr(pattern)) 159e3e400dfSChin-Yen Lee rtw_pattern->type = RTW_PATTERN_BROADCAST; 160e3e400dfSChin-Yen Lee else if (is_multicast_ether_addr(pattern)) 161e3e400dfSChin-Yen Lee rtw_pattern->type = RTW_PATTERN_MULTICAST; 162e3e400dfSChin-Yen Lee else if (ether_addr_equal(pattern, mac_addr)) 163e3e400dfSChin-Yen Lee rtw_pattern->type = RTW_PATTERN_UNICAST; 164e3e400dfSChin-Yen Lee else 165e3e400dfSChin-Yen Lee rtw_pattern->type = RTW_PATTERN_INVALID; 166e3e400dfSChin-Yen Lee 167e3e400dfSChin-Yen Lee /* translate mask from os to mask for hw 168e3e400dfSChin-Yen Lee * pattern from OS uses 'ethenet frame', like this: 169e3e400dfSChin-Yen Lee * | 6 | 6 | 2 | 20 | Variable | 4 | 170e3e400dfSChin-Yen Lee * |--------+--------+------+-----------+------------+-----| 171e3e400dfSChin-Yen Lee * | 802.3 Mac Header | IP Header | TCP Packet | FCS | 172e3e400dfSChin-Yen Lee * | DA | SA | Type | 173e3e400dfSChin-Yen Lee * 174e3e400dfSChin-Yen Lee * BUT, packet catched by our HW is in '802.11 frame', begin from LLC 175e3e400dfSChin-Yen Lee * | 24 or 30 | 6 | 2 | 20 | Variable | 4 | 176e3e400dfSChin-Yen Lee * |-------------------+--------+------+-----------+------------+-----| 177e3e400dfSChin-Yen Lee * | 802.11 MAC Header | LLC | IP Header | TCP Packet | FCS | 178e3e400dfSChin-Yen Lee * | Others | Tpye | 179e3e400dfSChin-Yen Lee * 180e3e400dfSChin-Yen Lee * Therefore, we need translate mask_from_OS to mask_to_hw. 181e3e400dfSChin-Yen Lee * We should left-shift mask by 6 bits, then set the new bit[0~5] = 0, 182e3e400dfSChin-Yen Lee * because new mask[0~5] means 'SA', but our HW packet begins from LLC, 183e3e400dfSChin-Yen Lee * bit[0~5] corresponds to first 6 Bytes in LLC, they just don't match. 184e3e400dfSChin-Yen Lee */ 185e3e400dfSChin-Yen Lee 186e3e400dfSChin-Yen Lee /* Shift 6 bits */ 187e3e400dfSChin-Yen Lee for (i = 0; i < mask_len - 1; i++) { 188e3e400dfSChin-Yen Lee mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)); 189e3e400dfSChin-Yen Lee mask_hw[i] |= u8_get_bits(mask[i + 1], GENMASK(5, 0)) << 2; 190e3e400dfSChin-Yen Lee } 191e3e400dfSChin-Yen Lee mask_hw[i] = u8_get_bits(mask[i], GENMASK(7, 6)); 192e3e400dfSChin-Yen Lee 193e3e400dfSChin-Yen Lee /* Set bit 0-5 to zero */ 194e3e400dfSChin-Yen Lee mask_hw[0] &= (~GENMASK(5, 0)); 195e3e400dfSChin-Yen Lee 196e3e400dfSChin-Yen Lee memcpy(rtw_pattern->mask, mask_hw, RTW_MAX_PATTERN_MASK_SIZE); 197e3e400dfSChin-Yen Lee 198e3e400dfSChin-Yen Lee /* To get the wake up pattern from the mask. 199e3e400dfSChin-Yen Lee * We do not count first 12 bits which means 200e3e400dfSChin-Yen Lee * DA[6] and SA[6] in the pattern to match HW design. 201e3e400dfSChin-Yen Lee */ 202e3e400dfSChin-Yen Lee count = 0; 203e3e400dfSChin-Yen Lee for (i = 12; i < len; i++) { 204e3e400dfSChin-Yen Lee if ((mask[i / 8] >> (i % 8)) & 0x01) { 205e3e400dfSChin-Yen Lee content[count] = pattern[i]; 206e3e400dfSChin-Yen Lee count++; 207e3e400dfSChin-Yen Lee } 208e3e400dfSChin-Yen Lee } 209e3e400dfSChin-Yen Lee 210e3e400dfSChin-Yen Lee rtw_pattern->crc = rtw_calc_crc(content, count); 211e3e400dfSChin-Yen Lee } 212e3e400dfSChin-Yen Lee 213e3e400dfSChin-Yen Lee static void rtw_wow_pattern_clear_cam(struct rtw_dev *rtwdev) 214e3e400dfSChin-Yen Lee { 215e3e400dfSChin-Yen Lee bool ret; 216e3e400dfSChin-Yen Lee 217e3e400dfSChin-Yen Lee rtw_write32(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1 | 218e3e400dfSChin-Yen Lee BIT_WKFCAM_CLR_V1); 219e3e400dfSChin-Yen Lee 220e3e400dfSChin-Yen Lee ret = check_hw_ready(rtwdev, REG_WKFMCAM_CMD, BIT_WKFCAM_POLLING_V1, 0); 221e3e400dfSChin-Yen Lee if (!ret) 222e3e400dfSChin-Yen Lee rtw_err(rtwdev, "failed to clean pattern cam\n"); 223e3e400dfSChin-Yen Lee } 224e3e400dfSChin-Yen Lee 225e3e400dfSChin-Yen Lee static void rtw_wow_pattern_write(struct rtw_dev *rtwdev) 226e3e400dfSChin-Yen Lee { 227e3e400dfSChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 228e3e400dfSChin-Yen Lee struct rtw_wow_pattern *rtw_pattern = rtw_wow->patterns; 229e3e400dfSChin-Yen Lee int i = 0; 230e3e400dfSChin-Yen Lee 231e3e400dfSChin-Yen Lee for (i = 0; i < rtw_wow->pattern_cnt; i++) 232e3e400dfSChin-Yen Lee rtw_wow_pattern_write_cam_ent(rtwdev, i, rtw_pattern + i); 233e3e400dfSChin-Yen Lee } 234e3e400dfSChin-Yen Lee 235e3e400dfSChin-Yen Lee static void rtw_wow_pattern_clear(struct rtw_dev *rtwdev) 236e3e400dfSChin-Yen Lee { 237e3e400dfSChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 238e3e400dfSChin-Yen Lee 239e3e400dfSChin-Yen Lee rtw_wow_pattern_clear_cam(rtwdev); 240e3e400dfSChin-Yen Lee 241e3e400dfSChin-Yen Lee rtw_wow->pattern_cnt = 0; 242e3e400dfSChin-Yen Lee memset(rtw_wow->patterns, 0, sizeof(rtw_wow->patterns)); 243e3e400dfSChin-Yen Lee } 244e3e400dfSChin-Yen Lee 24544bc17f7SChin-Yen Lee static void rtw_wow_bb_stop(struct rtw_dev *rtwdev) 24644bc17f7SChin-Yen Lee { 24744bc17f7SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 24844bc17f7SChin-Yen Lee 24944bc17f7SChin-Yen Lee /* wait 100ms for firmware to finish TX */ 25044bc17f7SChin-Yen Lee msleep(100); 25144bc17f7SChin-Yen Lee 25244bc17f7SChin-Yen Lee if (!rtw_read32_mask(rtwdev, REG_BCNQ_INFO, BIT_MGQ_CPU_EMPTY)) 25344bc17f7SChin-Yen Lee rtw_warn(rtwdev, "Wrong status of MGQ_CPU empty!\n"); 25444bc17f7SChin-Yen Lee 25544bc17f7SChin-Yen Lee rtw_wow->txpause = rtw_read8(rtwdev, REG_TXPAUSE); 25644bc17f7SChin-Yen Lee rtw_write8(rtwdev, REG_TXPAUSE, 0xff); 25744bc17f7SChin-Yen Lee rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); 25844bc17f7SChin-Yen Lee } 25944bc17f7SChin-Yen Lee 26044bc17f7SChin-Yen Lee static void rtw_wow_bb_start(struct rtw_dev *rtwdev) 26144bc17f7SChin-Yen Lee { 26244bc17f7SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 26344bc17f7SChin-Yen Lee 26444bc17f7SChin-Yen Lee rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_BB_RSTB); 26544bc17f7SChin-Yen Lee rtw_write8(rtwdev, REG_TXPAUSE, rtw_wow->txpause); 26644bc17f7SChin-Yen Lee } 26744bc17f7SChin-Yen Lee 26844bc17f7SChin-Yen Lee static void rtw_wow_rx_dma_stop(struct rtw_dev *rtwdev) 26944bc17f7SChin-Yen Lee { 27044bc17f7SChin-Yen Lee /* wait 100ms for HW to finish rx dma */ 27144bc17f7SChin-Yen Lee msleep(100); 27244bc17f7SChin-Yen Lee 27344bc17f7SChin-Yen Lee rtw_write32_set(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); 27444bc17f7SChin-Yen Lee 27544bc17f7SChin-Yen Lee if (!check_hw_ready(rtwdev, REG_RXPKT_NUM, BIT_RXDMA_IDLE, 1)) 27644bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to stop rx dma\n"); 27744bc17f7SChin-Yen Lee } 27844bc17f7SChin-Yen Lee 27944bc17f7SChin-Yen Lee static void rtw_wow_rx_dma_start(struct rtw_dev *rtwdev) 28044bc17f7SChin-Yen Lee { 28144bc17f7SChin-Yen Lee rtw_write32_clr(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE); 28244bc17f7SChin-Yen Lee } 28344bc17f7SChin-Yen Lee 284ebe8e611SChin-Yen Lee static int rtw_wow_check_fw_status(struct rtw_dev *rtwdev, bool wow_enable) 28544bc17f7SChin-Yen Lee { 28644bc17f7SChin-Yen Lee /* wait 100ms for wow firmware to finish work */ 28744bc17f7SChin-Yen Lee msleep(100); 28844bc17f7SChin-Yen Lee 28944bc17f7SChin-Yen Lee if (wow_enable) { 290ebe8e611SChin-Yen Lee if (rtw_read8(rtwdev, REG_WOWLAN_WAKE_REASON)) 291ebe8e611SChin-Yen Lee goto wow_fail; 29244bc17f7SChin-Yen Lee } else { 293ebe8e611SChin-Yen Lee if (rtw_read32_mask(rtwdev, REG_FE1IMR, BIT_FS_RXDONE) || 294ebe8e611SChin-Yen Lee rtw_read32_mask(rtwdev, REG_RXPKT_NUM, BIT_RW_RELEASE)) 295ebe8e611SChin-Yen Lee goto wow_fail; 29644bc17f7SChin-Yen Lee } 29744bc17f7SChin-Yen Lee 298ebe8e611SChin-Yen Lee return 0; 299ebe8e611SChin-Yen Lee 300ebe8e611SChin-Yen Lee wow_fail: 30144bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to check wow status %s\n", 30244bc17f7SChin-Yen Lee wow_enable ? "enabled" : "disabled"); 303ebe8e611SChin-Yen Lee return -EBUSY; 30444bc17f7SChin-Yen Lee } 30544bc17f7SChin-Yen Lee 30644bc17f7SChin-Yen Lee static void rtw_wow_fw_security_type_iter(struct ieee80211_hw *hw, 30744bc17f7SChin-Yen Lee struct ieee80211_vif *vif, 30844bc17f7SChin-Yen Lee struct ieee80211_sta *sta, 30944bc17f7SChin-Yen Lee struct ieee80211_key_conf *key, 31044bc17f7SChin-Yen Lee void *data) 31144bc17f7SChin-Yen Lee { 31244bc17f7SChin-Yen Lee struct rtw_fw_key_type_iter_data *iter_data = data; 31344bc17f7SChin-Yen Lee struct rtw_dev *rtwdev = hw->priv; 31444bc17f7SChin-Yen Lee u8 hw_key_type; 31544bc17f7SChin-Yen Lee 31644bc17f7SChin-Yen Lee if (vif != rtwdev->wow.wow_vif) 31744bc17f7SChin-Yen Lee return; 31844bc17f7SChin-Yen Lee 31944bc17f7SChin-Yen Lee switch (key->cipher) { 32044bc17f7SChin-Yen Lee case WLAN_CIPHER_SUITE_WEP40: 32144bc17f7SChin-Yen Lee hw_key_type = RTW_CAM_WEP40; 32244bc17f7SChin-Yen Lee break; 32344bc17f7SChin-Yen Lee case WLAN_CIPHER_SUITE_WEP104: 32444bc17f7SChin-Yen Lee hw_key_type = RTW_CAM_WEP104; 32544bc17f7SChin-Yen Lee break; 32644bc17f7SChin-Yen Lee case WLAN_CIPHER_SUITE_TKIP: 32744bc17f7SChin-Yen Lee hw_key_type = RTW_CAM_TKIP; 32844bc17f7SChin-Yen Lee key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; 32944bc17f7SChin-Yen Lee break; 33044bc17f7SChin-Yen Lee case WLAN_CIPHER_SUITE_CCMP: 33144bc17f7SChin-Yen Lee hw_key_type = RTW_CAM_AES; 33244bc17f7SChin-Yen Lee key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; 33344bc17f7SChin-Yen Lee break; 33444bc17f7SChin-Yen Lee default: 33544bc17f7SChin-Yen Lee rtw_err(rtwdev, "Unsupported key type for wowlan mode\n"); 33644bc17f7SChin-Yen Lee hw_key_type = 0; 33744bc17f7SChin-Yen Lee break; 33844bc17f7SChin-Yen Lee } 33944bc17f7SChin-Yen Lee 34044bc17f7SChin-Yen Lee if (sta) 34144bc17f7SChin-Yen Lee iter_data->pairwise_key_type = hw_key_type; 34244bc17f7SChin-Yen Lee else 34344bc17f7SChin-Yen Lee iter_data->group_key_type = hw_key_type; 34444bc17f7SChin-Yen Lee } 34544bc17f7SChin-Yen Lee 34644bc17f7SChin-Yen Lee static void rtw_wow_fw_security_type(struct rtw_dev *rtwdev) 34744bc17f7SChin-Yen Lee { 34844bc17f7SChin-Yen Lee struct rtw_fw_key_type_iter_data data = {}; 34944bc17f7SChin-Yen Lee struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; 35044bc17f7SChin-Yen Lee 35144bc17f7SChin-Yen Lee data.rtwdev = rtwdev; 35244bc17f7SChin-Yen Lee rtw_iterate_keys(rtwdev, wow_vif, 35344bc17f7SChin-Yen Lee rtw_wow_fw_security_type_iter, &data); 35444bc17f7SChin-Yen Lee rtw_fw_set_aoac_global_info_cmd(rtwdev, data.pairwise_key_type, 35544bc17f7SChin-Yen Lee data.group_key_type); 35644bc17f7SChin-Yen Lee } 35744bc17f7SChin-Yen Lee 35844bc17f7SChin-Yen Lee static int rtw_wow_fw_start(struct rtw_dev *rtwdev) 35944bc17f7SChin-Yen Lee { 36044bc17f7SChin-Yen Lee if (rtw_wow_mgd_linked(rtwdev)) { 36144bc17f7SChin-Yen Lee rtw_send_rsvd_page_h2c(rtwdev); 362e3e400dfSChin-Yen Lee rtw_wow_pattern_write(rtwdev); 36344bc17f7SChin-Yen Lee rtw_wow_fw_security_type(rtwdev); 36444bc17f7SChin-Yen Lee rtw_fw_set_disconnect_decision_cmd(rtwdev, true); 36544bc17f7SChin-Yen Lee rtw_fw_set_keep_alive_cmd(rtwdev, true); 366b6c12908SChin-Yen Lee } else if (rtw_wow_no_link(rtwdev)) { 367b6c12908SChin-Yen Lee rtw_fw_set_nlo_info(rtwdev, true); 368b6c12908SChin-Yen Lee rtw_fw_update_pkt_probe_req(rtwdev, NULL); 369b6c12908SChin-Yen Lee rtw_fw_channel_switch(rtwdev, true); 37044bc17f7SChin-Yen Lee } 37144bc17f7SChin-Yen Lee 37244bc17f7SChin-Yen Lee rtw_fw_set_wowlan_ctrl_cmd(rtwdev, true); 37344bc17f7SChin-Yen Lee rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, true); 37444bc17f7SChin-Yen Lee 37544bc17f7SChin-Yen Lee return rtw_wow_check_fw_status(rtwdev, true); 37644bc17f7SChin-Yen Lee } 37744bc17f7SChin-Yen Lee 37844bc17f7SChin-Yen Lee static int rtw_wow_fw_stop(struct rtw_dev *rtwdev) 37944bc17f7SChin-Yen Lee { 38044bc17f7SChin-Yen Lee if (rtw_wow_mgd_linked(rtwdev)) { 38144bc17f7SChin-Yen Lee rtw_fw_set_disconnect_decision_cmd(rtwdev, false); 38244bc17f7SChin-Yen Lee rtw_fw_set_keep_alive_cmd(rtwdev, false); 383e3e400dfSChin-Yen Lee rtw_wow_pattern_clear(rtwdev); 384b6c12908SChin-Yen Lee } else if (rtw_wow_no_link(rtwdev)) { 385b6c12908SChin-Yen Lee rtw_fw_channel_switch(rtwdev, false); 386b6c12908SChin-Yen Lee rtw_fw_set_nlo_info(rtwdev, false); 38744bc17f7SChin-Yen Lee } 38844bc17f7SChin-Yen Lee 38944bc17f7SChin-Yen Lee rtw_fw_set_wowlan_ctrl_cmd(rtwdev, false); 39044bc17f7SChin-Yen Lee rtw_fw_set_remote_wake_ctrl_cmd(rtwdev, false); 39144bc17f7SChin-Yen Lee 39244bc17f7SChin-Yen Lee return rtw_wow_check_fw_status(rtwdev, false); 39344bc17f7SChin-Yen Lee } 39444bc17f7SChin-Yen Lee 39544bc17f7SChin-Yen Lee static void rtw_wow_avoid_reset_mac(struct rtw_dev *rtwdev) 39644bc17f7SChin-Yen Lee { 39744bc17f7SChin-Yen Lee /* When resuming from wowlan mode, some hosts issue signal 39844bc17f7SChin-Yen Lee * (PCIE: PREST, USB: SE0RST) to device, and lead to reset 39944bc17f7SChin-Yen Lee * mac core. If it happens, the connection to AP will be lost. 40044bc17f7SChin-Yen Lee * Setting REG_RSV_CTRL Register can avoid this process. 40144bc17f7SChin-Yen Lee */ 40244bc17f7SChin-Yen Lee switch (rtw_hci_type(rtwdev)) { 40344bc17f7SChin-Yen Lee case RTW_HCI_TYPE_PCIE: 40444bc17f7SChin-Yen Lee case RTW_HCI_TYPE_USB: 40544bc17f7SChin-Yen Lee rtw_write8(rtwdev, REG_RSV_CTRL, BIT_WLOCK_1C_B6); 40644bc17f7SChin-Yen Lee rtw_write8(rtwdev, REG_RSV_CTRL, 40744bc17f7SChin-Yen Lee BIT_WLOCK_1C_B6 | BIT_R_DIS_PRST); 40844bc17f7SChin-Yen Lee break; 40944bc17f7SChin-Yen Lee default: 41044bc17f7SChin-Yen Lee rtw_warn(rtwdev, "Unsupported hci type to disable reset MAC\n"); 41144bc17f7SChin-Yen Lee break; 41244bc17f7SChin-Yen Lee } 41344bc17f7SChin-Yen Lee } 41444bc17f7SChin-Yen Lee 41544bc17f7SChin-Yen Lee static void rtw_wow_fw_media_status_iter(void *data, struct ieee80211_sta *sta) 41644bc17f7SChin-Yen Lee { 41744bc17f7SChin-Yen Lee struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; 41844bc17f7SChin-Yen Lee struct rtw_fw_media_status_iter_data *iter_data = data; 41944bc17f7SChin-Yen Lee struct rtw_dev *rtwdev = iter_data->rtwdev; 42044bc17f7SChin-Yen Lee 42144bc17f7SChin-Yen Lee rtw_fw_media_status_report(rtwdev, si->mac_id, iter_data->connect); 42244bc17f7SChin-Yen Lee } 42344bc17f7SChin-Yen Lee 42444bc17f7SChin-Yen Lee static void rtw_wow_fw_media_status(struct rtw_dev *rtwdev, bool connect) 42544bc17f7SChin-Yen Lee { 42644bc17f7SChin-Yen Lee struct rtw_fw_media_status_iter_data data; 42744bc17f7SChin-Yen Lee 42844bc17f7SChin-Yen Lee data.rtwdev = rtwdev; 42944bc17f7SChin-Yen Lee data.connect = connect; 43044bc17f7SChin-Yen Lee 43144bc17f7SChin-Yen Lee rtw_iterate_stas_atomic(rtwdev, rtw_wow_fw_media_status_iter, &data); 43244bc17f7SChin-Yen Lee } 43344bc17f7SChin-Yen Lee 434b6c12908SChin-Yen Lee static void rtw_wow_config_pno_rsvd_page(struct rtw_dev *rtwdev) 435b6c12908SChin-Yen Lee { 436b6c12908SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 437b6c12908SChin-Yen Lee struct rtw_pno_request *rtw_pno_req = &rtw_wow->pno_req; 438b6c12908SChin-Yen Lee struct cfg80211_ssid *ssid; 439b6c12908SChin-Yen Lee int i; 440b6c12908SChin-Yen Lee 441b6c12908SChin-Yen Lee for (i = 0 ; i < rtw_pno_req->match_set_cnt; i++) { 442b6c12908SChin-Yen Lee ssid = &rtw_pno_req->match_sets[i].ssid; 443b6c12908SChin-Yen Lee rtw_add_rsvd_page_probe_req(rtwdev, ssid); 444b6c12908SChin-Yen Lee } 445b6c12908SChin-Yen Lee rtw_add_rsvd_page_probe_req(rtwdev, NULL); 446b6c12908SChin-Yen Lee rtw_add_rsvd_page(rtwdev, RSVD_NLO_INFO, false); 447b6c12908SChin-Yen Lee rtw_add_rsvd_page(rtwdev, RSVD_CH_INFO, true); 448b6c12908SChin-Yen Lee } 449b6c12908SChin-Yen Lee 45044bc17f7SChin-Yen Lee static void rtw_wow_config_linked_rsvd_page(struct rtw_dev *rtwdev) 45144bc17f7SChin-Yen Lee { 45244bc17f7SChin-Yen Lee rtw_add_rsvd_page(rtwdev, RSVD_PS_POLL, true); 45344bc17f7SChin-Yen Lee rtw_add_rsvd_page(rtwdev, RSVD_QOS_NULL, true); 45444bc17f7SChin-Yen Lee rtw_add_rsvd_page(rtwdev, RSVD_NULL, true); 45544bc17f7SChin-Yen Lee rtw_add_rsvd_page(rtwdev, RSVD_LPS_PG_DPK, true); 45644bc17f7SChin-Yen Lee rtw_add_rsvd_page(rtwdev, RSVD_LPS_PG_INFO, true); 45744bc17f7SChin-Yen Lee } 45844bc17f7SChin-Yen Lee 45944bc17f7SChin-Yen Lee static void rtw_wow_config_rsvd_page(struct rtw_dev *rtwdev) 46044bc17f7SChin-Yen Lee { 46144bc17f7SChin-Yen Lee rtw_reset_rsvd_page(rtwdev); 46244bc17f7SChin-Yen Lee 463b6c12908SChin-Yen Lee if (rtw_wow_mgd_linked(rtwdev)) { 46444bc17f7SChin-Yen Lee rtw_wow_config_linked_rsvd_page(rtwdev); 465b6c12908SChin-Yen Lee } else if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags) && 466b6c12908SChin-Yen Lee rtw_wow_no_link(rtwdev)) { 467b6c12908SChin-Yen Lee rtw_wow_config_pno_rsvd_page(rtwdev); 468b6c12908SChin-Yen Lee } 46944bc17f7SChin-Yen Lee } 47044bc17f7SChin-Yen Lee 47144bc17f7SChin-Yen Lee static int rtw_wow_dl_fw_rsvd_page(struct rtw_dev *rtwdev) 47244bc17f7SChin-Yen Lee { 47344bc17f7SChin-Yen Lee struct ieee80211_vif *wow_vif = rtwdev->wow.wow_vif; 47444bc17f7SChin-Yen Lee 47544bc17f7SChin-Yen Lee rtw_wow_config_rsvd_page(rtwdev); 47644bc17f7SChin-Yen Lee 47744bc17f7SChin-Yen Lee return rtw_fw_download_rsvd_page(rtwdev, wow_vif); 47844bc17f7SChin-Yen Lee } 47944bc17f7SChin-Yen Lee 48044bc17f7SChin-Yen Lee static int rtw_wow_swap_fw(struct rtw_dev *rtwdev, enum rtw_fw_type type) 48144bc17f7SChin-Yen Lee { 48244bc17f7SChin-Yen Lee struct rtw_fw_state *fw; 48344bc17f7SChin-Yen Lee int ret; 48444bc17f7SChin-Yen Lee 48544bc17f7SChin-Yen Lee switch (type) { 48644bc17f7SChin-Yen Lee case RTW_WOWLAN_FW: 48744bc17f7SChin-Yen Lee fw = &rtwdev->wow_fw; 48844bc17f7SChin-Yen Lee break; 48944bc17f7SChin-Yen Lee 49044bc17f7SChin-Yen Lee case RTW_NORMAL_FW: 49144bc17f7SChin-Yen Lee fw = &rtwdev->fw; 49244bc17f7SChin-Yen Lee break; 49344bc17f7SChin-Yen Lee 49444bc17f7SChin-Yen Lee default: 49544bc17f7SChin-Yen Lee rtw_warn(rtwdev, "unsupported firmware type to swap\n"); 49644bc17f7SChin-Yen Lee return -ENOENT; 49744bc17f7SChin-Yen Lee } 49844bc17f7SChin-Yen Lee 49944bc17f7SChin-Yen Lee ret = rtw_download_firmware(rtwdev, fw); 50044bc17f7SChin-Yen Lee if (ret) 50144bc17f7SChin-Yen Lee goto out; 50244bc17f7SChin-Yen Lee 50344bc17f7SChin-Yen Lee rtw_fw_send_general_info(rtwdev); 50444bc17f7SChin-Yen Lee rtw_fw_send_phydm_info(rtwdev); 50544bc17f7SChin-Yen Lee rtw_wow_fw_media_status(rtwdev, true); 50644bc17f7SChin-Yen Lee 50744bc17f7SChin-Yen Lee out: 50844bc17f7SChin-Yen Lee return ret; 50944bc17f7SChin-Yen Lee } 51044bc17f7SChin-Yen Lee 511b6c12908SChin-Yen Lee static void rtw_wow_check_pno(struct rtw_dev *rtwdev, 512b6c12908SChin-Yen Lee struct cfg80211_sched_scan_request *nd_config) 513b6c12908SChin-Yen Lee { 514b6c12908SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 515b6c12908SChin-Yen Lee struct rtw_pno_request *pno_req = &rtw_wow->pno_req; 516b6c12908SChin-Yen Lee struct ieee80211_channel *channel; 517b6c12908SChin-Yen Lee int i, size; 518b6c12908SChin-Yen Lee 519b6c12908SChin-Yen Lee if (!nd_config->n_match_sets || !nd_config->n_channels) 520b6c12908SChin-Yen Lee goto err; 521b6c12908SChin-Yen Lee 522b6c12908SChin-Yen Lee pno_req->match_set_cnt = nd_config->n_match_sets; 523b6c12908SChin-Yen Lee size = sizeof(*pno_req->match_sets) * pno_req->match_set_cnt; 524b6c12908SChin-Yen Lee pno_req->match_sets = kmemdup(nd_config->match_sets, size, GFP_KERNEL); 525b6c12908SChin-Yen Lee if (!pno_req->match_sets) 526b6c12908SChin-Yen Lee goto err; 527b6c12908SChin-Yen Lee 528b6c12908SChin-Yen Lee pno_req->channel_cnt = nd_config->n_channels; 529b6c12908SChin-Yen Lee size = sizeof(*nd_config->channels[0]) * nd_config->n_channels; 530b6c12908SChin-Yen Lee pno_req->channels = kmalloc(size, GFP_KERNEL); 531b6c12908SChin-Yen Lee if (!pno_req->channels) 532b6c12908SChin-Yen Lee goto channel_err; 533b6c12908SChin-Yen Lee 534b6c12908SChin-Yen Lee for (i = 0 ; i < pno_req->channel_cnt; i++) { 535b6c12908SChin-Yen Lee channel = pno_req->channels + i; 536b6c12908SChin-Yen Lee memcpy(channel, nd_config->channels[i], sizeof(*channel)); 537b6c12908SChin-Yen Lee } 538b6c12908SChin-Yen Lee 539b6c12908SChin-Yen Lee pno_req->scan_plan = *nd_config->scan_plans; 540b6c12908SChin-Yen Lee pno_req->inited = true; 541b6c12908SChin-Yen Lee 542b6c12908SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is enabled\n"); 543b6c12908SChin-Yen Lee 544b6c12908SChin-Yen Lee return; 545b6c12908SChin-Yen Lee 546b6c12908SChin-Yen Lee channel_err: 547b6c12908SChin-Yen Lee kfree(pno_req->match_sets); 548b6c12908SChin-Yen Lee 549b6c12908SChin-Yen Lee err: 550b6c12908SChin-Yen Lee rtw_dbg(rtwdev, RTW_DBG_WOW, "WOW: net-detect is disabled\n"); 551b6c12908SChin-Yen Lee } 552b6c12908SChin-Yen Lee 55344bc17f7SChin-Yen Lee static int rtw_wow_leave_linked_ps(struct rtw_dev *rtwdev) 55444bc17f7SChin-Yen Lee { 55544bc17f7SChin-Yen Lee if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) 55644bc17f7SChin-Yen Lee cancel_delayed_work_sync(&rtwdev->watch_dog_work); 55744bc17f7SChin-Yen Lee 558b6c12908SChin-Yen Lee rtw_leave_lps(rtwdev); 559b6c12908SChin-Yen Lee 560b6c12908SChin-Yen Lee return 0; 561b6c12908SChin-Yen Lee } 562b6c12908SChin-Yen Lee 563b6c12908SChin-Yen Lee static int rtw_wow_leave_no_link_ps(struct rtw_dev *rtwdev) 564b6c12908SChin-Yen Lee { 565b6c12908SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 566b6c12908SChin-Yen Lee int ret = 0; 567b6c12908SChin-Yen Lee 568b6c12908SChin-Yen Lee if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) { 569b6c12908SChin-Yen Lee if (rtw_fw_lps_deep_mode) 570b6c12908SChin-Yen Lee rtw_leave_lps_deep(rtwdev); 571b6c12908SChin-Yen Lee } else { 572b6c12908SChin-Yen Lee if (test_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags)) { 573b6c12908SChin-Yen Lee rtw_wow->ips_enabled = true; 574b6c12908SChin-Yen Lee ret = rtw_leave_ips(rtwdev); 575b6c12908SChin-Yen Lee if (ret) 576b6c12908SChin-Yen Lee return ret; 577b6c12908SChin-Yen Lee } 578b6c12908SChin-Yen Lee } 579b6c12908SChin-Yen Lee 58044bc17f7SChin-Yen Lee return 0; 58144bc17f7SChin-Yen Lee } 58244bc17f7SChin-Yen Lee 58344bc17f7SChin-Yen Lee static int rtw_wow_leave_ps(struct rtw_dev *rtwdev) 58444bc17f7SChin-Yen Lee { 58544bc17f7SChin-Yen Lee int ret = 0; 58644bc17f7SChin-Yen Lee 58744bc17f7SChin-Yen Lee if (rtw_wow_mgd_linked(rtwdev)) 58844bc17f7SChin-Yen Lee ret = rtw_wow_leave_linked_ps(rtwdev); 589b6c12908SChin-Yen Lee else if (rtw_wow_no_link(rtwdev)) 590b6c12908SChin-Yen Lee ret = rtw_wow_leave_no_link_ps(rtwdev); 591b6c12908SChin-Yen Lee 592b6c12908SChin-Yen Lee return ret; 593b6c12908SChin-Yen Lee } 594b6c12908SChin-Yen Lee 595b6c12908SChin-Yen Lee static int rtw_wow_restore_ps(struct rtw_dev *rtwdev) 596b6c12908SChin-Yen Lee { 597b6c12908SChin-Yen Lee int ret = 0; 598b6c12908SChin-Yen Lee 599b6c12908SChin-Yen Lee if (rtw_wow_no_link(rtwdev) && rtwdev->wow.ips_enabled) 600b6c12908SChin-Yen Lee ret = rtw_enter_ips(rtwdev); 60144bc17f7SChin-Yen Lee 60244bc17f7SChin-Yen Lee return ret; 60344bc17f7SChin-Yen Lee } 60444bc17f7SChin-Yen Lee 60544bc17f7SChin-Yen Lee static int rtw_wow_enter_linked_ps(struct rtw_dev *rtwdev) 60644bc17f7SChin-Yen Lee { 60744bc17f7SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 60844bc17f7SChin-Yen Lee struct ieee80211_vif *wow_vif = rtw_wow->wow_vif; 60944bc17f7SChin-Yen Lee struct rtw_vif *rtwvif = (struct rtw_vif *)wow_vif->drv_priv; 61044bc17f7SChin-Yen Lee 61144bc17f7SChin-Yen Lee rtw_enter_lps(rtwdev, rtwvif->port); 61244bc17f7SChin-Yen Lee 61344bc17f7SChin-Yen Lee return 0; 61444bc17f7SChin-Yen Lee } 61544bc17f7SChin-Yen Lee 616b6c12908SChin-Yen Lee static int rtw_wow_enter_no_link_ps(struct rtw_dev *rtwdev) 617b6c12908SChin-Yen Lee { 618b6c12908SChin-Yen Lee /* firmware enters deep ps by itself if supported */ 619b6c12908SChin-Yen Lee set_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags); 620b6c12908SChin-Yen Lee 621b6c12908SChin-Yen Lee return 0; 622b6c12908SChin-Yen Lee } 623b6c12908SChin-Yen Lee 62444bc17f7SChin-Yen Lee static int rtw_wow_enter_ps(struct rtw_dev *rtwdev) 62544bc17f7SChin-Yen Lee { 62644bc17f7SChin-Yen Lee int ret = 0; 62744bc17f7SChin-Yen Lee 62844bc17f7SChin-Yen Lee if (rtw_wow_mgd_linked(rtwdev)) 62944bc17f7SChin-Yen Lee ret = rtw_wow_enter_linked_ps(rtwdev); 630b6c12908SChin-Yen Lee else if (rtw_wow_no_link(rtwdev) && rtw_fw_lps_deep_mode) 631b6c12908SChin-Yen Lee ret = rtw_wow_enter_no_link_ps(rtwdev); 63244bc17f7SChin-Yen Lee 63344bc17f7SChin-Yen Lee return ret; 63444bc17f7SChin-Yen Lee } 63544bc17f7SChin-Yen Lee 63644bc17f7SChin-Yen Lee static void rtw_wow_stop_trx(struct rtw_dev *rtwdev) 63744bc17f7SChin-Yen Lee { 63844bc17f7SChin-Yen Lee rtw_wow_bb_stop(rtwdev); 63944bc17f7SChin-Yen Lee rtw_wow_rx_dma_stop(rtwdev); 64044bc17f7SChin-Yen Lee } 64144bc17f7SChin-Yen Lee 64244bc17f7SChin-Yen Lee static int rtw_wow_start(struct rtw_dev *rtwdev) 64344bc17f7SChin-Yen Lee { 64444bc17f7SChin-Yen Lee int ret; 64544bc17f7SChin-Yen Lee 64644bc17f7SChin-Yen Lee ret = rtw_wow_fw_start(rtwdev); 64744bc17f7SChin-Yen Lee if (ret) 64844bc17f7SChin-Yen Lee goto out; 64944bc17f7SChin-Yen Lee 65044bc17f7SChin-Yen Lee rtw_hci_stop(rtwdev); 65144bc17f7SChin-Yen Lee rtw_wow_bb_start(rtwdev); 65244bc17f7SChin-Yen Lee rtw_wow_avoid_reset_mac(rtwdev); 65344bc17f7SChin-Yen Lee 65444bc17f7SChin-Yen Lee out: 65544bc17f7SChin-Yen Lee return ret; 65644bc17f7SChin-Yen Lee } 65744bc17f7SChin-Yen Lee 65844bc17f7SChin-Yen Lee static int rtw_wow_enable(struct rtw_dev *rtwdev) 65944bc17f7SChin-Yen Lee { 66044bc17f7SChin-Yen Lee int ret = 0; 66144bc17f7SChin-Yen Lee 66244bc17f7SChin-Yen Lee rtw_wow_stop_trx(rtwdev); 66344bc17f7SChin-Yen Lee 66444bc17f7SChin-Yen Lee ret = rtw_wow_swap_fw(rtwdev, RTW_WOWLAN_FW); 66544bc17f7SChin-Yen Lee if (ret) { 66644bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to swap wow fw\n"); 66744bc17f7SChin-Yen Lee goto error; 66844bc17f7SChin-Yen Lee } 66944bc17f7SChin-Yen Lee 67044bc17f7SChin-Yen Lee set_bit(RTW_FLAG_WOWLAN, rtwdev->flags); 67144bc17f7SChin-Yen Lee 67244bc17f7SChin-Yen Lee ret = rtw_wow_dl_fw_rsvd_page(rtwdev); 67344bc17f7SChin-Yen Lee if (ret) { 67444bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to download wowlan rsvd page\n"); 67544bc17f7SChin-Yen Lee goto error; 67644bc17f7SChin-Yen Lee } 67744bc17f7SChin-Yen Lee 67844bc17f7SChin-Yen Lee ret = rtw_wow_start(rtwdev); 67944bc17f7SChin-Yen Lee if (ret) { 68044bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to start wow\n"); 68144bc17f7SChin-Yen Lee goto error; 68244bc17f7SChin-Yen Lee } 68344bc17f7SChin-Yen Lee 68444bc17f7SChin-Yen Lee return ret; 68544bc17f7SChin-Yen Lee 68644bc17f7SChin-Yen Lee error: 68744bc17f7SChin-Yen Lee clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags); 68844bc17f7SChin-Yen Lee return ret; 68944bc17f7SChin-Yen Lee } 69044bc17f7SChin-Yen Lee 69144bc17f7SChin-Yen Lee static int rtw_wow_stop(struct rtw_dev *rtwdev) 69244bc17f7SChin-Yen Lee { 69344bc17f7SChin-Yen Lee int ret; 69444bc17f7SChin-Yen Lee 69544bc17f7SChin-Yen Lee /* some HCI related registers will be reset after resume, 69644bc17f7SChin-Yen Lee * need to set them again. 69744bc17f7SChin-Yen Lee */ 69844bc17f7SChin-Yen Lee ret = rtw_hci_setup(rtwdev); 69944bc17f7SChin-Yen Lee if (ret) { 70044bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to setup hci\n"); 70144bc17f7SChin-Yen Lee return ret; 70244bc17f7SChin-Yen Lee } 70344bc17f7SChin-Yen Lee 70444bc17f7SChin-Yen Lee ret = rtw_hci_start(rtwdev); 70544bc17f7SChin-Yen Lee if (ret) { 70644bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to start hci\n"); 70744bc17f7SChin-Yen Lee return ret; 70844bc17f7SChin-Yen Lee } 70944bc17f7SChin-Yen Lee 71044bc17f7SChin-Yen Lee ret = rtw_wow_fw_stop(rtwdev); 71144bc17f7SChin-Yen Lee if (ret) 71244bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to stop wowlan fw\n"); 71344bc17f7SChin-Yen Lee 71444bc17f7SChin-Yen Lee rtw_wow_bb_stop(rtwdev); 71544bc17f7SChin-Yen Lee 71644bc17f7SChin-Yen Lee return ret; 71744bc17f7SChin-Yen Lee } 71844bc17f7SChin-Yen Lee 71944bc17f7SChin-Yen Lee static void rtw_wow_resume_trx(struct rtw_dev *rtwdev) 72044bc17f7SChin-Yen Lee { 72144bc17f7SChin-Yen Lee rtw_wow_rx_dma_start(rtwdev); 72244bc17f7SChin-Yen Lee rtw_wow_bb_start(rtwdev); 72344bc17f7SChin-Yen Lee ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->watch_dog_work, 72444bc17f7SChin-Yen Lee RTW_WATCH_DOG_DELAY_TIME); 72544bc17f7SChin-Yen Lee } 72644bc17f7SChin-Yen Lee 72744bc17f7SChin-Yen Lee static int rtw_wow_disable(struct rtw_dev *rtwdev) 72844bc17f7SChin-Yen Lee { 72944bc17f7SChin-Yen Lee int ret; 73044bc17f7SChin-Yen Lee 73144bc17f7SChin-Yen Lee clear_bit(RTW_FLAG_WOWLAN, rtwdev->flags); 73244bc17f7SChin-Yen Lee 73344bc17f7SChin-Yen Lee ret = rtw_wow_stop(rtwdev); 73444bc17f7SChin-Yen Lee if (ret) { 73544bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to stop wow\n"); 73644bc17f7SChin-Yen Lee goto out; 73744bc17f7SChin-Yen Lee } 73844bc17f7SChin-Yen Lee 73944bc17f7SChin-Yen Lee ret = rtw_wow_swap_fw(rtwdev, RTW_NORMAL_FW); 74044bc17f7SChin-Yen Lee if (ret) { 74144bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to swap normal fw\n"); 74244bc17f7SChin-Yen Lee goto out; 74344bc17f7SChin-Yen Lee } 74444bc17f7SChin-Yen Lee 74544bc17f7SChin-Yen Lee ret = rtw_wow_dl_fw_rsvd_page(rtwdev); 74644bc17f7SChin-Yen Lee if (ret) 74744bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to download normal rsvd page\n"); 74844bc17f7SChin-Yen Lee 74944bc17f7SChin-Yen Lee out: 75044bc17f7SChin-Yen Lee rtw_wow_resume_trx(rtwdev); 75144bc17f7SChin-Yen Lee return ret; 75244bc17f7SChin-Yen Lee } 75344bc17f7SChin-Yen Lee 75444bc17f7SChin-Yen Lee static void rtw_wow_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) 75544bc17f7SChin-Yen Lee { 75644bc17f7SChin-Yen Lee struct rtw_dev *rtwdev = data; 75744bc17f7SChin-Yen Lee struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; 75844bc17f7SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 75944bc17f7SChin-Yen Lee 76044bc17f7SChin-Yen Lee /* Current wowlan function support setting of only one STATION vif. 76144bc17f7SChin-Yen Lee * So when one suitable vif is found, stop the iteration. 76244bc17f7SChin-Yen Lee */ 76344bc17f7SChin-Yen Lee if (rtw_wow->wow_vif || vif->type != NL80211_IFTYPE_STATION) 76444bc17f7SChin-Yen Lee return; 76544bc17f7SChin-Yen Lee 76644bc17f7SChin-Yen Lee switch (rtwvif->net_type) { 76744bc17f7SChin-Yen Lee case RTW_NET_MGD_LINKED: 76844bc17f7SChin-Yen Lee rtw_wow->wow_vif = vif; 76944bc17f7SChin-Yen Lee break; 770b6c12908SChin-Yen Lee case RTW_NET_NO_LINK: 771b6c12908SChin-Yen Lee if (rtw_wow->pno_req.inited) 772b6c12908SChin-Yen Lee rtwdev->wow.wow_vif = vif; 773b6c12908SChin-Yen Lee break; 77444bc17f7SChin-Yen Lee default: 77544bc17f7SChin-Yen Lee break; 77644bc17f7SChin-Yen Lee } 77744bc17f7SChin-Yen Lee } 77844bc17f7SChin-Yen Lee 77944bc17f7SChin-Yen Lee static int rtw_wow_set_wakeups(struct rtw_dev *rtwdev, 78044bc17f7SChin-Yen Lee struct cfg80211_wowlan *wowlan) 78144bc17f7SChin-Yen Lee { 78244bc17f7SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 783e3e400dfSChin-Yen Lee struct rtw_wow_pattern *rtw_patterns = rtw_wow->patterns; 784e3e400dfSChin-Yen Lee struct rtw_vif *rtwvif; 785e3e400dfSChin-Yen Lee int i; 78644bc17f7SChin-Yen Lee 78744bc17f7SChin-Yen Lee if (wowlan->disconnect) 78844bc17f7SChin-Yen Lee set_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags); 78944bc17f7SChin-Yen Lee if (wowlan->magic_pkt) 79044bc17f7SChin-Yen Lee set_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags); 79144bc17f7SChin-Yen Lee if (wowlan->gtk_rekey_failure) 79244bc17f7SChin-Yen Lee set_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags); 79344bc17f7SChin-Yen Lee 794b6c12908SChin-Yen Lee if (wowlan->nd_config) 795b6c12908SChin-Yen Lee rtw_wow_check_pno(rtwdev, wowlan->nd_config); 796b6c12908SChin-Yen Lee 79744bc17f7SChin-Yen Lee rtw_iterate_vifs_atomic(rtwdev, rtw_wow_vif_iter, rtwdev); 79844bc17f7SChin-Yen Lee if (!rtw_wow->wow_vif) 79944bc17f7SChin-Yen Lee return -EPERM; 80044bc17f7SChin-Yen Lee 801e3e400dfSChin-Yen Lee rtwvif = (struct rtw_vif *)rtw_wow->wow_vif->drv_priv; 802e3e400dfSChin-Yen Lee if (wowlan->n_patterns && wowlan->patterns) { 803e3e400dfSChin-Yen Lee rtw_wow->pattern_cnt = wowlan->n_patterns; 804e3e400dfSChin-Yen Lee for (i = 0; i < wowlan->n_patterns; i++) 805e3e400dfSChin-Yen Lee rtw_wow_pattern_generate(rtwdev, rtwvif, 806e3e400dfSChin-Yen Lee wowlan->patterns + i, 807e3e400dfSChin-Yen Lee rtw_patterns + i); 808e3e400dfSChin-Yen Lee } 809e3e400dfSChin-Yen Lee 81044bc17f7SChin-Yen Lee return 0; 81144bc17f7SChin-Yen Lee } 81244bc17f7SChin-Yen Lee 81344bc17f7SChin-Yen Lee static void rtw_wow_clear_wakeups(struct rtw_dev *rtwdev) 81444bc17f7SChin-Yen Lee { 81544bc17f7SChin-Yen Lee struct rtw_wow_param *rtw_wow = &rtwdev->wow; 816b6c12908SChin-Yen Lee struct rtw_pno_request *pno_req = &rtw_wow->pno_req; 817b6c12908SChin-Yen Lee 818b6c12908SChin-Yen Lee if (pno_req->inited) { 819b6c12908SChin-Yen Lee kfree(pno_req->channels); 820b6c12908SChin-Yen Lee kfree(pno_req->match_sets); 821b6c12908SChin-Yen Lee } 82244bc17f7SChin-Yen Lee 82344bc17f7SChin-Yen Lee memset(rtw_wow, 0, sizeof(rtwdev->wow)); 82444bc17f7SChin-Yen Lee } 82544bc17f7SChin-Yen Lee 82644bc17f7SChin-Yen Lee int rtw_wow_suspend(struct rtw_dev *rtwdev, struct cfg80211_wowlan *wowlan) 82744bc17f7SChin-Yen Lee { 82844bc17f7SChin-Yen Lee int ret = 0; 82944bc17f7SChin-Yen Lee 83044bc17f7SChin-Yen Lee ret = rtw_wow_set_wakeups(rtwdev, wowlan); 83144bc17f7SChin-Yen Lee if (ret) { 83244bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to set wakeup event\n"); 83344bc17f7SChin-Yen Lee goto out; 83444bc17f7SChin-Yen Lee } 83544bc17f7SChin-Yen Lee 83644bc17f7SChin-Yen Lee ret = rtw_wow_leave_ps(rtwdev); 83744bc17f7SChin-Yen Lee if (ret) { 83844bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to leave ps from normal mode\n"); 83944bc17f7SChin-Yen Lee goto out; 84044bc17f7SChin-Yen Lee } 84144bc17f7SChin-Yen Lee 84244bc17f7SChin-Yen Lee ret = rtw_wow_enable(rtwdev); 84344bc17f7SChin-Yen Lee if (ret) { 84444bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to enable wow\n"); 845b6c12908SChin-Yen Lee rtw_wow_restore_ps(rtwdev); 84644bc17f7SChin-Yen Lee goto out; 84744bc17f7SChin-Yen Lee } 84844bc17f7SChin-Yen Lee 84944bc17f7SChin-Yen Lee ret = rtw_wow_enter_ps(rtwdev); 85044bc17f7SChin-Yen Lee if (ret) 85144bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to enter ps for wow\n"); 85244bc17f7SChin-Yen Lee 85344bc17f7SChin-Yen Lee out: 85444bc17f7SChin-Yen Lee return ret; 85544bc17f7SChin-Yen Lee } 85644bc17f7SChin-Yen Lee 85744bc17f7SChin-Yen Lee int rtw_wow_resume(struct rtw_dev *rtwdev) 85844bc17f7SChin-Yen Lee { 85944bc17f7SChin-Yen Lee int ret; 86044bc17f7SChin-Yen Lee 86144bc17f7SChin-Yen Lee /* If wowlan mode is not enabled, do nothing */ 86244bc17f7SChin-Yen Lee if (!test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) { 86344bc17f7SChin-Yen Lee rtw_err(rtwdev, "wow is not enabled\n"); 86444bc17f7SChin-Yen Lee ret = -EPERM; 86544bc17f7SChin-Yen Lee goto out; 86644bc17f7SChin-Yen Lee } 86744bc17f7SChin-Yen Lee 86844bc17f7SChin-Yen Lee ret = rtw_wow_leave_ps(rtwdev); 86944bc17f7SChin-Yen Lee if (ret) { 87044bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to leave ps from wowlan mode\n"); 87144bc17f7SChin-Yen Lee goto out; 87244bc17f7SChin-Yen Lee } 87344bc17f7SChin-Yen Lee 87444bc17f7SChin-Yen Lee rtw_wow_show_wakeup_reason(rtwdev); 87544bc17f7SChin-Yen Lee 87644bc17f7SChin-Yen Lee ret = rtw_wow_disable(rtwdev); 877b6c12908SChin-Yen Lee if (ret) { 87844bc17f7SChin-Yen Lee rtw_err(rtwdev, "failed to disable wow\n"); 879b6c12908SChin-Yen Lee goto out; 880b6c12908SChin-Yen Lee } 881b6c12908SChin-Yen Lee 882b6c12908SChin-Yen Lee ret = rtw_wow_restore_ps(rtwdev); 883b6c12908SChin-Yen Lee if (ret) 884b6c12908SChin-Yen Lee rtw_err(rtwdev, "failed to restore ps to normal mode\n"); 88544bc17f7SChin-Yen Lee 88644bc17f7SChin-Yen Lee out: 88744bc17f7SChin-Yen Lee rtw_wow_clear_wakeups(rtwdev); 88844bc17f7SChin-Yen Lee return ret; 88944bc17f7SChin-Yen Lee } 890