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