1e3ec7017SPing-Ke Shih // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2e3ec7017SPing-Ke Shih /* Copyright(c) 2019-2020 Realtek Corporation 3e3ec7017SPing-Ke Shih */ 4e3ec7017SPing-Ke Shih 5e3ec7017SPing-Ke Shih #include "coex.h" 6e3ec7017SPing-Ke Shih #include "core.h" 7e3ec7017SPing-Ke Shih #include "debug.h" 8e3ec7017SPing-Ke Shih #include "fw.h" 9e3ec7017SPing-Ke Shih #include "mac.h" 10e3ec7017SPing-Ke Shih #include "ps.h" 11e3ec7017SPing-Ke Shih #include "reg.h" 12e3ec7017SPing-Ke Shih #include "util.h" 13e3ec7017SPing-Ke Shih 14e3ec7017SPing-Ke Shih static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid) 15e3ec7017SPing-Ke Shih { 16e3ec7017SPing-Ke Shih u32 pwr_en_bit = 0xE; 17e3ec7017SPing-Ke Shih u32 chk_msk = pwr_en_bit << (4 * macid); 18e3ec7017SPing-Ke Shih u32 polling; 19e3ec7017SPing-Ke Shih int ret; 20e3ec7017SPing-Ke Shih 21e3ec7017SPing-Ke Shih ret = read_poll_timeout_atomic(rtw89_read32_mask, polling, !polling, 22e3ec7017SPing-Ke Shih 1000, 50000, false, rtwdev, 23e3ec7017SPing-Ke Shih R_AX_PPWRBIT_SETTING, chk_msk); 24e3ec7017SPing-Ke Shih if (ret) { 25e3ec7017SPing-Ke Shih rtw89_info(rtwdev, "rtw89: failed to leave lps state\n"); 26e3ec7017SPing-Ke Shih return -EBUSY; 27e3ec7017SPing-Ke Shih } 28e3ec7017SPing-Ke Shih 29e3ec7017SPing-Ke Shih return 0; 30e3ec7017SPing-Ke Shih } 31e3ec7017SPing-Ke Shih 3252edbb9fSPing-Ke Shih static void rtw89_ps_power_mode_change_with_hci(struct rtw89_dev *rtwdev, 3352edbb9fSPing-Ke Shih bool enter) 3452edbb9fSPing-Ke Shih { 3552edbb9fSPing-Ke Shih ieee80211_stop_queues(rtwdev->hw); 3652edbb9fSPing-Ke Shih rtwdev->hci.paused = true; 3752edbb9fSPing-Ke Shih flush_work(&rtwdev->txq_work); 3852edbb9fSPing-Ke Shih ieee80211_wake_queues(rtwdev->hw); 3952edbb9fSPing-Ke Shih 4052edbb9fSPing-Ke Shih rtw89_hci_pause(rtwdev, true); 4152edbb9fSPing-Ke Shih rtw89_mac_power_mode_change(rtwdev, enter); 4252edbb9fSPing-Ke Shih rtw89_hci_switch_mode(rtwdev, enter); 4352edbb9fSPing-Ke Shih rtw89_hci_pause(rtwdev, false); 4452edbb9fSPing-Ke Shih 4552edbb9fSPing-Ke Shih rtwdev->hci.paused = false; 4652edbb9fSPing-Ke Shih 4752edbb9fSPing-Ke Shih if (!enter) { 4852edbb9fSPing-Ke Shih local_bh_disable(); 4952edbb9fSPing-Ke Shih napi_schedule(&rtwdev->napi); 5052edbb9fSPing-Ke Shih local_bh_enable(); 5152edbb9fSPing-Ke Shih } 5252edbb9fSPing-Ke Shih } 5352edbb9fSPing-Ke Shih 5452edbb9fSPing-Ke Shih static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter) 5552edbb9fSPing-Ke Shih { 5652edbb9fSPing-Ke Shih if (rtwdev->chip->low_power_hci_modes & BIT(rtwdev->ps_mode)) 5752edbb9fSPing-Ke Shih rtw89_ps_power_mode_change_with_hci(rtwdev, enter); 5852edbb9fSPing-Ke Shih else 5952edbb9fSPing-Ke Shih rtw89_mac_power_mode_change(rtwdev, enter); 6052edbb9fSPing-Ke Shih } 6152edbb9fSPing-Ke Shih 62*19e28c7fSChin-Yen Lee void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) 63e3ec7017SPing-Ke Shih { 64487b7b70SPing-Ke Shih if (rtwvif->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) 65487b7b70SPing-Ke Shih return; 66487b7b70SPing-Ke Shih 67e3ec7017SPing-Ke Shih if (!rtwdev->ps_mode) 68e3ec7017SPing-Ke Shih return; 69e3ec7017SPing-Ke Shih 70e3ec7017SPing-Ke Shih if (test_and_set_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) 71e3ec7017SPing-Ke Shih return; 72e3ec7017SPing-Ke Shih 7352edbb9fSPing-Ke Shih rtw89_ps_power_mode_change(rtwdev, true); 74e3ec7017SPing-Ke Shih } 75e3ec7017SPing-Ke Shih 76e3ec7017SPing-Ke Shih void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev) 77e3ec7017SPing-Ke Shih { 78e3ec7017SPing-Ke Shih if (!rtwdev->ps_mode) 79e3ec7017SPing-Ke Shih return; 80e3ec7017SPing-Ke Shih 81e3ec7017SPing-Ke Shih if (test_and_clear_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) 8252edbb9fSPing-Ke Shih rtw89_ps_power_mode_change(rtwdev, false); 83e3ec7017SPing-Ke Shih } 84e3ec7017SPing-Ke Shih 85e3ec7017SPing-Ke Shih static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id) 86e3ec7017SPing-Ke Shih { 87e3ec7017SPing-Ke Shih struct rtw89_lps_parm lps_param = { 88e3ec7017SPing-Ke Shih .macid = mac_id, 89e3ec7017SPing-Ke Shih .psmode = RTW89_MAC_AX_PS_MODE_LEGACY, 90e3ec7017SPing-Ke Shih .lastrpwm = RTW89_LAST_RPWM_PS, 91e3ec7017SPing-Ke Shih }; 92e3ec7017SPing-Ke Shih 93e3ec7017SPing-Ke Shih rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_FW_CTRL); 94e3ec7017SPing-Ke Shih rtw89_fw_h2c_lps_parm(rtwdev, &lps_param); 95e3ec7017SPing-Ke Shih } 96e3ec7017SPing-Ke Shih 97e3ec7017SPing-Ke Shih static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id) 98e3ec7017SPing-Ke Shih { 99e3ec7017SPing-Ke Shih struct rtw89_lps_parm lps_param = { 100e3ec7017SPing-Ke Shih .macid = mac_id, 101e3ec7017SPing-Ke Shih .psmode = RTW89_MAC_AX_PS_MODE_ACTIVE, 102e3ec7017SPing-Ke Shih .lastrpwm = RTW89_LAST_RPWM_ACTIVE, 103e3ec7017SPing-Ke Shih }; 104e3ec7017SPing-Ke Shih 105e3ec7017SPing-Ke Shih rtw89_fw_h2c_lps_parm(rtwdev, &lps_param); 106e3ec7017SPing-Ke Shih rtw89_fw_leave_lps_check(rtwdev, 0); 107e3ec7017SPing-Ke Shih rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON); 108e3ec7017SPing-Ke Shih } 109e3ec7017SPing-Ke Shih 110e3ec7017SPing-Ke Shih void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev) 111e3ec7017SPing-Ke Shih { 112e3ec7017SPing-Ke Shih lockdep_assert_held(&rtwdev->mutex); 113e3ec7017SPing-Ke Shih 114e3ec7017SPing-Ke Shih __rtw89_leave_ps_mode(rtwdev); 115e3ec7017SPing-Ke Shih } 116e3ec7017SPing-Ke Shih 117487b7b70SPing-Ke Shih void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) 118e3ec7017SPing-Ke Shih { 119e3ec7017SPing-Ke Shih lockdep_assert_held(&rtwdev->mutex); 120e3ec7017SPing-Ke Shih 121e3ec7017SPing-Ke Shih if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) 122e3ec7017SPing-Ke Shih return; 123e3ec7017SPing-Ke Shih 124487b7b70SPing-Ke Shih __rtw89_enter_lps(rtwdev, rtwvif->mac_id); 125487b7b70SPing-Ke Shih __rtw89_enter_ps_mode(rtwdev, rtwvif); 126e3ec7017SPing-Ke Shih } 127e3ec7017SPing-Ke Shih 128e3ec7017SPing-Ke Shih static void rtw89_leave_lps_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) 129e3ec7017SPing-Ke Shih { 130487b7b70SPing-Ke Shih if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION && 131487b7b70SPing-Ke Shih rtwvif->wifi_role != RTW89_WIFI_ROLE_P2P_CLIENT) 132e3ec7017SPing-Ke Shih return; 133e3ec7017SPing-Ke Shih 134e3ec7017SPing-Ke Shih __rtw89_leave_lps(rtwdev, rtwvif->mac_id); 135e3ec7017SPing-Ke Shih } 136e3ec7017SPing-Ke Shih 137e3ec7017SPing-Ke Shih void rtw89_leave_lps(struct rtw89_dev *rtwdev) 138e3ec7017SPing-Ke Shih { 139e3ec7017SPing-Ke Shih struct rtw89_vif *rtwvif; 140e3ec7017SPing-Ke Shih 141e3ec7017SPing-Ke Shih lockdep_assert_held(&rtwdev->mutex); 142e3ec7017SPing-Ke Shih 143e3ec7017SPing-Ke Shih if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) 144e3ec7017SPing-Ke Shih return; 145e3ec7017SPing-Ke Shih 146674ece27SZong-Zhe Yang __rtw89_leave_ps_mode(rtwdev); 147674ece27SZong-Zhe Yang 148e3ec7017SPing-Ke Shih rtw89_for_each_rtwvif(rtwdev, rtwvif) 149e3ec7017SPing-Ke Shih rtw89_leave_lps_vif(rtwdev, rtwvif); 150e3ec7017SPing-Ke Shih } 151e3ec7017SPing-Ke Shih 152e3ec7017SPing-Ke Shih void rtw89_enter_ips(struct rtw89_dev *rtwdev) 153e3ec7017SPing-Ke Shih { 154e3ec7017SPing-Ke Shih struct rtw89_vif *rtwvif; 155e3ec7017SPing-Ke Shih 156e3ec7017SPing-Ke Shih set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags); 157e3ec7017SPing-Ke Shih 158e3ec7017SPing-Ke Shih rtw89_for_each_rtwvif(rtwdev, rtwvif) 159e3ec7017SPing-Ke Shih rtw89_mac_vif_deinit(rtwdev, rtwvif); 160e3ec7017SPing-Ke Shih 161e3ec7017SPing-Ke Shih rtw89_core_stop(rtwdev); 162e3ec7017SPing-Ke Shih } 163e3ec7017SPing-Ke Shih 164e3ec7017SPing-Ke Shih void rtw89_leave_ips(struct rtw89_dev *rtwdev) 165e3ec7017SPing-Ke Shih { 166e3ec7017SPing-Ke Shih struct rtw89_vif *rtwvif; 167e3ec7017SPing-Ke Shih int ret; 168e3ec7017SPing-Ke Shih 169e3ec7017SPing-Ke Shih ret = rtw89_core_start(rtwdev); 170e3ec7017SPing-Ke Shih if (ret) 171e3ec7017SPing-Ke Shih rtw89_err(rtwdev, "failed to leave idle state\n"); 172e3ec7017SPing-Ke Shih 173e3ec7017SPing-Ke Shih rtw89_set_channel(rtwdev); 174e3ec7017SPing-Ke Shih 175e3ec7017SPing-Ke Shih rtw89_for_each_rtwvif(rtwdev, rtwvif) 176e3ec7017SPing-Ke Shih rtw89_mac_vif_init(rtwdev, rtwvif); 177e3ec7017SPing-Ke Shih 178e3ec7017SPing-Ke Shih clear_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags); 179e3ec7017SPing-Ke Shih } 180e3ec7017SPing-Ke Shih 181e3ec7017SPing-Ke Shih void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl) 182e3ec7017SPing-Ke Shih { 183e3ec7017SPing-Ke Shih if (btc_ctrl) 184e3ec7017SPing-Ke Shih rtw89_leave_lps(rtwdev); 185e3ec7017SPing-Ke Shih } 186f4a43c3bSDian-Syuan Yang 187f4a43c3bSDian-Syuan Yang static void rtw89_tsf32_toggle(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, 188f4a43c3bSDian-Syuan Yang enum rtw89_p2pps_action act) 189f4a43c3bSDian-Syuan Yang { 190f4a43c3bSDian-Syuan Yang if (act == RTW89_P2P_ACT_UPDATE || act == RTW89_P2P_ACT_REMOVE) 191f4a43c3bSDian-Syuan Yang return; 192f4a43c3bSDian-Syuan Yang 193f4a43c3bSDian-Syuan Yang if (act == RTW89_P2P_ACT_INIT) 194f4a43c3bSDian-Syuan Yang rtw89_fw_h2c_tsf32_toggle(rtwdev, rtwvif, true); 195f4a43c3bSDian-Syuan Yang else if (act == RTW89_P2P_ACT_TERMINATE) 196f4a43c3bSDian-Syuan Yang rtw89_fw_h2c_tsf32_toggle(rtwdev, rtwvif, false); 197f4a43c3bSDian-Syuan Yang } 198f4a43c3bSDian-Syuan Yang 199f4a43c3bSDian-Syuan Yang static void rtw89_p2p_disable_all_noa(struct rtw89_dev *rtwdev, 200f4a43c3bSDian-Syuan Yang struct ieee80211_vif *vif) 201f4a43c3bSDian-Syuan Yang { 202f4a43c3bSDian-Syuan Yang struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; 203f4a43c3bSDian-Syuan Yang enum rtw89_p2pps_action act; 204f4a43c3bSDian-Syuan Yang u8 noa_id; 205f4a43c3bSDian-Syuan Yang 206f4a43c3bSDian-Syuan Yang if (rtwvif->last_noa_nr == 0) 207f4a43c3bSDian-Syuan Yang return; 208f4a43c3bSDian-Syuan Yang 209f4a43c3bSDian-Syuan Yang for (noa_id = 0; noa_id < rtwvif->last_noa_nr; noa_id++) { 210f4a43c3bSDian-Syuan Yang if (noa_id == rtwvif->last_noa_nr - 1) 211f4a43c3bSDian-Syuan Yang act = RTW89_P2P_ACT_TERMINATE; 212f4a43c3bSDian-Syuan Yang else 213f4a43c3bSDian-Syuan Yang act = RTW89_P2P_ACT_REMOVE; 214f4a43c3bSDian-Syuan Yang rtw89_tsf32_toggle(rtwdev, rtwvif, act); 215f4a43c3bSDian-Syuan Yang rtw89_fw_h2c_p2p_act(rtwdev, vif, NULL, act, noa_id); 216f4a43c3bSDian-Syuan Yang } 217f4a43c3bSDian-Syuan Yang } 218f4a43c3bSDian-Syuan Yang 219f4a43c3bSDian-Syuan Yang static void rtw89_p2p_update_noa(struct rtw89_dev *rtwdev, 220f4a43c3bSDian-Syuan Yang struct ieee80211_vif *vif) 221f4a43c3bSDian-Syuan Yang { 222f4a43c3bSDian-Syuan Yang struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; 223f4a43c3bSDian-Syuan Yang struct ieee80211_p2p_noa_desc *desc; 224f4a43c3bSDian-Syuan Yang enum rtw89_p2pps_action act; 225f4a43c3bSDian-Syuan Yang u8 noa_id; 226f4a43c3bSDian-Syuan Yang 227f4a43c3bSDian-Syuan Yang for (noa_id = 0; noa_id < RTW89_P2P_MAX_NOA_NUM; noa_id++) { 228f4a43c3bSDian-Syuan Yang desc = &vif->bss_conf.p2p_noa_attr.desc[noa_id]; 229f4a43c3bSDian-Syuan Yang if (!desc->count || !desc->duration) 230f4a43c3bSDian-Syuan Yang break; 231f4a43c3bSDian-Syuan Yang 232f4a43c3bSDian-Syuan Yang if (noa_id == 0) 233f4a43c3bSDian-Syuan Yang act = RTW89_P2P_ACT_INIT; 234f4a43c3bSDian-Syuan Yang else 235f4a43c3bSDian-Syuan Yang act = RTW89_P2P_ACT_UPDATE; 236f4a43c3bSDian-Syuan Yang rtw89_tsf32_toggle(rtwdev, rtwvif, act); 237f4a43c3bSDian-Syuan Yang rtw89_fw_h2c_p2p_act(rtwdev, vif, desc, act, noa_id); 238f4a43c3bSDian-Syuan Yang } 239f4a43c3bSDian-Syuan Yang rtwvif->last_noa_nr = noa_id; 240f4a43c3bSDian-Syuan Yang } 241f4a43c3bSDian-Syuan Yang 242f4a43c3bSDian-Syuan Yang void rtw89_process_p2p_ps(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif) 243f4a43c3bSDian-Syuan Yang { 244f4a43c3bSDian-Syuan Yang rtw89_p2p_disable_all_noa(rtwdev, vif); 245f4a43c3bSDian-Syuan Yang rtw89_p2p_update_noa(rtwdev, vif); 246f4a43c3bSDian-Syuan Yang } 247