1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* Copyright(c) 2018-2019 Realtek Corporation |
3 | */ |
4 | |
5 | #include "main.h" |
6 | #include "reg.h" |
7 | #include "fw.h" |
8 | #include "ps.h" |
9 | #include "mac.h" |
10 | #include "coex.h" |
11 | #include "debug.h" |
12 | |
13 | static int rtw_ips_pwr_up(struct rtw_dev *rtwdev) |
14 | { |
15 | int ret; |
16 | |
17 | ret = rtw_core_start(rtwdev); |
18 | if (ret) |
19 | rtw_err(rtwdev, "leave idle state failed\n" ); |
20 | |
21 | rtw_coex_ips_notify(rtwdev, type: COEX_IPS_LEAVE); |
22 | rtw_set_channel(rtwdev); |
23 | |
24 | return ret; |
25 | } |
26 | |
27 | int rtw_enter_ips(struct rtw_dev *rtwdev) |
28 | { |
29 | if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags)) |
30 | return 0; |
31 | |
32 | rtw_coex_ips_notify(rtwdev, type: COEX_IPS_ENTER); |
33 | |
34 | rtw_core_stop(rtwdev); |
35 | rtw_hci_link_ps(rtwdev, enter: true); |
36 | |
37 | return 0; |
38 | } |
39 | |
40 | static void rtw_restore_port_cfg_iter(void *data, struct ieee80211_vif *vif) |
41 | { |
42 | struct rtw_dev *rtwdev = data; |
43 | struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; |
44 | u32 config = ~0; |
45 | |
46 | rtw_vif_port_config(rtwdev, rtwvif, config); |
47 | } |
48 | |
49 | int rtw_leave_ips(struct rtw_dev *rtwdev) |
50 | { |
51 | int ret; |
52 | |
53 | if (test_bit(RTW_FLAG_POWERON, rtwdev->flags)) |
54 | return 0; |
55 | |
56 | rtw_hci_link_ps(rtwdev, enter: false); |
57 | |
58 | ret = rtw_ips_pwr_up(rtwdev); |
59 | if (ret) { |
60 | rtw_err(rtwdev, "failed to leave ips state\n" ); |
61 | return ret; |
62 | } |
63 | |
64 | rtw_iterate_vifs(rtwdev, iterator: rtw_restore_port_cfg_iter, data: rtwdev); |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter) |
70 | { |
71 | u8 request, confirm, polling; |
72 | int ret; |
73 | |
74 | request = rtw_read8(rtwdev, addr: rtwdev->hci.rpwm_addr); |
75 | confirm = rtw_read8(rtwdev, addr: rtwdev->hci.cpwm_addr); |
76 | |
77 | /* toggle to request power mode, others remain 0 */ |
78 | request ^= request | BIT_RPWM_TOGGLE; |
79 | if (enter) { |
80 | request |= POWER_MODE_LCLK; |
81 | if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG) |
82 | request |= POWER_MODE_PG; |
83 | } |
84 | /* Each request require an ack from firmware */ |
85 | request |= POWER_MODE_ACK; |
86 | |
87 | if (rtw_fw_feature_check(fw: &rtwdev->fw, feature: FW_FEATURE_TX_WAKE)) |
88 | request |= POWER_TX_WAKE; |
89 | |
90 | rtw_write8(rtwdev, addr: rtwdev->hci.rpwm_addr, val: request); |
91 | |
92 | /* Check firmware get the power requset and ack via cpwm register */ |
93 | ret = read_poll_timeout_atomic(rtw_read8, polling, |
94 | (polling ^ confirm) & BIT_RPWM_TOGGLE, |
95 | 100, 15000, true, rtwdev, |
96 | rtwdev->hci.cpwm_addr); |
97 | if (ret) { |
98 | /* Hit here means that driver failed to get an ack from firmware. |
99 | * The reason could be that hardware is locked at Deep sleep, |
100 | * so most of the hardware circuits are not working, even |
101 | * register read/write; or firmware is locked in some state and |
102 | * cannot get the request. It should be treated as fatal error |
103 | * and requires an entire analysis about the firmware/hardware. |
104 | */ |
105 | WARN(1, "firmware failed to ack driver for %s Deep Power mode\n" , |
106 | enter ? "entering" : "leaving" ); |
107 | rtw_fw_dump_dbg_info(rtwdev); |
108 | } |
109 | } |
110 | EXPORT_SYMBOL(rtw_power_mode_change); |
111 | |
112 | static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev) |
113 | { |
114 | rtw_hci_deep_ps(rtwdev, enter: false); |
115 | } |
116 | |
117 | static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev) |
118 | { |
119 | int i; |
120 | |
121 | /* Driver needs to wait for firmware to leave LPS state |
122 | * successfully. Firmware will send null packet to inform AP, |
123 | * and see if AP sends an ACK back, then firmware will restore |
124 | * the REG_TCR register. |
125 | * |
126 | * If driver does not wait for firmware, null packet with |
127 | * PS bit could be sent due to incorrect REG_TCR setting. |
128 | * |
129 | * In our test, 100ms should be enough for firmware to finish |
130 | * the flow. If REG_TCR Register is still incorrect after 100ms, |
131 | * just modify it directly, and throw a warn message. |
132 | */ |
133 | for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) { |
134 | if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0) |
135 | return 0; |
136 | msleep(msecs: 20); |
137 | } |
138 | |
139 | return -EBUSY; |
140 | } |
141 | |
142 | static int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev) |
143 | { |
144 | if (wait_for_completion_timeout(x: &rtwdev->lps_leave_check, |
145 | LEAVE_LPS_TIMEOUT)) |
146 | return 0; |
147 | return -EBUSY; |
148 | } |
149 | |
150 | static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev) |
151 | { |
152 | bool ret = false; |
153 | struct rtw_fw_state *fw; |
154 | |
155 | if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) |
156 | fw = &rtwdev->wow_fw; |
157 | else |
158 | fw = &rtwdev->fw; |
159 | |
160 | if (rtw_fw_feature_check(fw, feature: FW_FEATURE_LPS_C2H)) |
161 | ret = __rtw_fw_leave_lps_check_c2h(rtwdev); |
162 | else |
163 | ret = __rtw_fw_leave_lps_check_reg(rtwdev); |
164 | |
165 | if (ret) { |
166 | rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN); |
167 | rtw_warn(rtwdev, "firmware failed to leave lps state\n" ); |
168 | rtw_fw_dump_dbg_info(rtwdev); |
169 | } |
170 | } |
171 | |
172 | static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev) |
173 | { |
174 | struct rtw_fw_state *fw; |
175 | |
176 | if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) |
177 | fw = &rtwdev->wow_fw; |
178 | else |
179 | fw = &rtwdev->fw; |
180 | |
181 | if (rtw_fw_feature_check(fw, feature: FW_FEATURE_LPS_C2H)) |
182 | reinit_completion(x: &rtwdev->lps_leave_check); |
183 | } |
184 | |
185 | static void rtw_leave_lps_core(struct rtw_dev *rtwdev) |
186 | { |
187 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; |
188 | |
189 | conf->state = RTW_ALL_ON; |
190 | conf->awake_interval = 1; |
191 | conf->rlbm = 0; |
192 | conf->smart_ps = 0; |
193 | |
194 | rtw_hci_link_ps(rtwdev, enter: false); |
195 | rtw_fw_leave_lps_check_prepare(rtwdev); |
196 | rtw_fw_set_pwr_mode(rtwdev); |
197 | rtw_fw_leave_lps_check(rtwdev); |
198 | |
199 | clear_bit(nr: RTW_FLAG_LEISURE_PS, addr: rtwdev->flags); |
200 | |
201 | rtw_coex_lps_notify(rtwdev, type: COEX_LPS_DISABLE); |
202 | } |
203 | |
204 | enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev) |
205 | { |
206 | if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags)) |
207 | return rtwdev->lps_conf.wow_deep_mode; |
208 | else |
209 | return rtwdev->lps_conf.deep_mode; |
210 | } |
211 | |
212 | static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev) |
213 | { |
214 | if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE) |
215 | return; |
216 | |
217 | if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) { |
218 | rtw_dbg(rtwdev, mask: RTW_DBG_PS, |
219 | fmt: "Should enter LPS before entering deep PS\n" ); |
220 | return; |
221 | } |
222 | |
223 | if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG) |
224 | rtw_fw_set_pg_info(rtwdev); |
225 | |
226 | rtw_hci_deep_ps(rtwdev, enter: true); |
227 | } |
228 | |
229 | static void rtw_enter_lps_core(struct rtw_dev *rtwdev) |
230 | { |
231 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; |
232 | |
233 | conf->state = RTW_RF_OFF; |
234 | conf->awake_interval = 1; |
235 | conf->rlbm = 1; |
236 | conf->smart_ps = 2; |
237 | |
238 | rtw_coex_lps_notify(rtwdev, type: COEX_LPS_ENABLE); |
239 | |
240 | rtw_fw_set_pwr_mode(rtwdev); |
241 | rtw_hci_link_ps(rtwdev, enter: true); |
242 | |
243 | set_bit(nr: RTW_FLAG_LEISURE_PS, addr: rtwdev->flags); |
244 | } |
245 | |
246 | static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id) |
247 | { |
248 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; |
249 | |
250 | if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) |
251 | return; |
252 | |
253 | conf->mode = RTW_MODE_LPS; |
254 | conf->port_id = port_id; |
255 | |
256 | rtw_enter_lps_core(rtwdev); |
257 | } |
258 | |
259 | static void __rtw_leave_lps(struct rtw_dev *rtwdev) |
260 | { |
261 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; |
262 | |
263 | if (test_and_clear_bit(nr: RTW_FLAG_LEISURE_PS_DEEP, addr: rtwdev->flags)) { |
264 | rtw_dbg(rtwdev, mask: RTW_DBG_PS, |
265 | fmt: "Should leave deep PS before leaving LPS\n" ); |
266 | __rtw_leave_lps_deep(rtwdev); |
267 | } |
268 | |
269 | if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) |
270 | return; |
271 | |
272 | conf->mode = RTW_MODE_ACTIVE; |
273 | |
274 | rtw_leave_lps_core(rtwdev); |
275 | } |
276 | |
277 | void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id) |
278 | { |
279 | lockdep_assert_held(&rtwdev->mutex); |
280 | |
281 | if (rtwdev->coex.stat.wl_force_lps_ctrl) |
282 | return; |
283 | |
284 | __rtw_enter_lps(rtwdev, port_id); |
285 | __rtw_enter_lps_deep(rtwdev); |
286 | } |
287 | |
288 | void rtw_leave_lps(struct rtw_dev *rtwdev) |
289 | { |
290 | lockdep_assert_held(&rtwdev->mutex); |
291 | |
292 | __rtw_leave_lps_deep(rtwdev); |
293 | __rtw_leave_lps(rtwdev); |
294 | } |
295 | |
296 | void rtw_leave_lps_deep(struct rtw_dev *rtwdev) |
297 | { |
298 | lockdep_assert_held(&rtwdev->mutex); |
299 | |
300 | __rtw_leave_lps_deep(rtwdev); |
301 | } |
302 | |
303 | struct rtw_vif_recalc_lps_iter_data { |
304 | struct rtw_dev *rtwdev; |
305 | struct ieee80211_vif *found_vif; |
306 | int count; |
307 | }; |
308 | |
309 | static void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data, |
310 | struct ieee80211_vif *vif) |
311 | { |
312 | if (data->count < 0) |
313 | return; |
314 | |
315 | if (vif->type != NL80211_IFTYPE_STATION) { |
316 | data->count = -1; |
317 | return; |
318 | } |
319 | |
320 | data->count++; |
321 | data->found_vif = vif; |
322 | } |
323 | |
324 | static void rtw_vif_recalc_lps_iter(void *data, struct ieee80211_vif *vif) |
325 | { |
326 | __rtw_vif_recalc_lps(data, vif); |
327 | } |
328 | |
329 | void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif) |
330 | { |
331 | struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev }; |
332 | |
333 | if (new_vif) |
334 | __rtw_vif_recalc_lps(data: &data, vif: new_vif); |
335 | rtw_iterate_vifs(rtwdev, iterator: rtw_vif_recalc_lps_iter, data: &data); |
336 | |
337 | if (data.count == 1 && data.found_vif->cfg.ps) { |
338 | rtwdev->ps_enabled = true; |
339 | } else { |
340 | rtwdev->ps_enabled = false; |
341 | rtw_leave_lps(rtwdev); |
342 | } |
343 | } |
344 | |