1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /* Copyright(c) 2019-2020 Realtek Corporation
3 */
4
5 #include "coex.h"
6 #include "core.h"
7 #include "debug.h"
8 #include "fw.h"
9 #include "mac.h"
10 #include "ps.h"
11 #include "reg.h"
12 #include "util.h"
13
rtw89_fw_leave_lps_check(struct rtw89_dev * rtwdev,u8 macid)14 static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid)
15 {
16 u32 pwr_en_bit = 0xE;
17 u32 chk_msk = pwr_en_bit << (4 * macid);
18 u32 polling;
19 int ret;
20
21 ret = read_poll_timeout_atomic(rtw89_read32_mask, polling, !polling,
22 1000, 50000, false, rtwdev,
23 R_AX_PPWRBIT_SETTING, chk_msk);
24 if (ret) {
25 rtw89_info(rtwdev, "rtw89: failed to leave lps state\n");
26 return -EBUSY;
27 }
28
29 return 0;
30 }
31
rtw89_ps_power_mode_change_with_hci(struct rtw89_dev * rtwdev,bool enter)32 static void rtw89_ps_power_mode_change_with_hci(struct rtw89_dev *rtwdev,
33 bool enter)
34 {
35 ieee80211_stop_queues(rtwdev->hw);
36 rtwdev->hci.paused = true;
37 flush_work(&rtwdev->txq_work);
38 ieee80211_wake_queues(rtwdev->hw);
39
40 rtw89_hci_pause(rtwdev, true);
41 rtw89_mac_power_mode_change(rtwdev, enter);
42 rtw89_hci_switch_mode(rtwdev, enter);
43 rtw89_hci_pause(rtwdev, false);
44
45 rtwdev->hci.paused = false;
46
47 if (!enter) {
48 local_bh_disable();
49 napi_schedule(&rtwdev->napi);
50 local_bh_enable();
51 }
52 }
53
rtw89_ps_power_mode_change(struct rtw89_dev * rtwdev,bool enter)54 static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter)
55 {
56 if (rtwdev->chip->low_power_hci_modes & BIT(rtwdev->ps_mode))
57 rtw89_ps_power_mode_change_with_hci(rtwdev, enter);
58 else
59 rtw89_mac_power_mode_change(rtwdev, enter);
60 }
61
__rtw89_enter_ps_mode(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif)62 void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
63 {
64 if (rtwvif->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT)
65 return;
66
67 if (!rtwdev->ps_mode)
68 return;
69
70 if (test_and_set_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
71 return;
72
73 rtw89_ps_power_mode_change(rtwdev, true);
74 }
75
__rtw89_leave_ps_mode(struct rtw89_dev * rtwdev)76 void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
77 {
78 if (!rtwdev->ps_mode)
79 return;
80
81 if (test_and_clear_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
82 rtw89_ps_power_mode_change(rtwdev, false);
83 }
84
__rtw89_enter_lps(struct rtw89_dev * rtwdev,u8 mac_id)85 static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
86 {
87 struct rtw89_lps_parm lps_param = {
88 .macid = mac_id,
89 .psmode = RTW89_MAC_AX_PS_MODE_LEGACY,
90 .lastrpwm = RTW89_LAST_RPWM_PS,
91 };
92
93 rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_FW_CTRL);
94 rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
95 }
96
__rtw89_leave_lps(struct rtw89_dev * rtwdev,u8 mac_id)97 static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id)
98 {
99 struct rtw89_lps_parm lps_param = {
100 .macid = mac_id,
101 .psmode = RTW89_MAC_AX_PS_MODE_ACTIVE,
102 .lastrpwm = RTW89_LAST_RPWM_ACTIVE,
103 };
104
105 rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
106 rtw89_fw_leave_lps_check(rtwdev, 0);
107 rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
108 }
109
rtw89_leave_ps_mode(struct rtw89_dev * rtwdev)110 void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
111 {
112 lockdep_assert_held(&rtwdev->mutex);
113
114 __rtw89_leave_ps_mode(rtwdev);
115 }
116
rtw89_enter_lps(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif)117 void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
118 {
119 lockdep_assert_held(&rtwdev->mutex);
120
121 if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
122 return;
123
124 __rtw89_enter_lps(rtwdev, rtwvif->mac_id);
125 __rtw89_enter_ps_mode(rtwdev, rtwvif);
126 }
127
rtw89_leave_lps_vif(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif)128 static void rtw89_leave_lps_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
129 {
130 if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION &&
131 rtwvif->wifi_role != RTW89_WIFI_ROLE_P2P_CLIENT)
132 return;
133
134 __rtw89_leave_lps(rtwdev, rtwvif->mac_id);
135 }
136
rtw89_leave_lps(struct rtw89_dev * rtwdev)137 void rtw89_leave_lps(struct rtw89_dev *rtwdev)
138 {
139 struct rtw89_vif *rtwvif;
140
141 lockdep_assert_held(&rtwdev->mutex);
142
143 if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
144 return;
145
146 __rtw89_leave_ps_mode(rtwdev);
147
148 rtw89_for_each_rtwvif(rtwdev, rtwvif)
149 rtw89_leave_lps_vif(rtwdev, rtwvif);
150 }
151
rtw89_enter_ips(struct rtw89_dev * rtwdev)152 void rtw89_enter_ips(struct rtw89_dev *rtwdev)
153 {
154 struct rtw89_vif *rtwvif;
155
156 set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
157
158 rtw89_for_each_rtwvif(rtwdev, rtwvif)
159 rtw89_mac_vif_deinit(rtwdev, rtwvif);
160
161 rtw89_core_stop(rtwdev);
162 }
163
rtw89_leave_ips(struct rtw89_dev * rtwdev)164 void rtw89_leave_ips(struct rtw89_dev *rtwdev)
165 {
166 struct rtw89_vif *rtwvif;
167 int ret;
168
169 ret = rtw89_core_start(rtwdev);
170 if (ret)
171 rtw89_err(rtwdev, "failed to leave idle state\n");
172
173 rtw89_set_channel(rtwdev);
174
175 rtw89_for_each_rtwvif(rtwdev, rtwvif)
176 rtw89_mac_vif_init(rtwdev, rtwvif);
177
178 clear_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
179 }
180
rtw89_set_coex_ctrl_lps(struct rtw89_dev * rtwdev,bool btc_ctrl)181 void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl)
182 {
183 if (btc_ctrl)
184 rtw89_leave_lps(rtwdev);
185 }
186
rtw89_tsf32_toggle(struct rtw89_dev * rtwdev,struct rtw89_vif * rtwvif,enum rtw89_p2pps_action act)187 static void rtw89_tsf32_toggle(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
188 enum rtw89_p2pps_action act)
189 {
190 if (act == RTW89_P2P_ACT_UPDATE || act == RTW89_P2P_ACT_REMOVE)
191 return;
192
193 if (act == RTW89_P2P_ACT_INIT)
194 rtw89_fw_h2c_tsf32_toggle(rtwdev, rtwvif, true);
195 else if (act == RTW89_P2P_ACT_TERMINATE)
196 rtw89_fw_h2c_tsf32_toggle(rtwdev, rtwvif, false);
197 }
198
rtw89_p2p_disable_all_noa(struct rtw89_dev * rtwdev,struct ieee80211_vif * vif)199 static void rtw89_p2p_disable_all_noa(struct rtw89_dev *rtwdev,
200 struct ieee80211_vif *vif)
201 {
202 struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
203 enum rtw89_p2pps_action act;
204 u8 noa_id;
205
206 if (rtwvif->last_noa_nr == 0)
207 return;
208
209 for (noa_id = 0; noa_id < rtwvif->last_noa_nr; noa_id++) {
210 if (noa_id == rtwvif->last_noa_nr - 1)
211 act = RTW89_P2P_ACT_TERMINATE;
212 else
213 act = RTW89_P2P_ACT_REMOVE;
214 rtw89_tsf32_toggle(rtwdev, rtwvif, act);
215 rtw89_fw_h2c_p2p_act(rtwdev, vif, NULL, act, noa_id);
216 }
217 }
218
rtw89_p2p_update_noa(struct rtw89_dev * rtwdev,struct ieee80211_vif * vif)219 static void rtw89_p2p_update_noa(struct rtw89_dev *rtwdev,
220 struct ieee80211_vif *vif)
221 {
222 struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
223 struct ieee80211_p2p_noa_desc *desc;
224 enum rtw89_p2pps_action act;
225 u8 noa_id;
226
227 for (noa_id = 0; noa_id < RTW89_P2P_MAX_NOA_NUM; noa_id++) {
228 desc = &vif->bss_conf.p2p_noa_attr.desc[noa_id];
229 if (!desc->count || !desc->duration)
230 break;
231
232 if (noa_id == 0)
233 act = RTW89_P2P_ACT_INIT;
234 else
235 act = RTW89_P2P_ACT_UPDATE;
236 rtw89_tsf32_toggle(rtwdev, rtwvif, act);
237 rtw89_fw_h2c_p2p_act(rtwdev, vif, desc, act, noa_id);
238 }
239 rtwvif->last_noa_nr = noa_id;
240 }
241
rtw89_process_p2p_ps(struct rtw89_dev * rtwdev,struct ieee80211_vif * vif)242 void rtw89_process_p2p_ps(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif)
243 {
244 rtw89_p2p_disable_all_noa(rtwdev, vif);
245 rtw89_p2p_update_noa(rtwdev, vif);
246 }
247