1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* Copyright(c) 2019-2020 Realtek Corporation |
3 | */ |
4 | |
5 | #include "chan.h" |
6 | #include "coex.h" |
7 | #include "core.h" |
8 | #include "debug.h" |
9 | #include "fw.h" |
10 | #include "mac.h" |
11 | #include "ps.h" |
12 | #include "reg.h" |
13 | #include "util.h" |
14 | |
15 | static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid) |
16 | { |
17 | const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; |
18 | u32 pwr_en_bit = 0xE; |
19 | u32 chk_msk = pwr_en_bit << (4 * macid); |
20 | u32 polling; |
21 | int ret; |
22 | |
23 | ret = read_poll_timeout_atomic(rtw89_read32_mask, polling, !polling, |
24 | 1000, 50000, false, rtwdev, |
25 | mac->ps_status, chk_msk); |
26 | if (ret) { |
27 | rtw89_info(rtwdev, "rtw89: failed to leave lps state\n" ); |
28 | return -EBUSY; |
29 | } |
30 | |
31 | return 0; |
32 | } |
33 | |
34 | static void rtw89_ps_power_mode_change_with_hci(struct rtw89_dev *rtwdev, |
35 | bool enter) |
36 | { |
37 | ieee80211_stop_queues(hw: rtwdev->hw); |
38 | rtwdev->hci.paused = true; |
39 | flush_work(work: &rtwdev->txq_work); |
40 | ieee80211_wake_queues(hw: rtwdev->hw); |
41 | |
42 | rtw89_hci_pause(rtwdev, pause: true); |
43 | rtw89_mac_power_mode_change(rtwdev, enter); |
44 | rtw89_hci_switch_mode(rtwdev, low_power: enter); |
45 | rtw89_hci_pause(rtwdev, pause: false); |
46 | |
47 | rtwdev->hci.paused = false; |
48 | |
49 | if (!enter) { |
50 | local_bh_disable(); |
51 | napi_schedule(n: &rtwdev->napi); |
52 | local_bh_enable(); |
53 | } |
54 | } |
55 | |
56 | static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter) |
57 | { |
58 | if (rtwdev->chip->low_power_hci_modes & BIT(rtwdev->ps_mode)) |
59 | rtw89_ps_power_mode_change_with_hci(rtwdev, enter); |
60 | else |
61 | rtw89_mac_power_mode_change(rtwdev, enter); |
62 | } |
63 | |
64 | void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) |
65 | { |
66 | if (rtwvif->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) |
67 | return; |
68 | |
69 | if (!rtwdev->ps_mode) |
70 | return; |
71 | |
72 | if (test_and_set_bit(nr: RTW89_FLAG_LOW_POWER_MODE, addr: rtwdev->flags)) |
73 | return; |
74 | |
75 | rtw89_ps_power_mode_change(rtwdev, enter: true); |
76 | } |
77 | |
78 | void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev) |
79 | { |
80 | if (!rtwdev->ps_mode) |
81 | return; |
82 | |
83 | if (test_and_clear_bit(nr: RTW89_FLAG_LOW_POWER_MODE, addr: rtwdev->flags)) |
84 | rtw89_ps_power_mode_change(rtwdev, enter: false); |
85 | } |
86 | |
87 | static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) |
88 | { |
89 | struct rtw89_lps_parm lps_param = { |
90 | .macid = rtwvif->mac_id, |
91 | .psmode = RTW89_MAC_AX_PS_MODE_LEGACY, |
92 | .lastrpwm = RTW89_LAST_RPWM_PS, |
93 | }; |
94 | |
95 | rtw89_btc_ntfy_radio_state(rtwdev, rf_state: BTC_RFCTRL_FW_CTRL); |
96 | rtw89_fw_h2c_lps_parm(rtwdev, lps_param: &lps_param); |
97 | rtw89_fw_h2c_lps_ch_info(rtwdev, rtwvif); |
98 | } |
99 | |
100 | static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id) |
101 | { |
102 | struct rtw89_lps_parm lps_param = { |
103 | .macid = mac_id, |
104 | .psmode = RTW89_MAC_AX_PS_MODE_ACTIVE, |
105 | .lastrpwm = RTW89_LAST_RPWM_ACTIVE, |
106 | }; |
107 | |
108 | rtw89_fw_h2c_lps_parm(rtwdev, lps_param: &lps_param); |
109 | rtw89_fw_leave_lps_check(rtwdev, macid: 0); |
110 | rtw89_btc_ntfy_radio_state(rtwdev, rf_state: BTC_RFCTRL_WL_ON); |
111 | } |
112 | |
113 | void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev) |
114 | { |
115 | lockdep_assert_held(&rtwdev->mutex); |
116 | |
117 | __rtw89_leave_ps_mode(rtwdev); |
118 | } |
119 | |
120 | void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, |
121 | bool ps_mode) |
122 | { |
123 | lockdep_assert_held(&rtwdev->mutex); |
124 | |
125 | if (test_and_set_bit(nr: RTW89_FLAG_LEISURE_PS, addr: rtwdev->flags)) |
126 | return; |
127 | |
128 | __rtw89_enter_lps(rtwdev, rtwvif); |
129 | if (ps_mode) |
130 | __rtw89_enter_ps_mode(rtwdev, rtwvif); |
131 | } |
132 | |
133 | static void rtw89_leave_lps_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) |
134 | { |
135 | if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION && |
136 | rtwvif->wifi_role != RTW89_WIFI_ROLE_P2P_CLIENT) |
137 | return; |
138 | |
139 | __rtw89_leave_lps(rtwdev, mac_id: rtwvif->mac_id); |
140 | } |
141 | |
142 | void rtw89_leave_lps(struct rtw89_dev *rtwdev) |
143 | { |
144 | struct rtw89_vif *rtwvif; |
145 | |
146 | lockdep_assert_held(&rtwdev->mutex); |
147 | |
148 | if (!test_and_clear_bit(nr: RTW89_FLAG_LEISURE_PS, addr: rtwdev->flags)) |
149 | return; |
150 | |
151 | __rtw89_leave_ps_mode(rtwdev); |
152 | |
153 | rtw89_for_each_rtwvif(rtwdev, rtwvif) |
154 | rtw89_leave_lps_vif(rtwdev, rtwvif); |
155 | } |
156 | |
157 | void rtw89_enter_ips(struct rtw89_dev *rtwdev) |
158 | { |
159 | struct rtw89_vif *rtwvif; |
160 | |
161 | set_bit(nr: RTW89_FLAG_INACTIVE_PS, addr: rtwdev->flags); |
162 | |
163 | if (!test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) |
164 | return; |
165 | |
166 | rtw89_for_each_rtwvif(rtwdev, rtwvif) |
167 | rtw89_mac_vif_deinit(rtwdev, rtwvif); |
168 | |
169 | rtw89_core_stop(rtwdev); |
170 | } |
171 | |
172 | void rtw89_leave_ips(struct rtw89_dev *rtwdev) |
173 | { |
174 | struct rtw89_vif *rtwvif; |
175 | int ret; |
176 | |
177 | if (test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) |
178 | return; |
179 | |
180 | ret = rtw89_core_start(rtwdev); |
181 | if (ret) |
182 | rtw89_err(rtwdev, "failed to leave idle state\n" ); |
183 | |
184 | rtw89_set_channel(rtwdev); |
185 | |
186 | rtw89_for_each_rtwvif(rtwdev, rtwvif) |
187 | rtw89_mac_vif_init(rtwdev, rtwvif); |
188 | |
189 | clear_bit(nr: RTW89_FLAG_INACTIVE_PS, addr: rtwdev->flags); |
190 | } |
191 | |
192 | void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl) |
193 | { |
194 | if (btc_ctrl) |
195 | rtw89_leave_lps(rtwdev); |
196 | } |
197 | |
198 | static void rtw89_tsf32_toggle(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, |
199 | enum rtw89_p2pps_action act) |
200 | { |
201 | if (act == RTW89_P2P_ACT_UPDATE || act == RTW89_P2P_ACT_REMOVE) |
202 | return; |
203 | |
204 | if (act == RTW89_P2P_ACT_INIT) |
205 | rtw89_fw_h2c_tsf32_toggle(rtwdev, rtwvif, en: true); |
206 | else if (act == RTW89_P2P_ACT_TERMINATE) |
207 | rtw89_fw_h2c_tsf32_toggle(rtwdev, rtwvif, en: false); |
208 | } |
209 | |
210 | static void rtw89_p2p_disable_all_noa(struct rtw89_dev *rtwdev, |
211 | struct ieee80211_vif *vif) |
212 | { |
213 | struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; |
214 | enum rtw89_p2pps_action act; |
215 | u8 noa_id; |
216 | |
217 | if (rtwvif->last_noa_nr == 0) |
218 | return; |
219 | |
220 | for (noa_id = 0; noa_id < rtwvif->last_noa_nr; noa_id++) { |
221 | if (noa_id == rtwvif->last_noa_nr - 1) |
222 | act = RTW89_P2P_ACT_TERMINATE; |
223 | else |
224 | act = RTW89_P2P_ACT_REMOVE; |
225 | rtw89_tsf32_toggle(rtwdev, rtwvif, act); |
226 | rtw89_fw_h2c_p2p_act(rtwdev, vif, NULL, act, noa_id); |
227 | } |
228 | } |
229 | |
230 | static void rtw89_p2p_update_noa(struct rtw89_dev *rtwdev, |
231 | struct ieee80211_vif *vif) |
232 | { |
233 | struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; |
234 | struct ieee80211_p2p_noa_desc *desc; |
235 | enum rtw89_p2pps_action act; |
236 | u8 noa_id; |
237 | |
238 | for (noa_id = 0; noa_id < RTW89_P2P_MAX_NOA_NUM; noa_id++) { |
239 | desc = &vif->bss_conf.p2p_noa_attr.desc[noa_id]; |
240 | if (!desc->count || !desc->duration) |
241 | break; |
242 | |
243 | if (noa_id == 0) |
244 | act = RTW89_P2P_ACT_INIT; |
245 | else |
246 | act = RTW89_P2P_ACT_UPDATE; |
247 | rtw89_tsf32_toggle(rtwdev, rtwvif, act); |
248 | rtw89_fw_h2c_p2p_act(rtwdev, vif, desc, act, noa_id); |
249 | } |
250 | rtwvif->last_noa_nr = noa_id; |
251 | } |
252 | |
253 | void rtw89_process_p2p_ps(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif) |
254 | { |
255 | rtw89_p2p_disable_all_noa(rtwdev, vif); |
256 | rtw89_p2p_update_noa(rtwdev, vif); |
257 | } |
258 | |
259 | void rtw89_recalc_lps(struct rtw89_dev *rtwdev) |
260 | { |
261 | struct ieee80211_vif *vif, *found_vif = NULL; |
262 | struct rtw89_vif *rtwvif; |
263 | enum rtw89_entity_mode mode; |
264 | int count = 0; |
265 | |
266 | mode = rtw89_get_entity_mode(rtwdev); |
267 | if (mode == RTW89_ENTITY_MODE_MCC) |
268 | goto disable_lps; |
269 | |
270 | rtw89_for_each_rtwvif(rtwdev, rtwvif) { |
271 | vif = rtwvif_to_vif(rtwvif); |
272 | |
273 | if (vif->type != NL80211_IFTYPE_STATION) { |
274 | count = 0; |
275 | break; |
276 | } |
277 | |
278 | count++; |
279 | found_vif = vif; |
280 | } |
281 | |
282 | if (count == 1 && found_vif->cfg.ps) { |
283 | rtwdev->lps_enabled = true; |
284 | return; |
285 | } |
286 | |
287 | disable_lps: |
288 | rtw89_leave_lps(rtwdev); |
289 | rtwdev->lps_enabled = false; |
290 | } |
291 | |
292 | void rtw89_p2p_noa_renew(struct rtw89_vif *rtwvif) |
293 | { |
294 | struct rtw89_p2p_noa_setter *setter = &rtwvif->p2p_noa; |
295 | struct rtw89_p2p_noa_ie *ie = &setter->ie; |
296 | struct rtw89_p2p_ie_head *p2p_head = &ie->p2p_head; |
297 | struct rtw89_noa_attr_head *noa_head = &ie->noa_head; |
298 | |
299 | if (setter->noa_count) { |
300 | setter->noa_index++; |
301 | setter->noa_count = 0; |
302 | } |
303 | |
304 | memset(ie, 0, sizeof(*ie)); |
305 | |
306 | p2p_head->eid = WLAN_EID_VENDOR_SPECIFIC; |
307 | p2p_head->ie_len = 4 + sizeof(*noa_head); |
308 | p2p_head->oui[0] = (WLAN_OUI_WFA >> 16) & 0xff; |
309 | p2p_head->oui[1] = (WLAN_OUI_WFA >> 8) & 0xff; |
310 | p2p_head->oui[2] = (WLAN_OUI_WFA >> 0) & 0xff; |
311 | p2p_head->oui_type = WLAN_OUI_TYPE_WFA_P2P; |
312 | |
313 | noa_head->attr_type = IEEE80211_P2P_ATTR_ABSENCE_NOTICE; |
314 | noa_head->attr_len = cpu_to_le16(2); |
315 | noa_head->index = setter->noa_index; |
316 | noa_head->oppps_ctwindow = 0; |
317 | } |
318 | |
319 | void rtw89_p2p_noa_append(struct rtw89_vif *rtwvif, |
320 | const struct ieee80211_p2p_noa_desc *desc) |
321 | { |
322 | struct rtw89_p2p_noa_setter *setter = &rtwvif->p2p_noa; |
323 | struct rtw89_p2p_noa_ie *ie = &setter->ie; |
324 | struct rtw89_p2p_ie_head *p2p_head = &ie->p2p_head; |
325 | struct rtw89_noa_attr_head *noa_head = &ie->noa_head; |
326 | |
327 | if (!desc->count || !desc->duration) |
328 | return; |
329 | |
330 | if (setter->noa_count >= RTW89_P2P_MAX_NOA_NUM) |
331 | return; |
332 | |
333 | p2p_head->ie_len += sizeof(*desc); |
334 | le16_add_cpu(var: &noa_head->attr_len, val: sizeof(*desc)); |
335 | |
336 | ie->noa_desc[setter->noa_count++] = *desc; |
337 | } |
338 | |
339 | u8 rtw89_p2p_noa_fetch(struct rtw89_vif *rtwvif, void **data) |
340 | { |
341 | struct rtw89_p2p_noa_setter *setter = &rtwvif->p2p_noa; |
342 | struct rtw89_p2p_noa_ie *ie = &setter->ie; |
343 | void *tail; |
344 | |
345 | if (!setter->noa_count) |
346 | return 0; |
347 | |
348 | *data = ie; |
349 | tail = ie->noa_desc + setter->noa_count; |
350 | return tail - *data; |
351 | } |
352 | |