1 /******************************************************************************
2 *
3 * Copyright(c) 2019 Realtek Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 *****************************************************************************/
15 #define _HAL_PS_C_
16 #include "hal_headers.h"
17 #ifdef CONFIG_POWER_SAVE
18 #define case_pwr_state(src) \
19 case PS_PWR_STATE_##src: return #src
hal_ps_pwr_state_to_str(u8 pwr_state)20 const char *hal_ps_pwr_state_to_str(u8 pwr_state)
21 {
22 switch (pwr_state) {
23 case_pwr_state(ACTIVE);
24 case_pwr_state(BAND0_RFON);
25 case_pwr_state(BAND1_RFON);
26 case_pwr_state(BAND0_RFOFF);
27 case_pwr_state(BAND1_RFOFF);
28 case_pwr_state(CLK_GATED);
29 case_pwr_state(PWR_GATED);
30 case_pwr_state(MAX);
31 default:
32 return "Undefined";
33 }
34 }
35
36 #define LPS_PROTOCAL_LEAVE_TOLERANCE 50 /* ms */
37 #define PWR_LVL_CHANGE_TOLERANCE 50 /* ms */
38 #define MAX_CHK_PWR_STATE_CHANGE_CNT 5
39
40 static enum rtw_hal_status
_hal_ps_lps_chk_leave(struct hal_info_t * hal_info,u16 macid)41 _hal_ps_lps_chk_leave(struct hal_info_t *hal_info, u16 macid)
42 {
43 enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS;
44 u32 mac_sts = 0;
45 u32 priv_mac_sts = 0;
46 u32 start_time = _os_get_cur_time_ms();
47 u32 pass_time = 0;
48
49 do {
50 status = rtw_hal_mac_lps_chk_leave(hal_info, macid, &mac_sts);
51 if (status == RTW_HAL_STATUS_SUCCESS) {
52 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
53 "[HALPS], %s(): pass time = %d ms.\n",
54 __func__, pass_time);
55 break;
56 }
57
58 if (mac_sts != priv_mac_sts) {
59 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
60 "[HALPS], %s(): mac status %u.\n",
61 __func__, mac_sts);
62 priv_mac_sts = mac_sts;
63 }
64
65 pass_time = phl_get_passing_time_ms(start_time);
66 if (pass_time > LPS_PROTOCAL_LEAVE_TOLERANCE)
67 break;
68
69 _os_sleep_us(hal_to_drvpriv(hal_info), 50);
70
71 } while (1);
72
73 #ifdef CONFIG_PHL_PS_FW_DBG
74 rtw_hal_fw_dbg_dump(hal_info);
75 #endif
76 if (status != RTW_HAL_STATUS_SUCCESS)
77 PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): polling timeout!\n", __func__);
78
79 return status;
80 }
81
82 static enum rtw_hal_status
_hal_ps_lps_cfg(struct hal_info_t * hal_info,struct rtw_hal_lps_info * lps_info)83 _hal_ps_lps_cfg(struct hal_info_t *hal_info,
84 struct rtw_hal_lps_info *lps_info)
85 {
86 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
87 "[HALPS], %s(): mode(%d), listen bcn mode(%d), awake interval(%d), smart_ps_mode(%d).\n",
88 __func__, lps_info->en, lps_info->listen_bcn_mode,
89 lps_info->awake_interval, lps_info->smart_ps_mode);
90
91 return rtw_hal_mac_lps_cfg(hal_info, lps_info);
92 }
93
94 static enum rtw_hal_status
_hal_ps_pwr_state_chk(struct hal_info_t * hal_info,u8 req_pwr_state)95 _hal_ps_pwr_state_chk(struct hal_info_t *hal_info, u8 req_pwr_state)
96 {
97 enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS;
98 u32 mac_sts = 0;
99 u32 priv_mac_sts = 0;
100 u32 start_time = _os_get_cur_time_ms();
101 u32 pass_time = 0;
102
103 do {
104 status = rtw_hal_mac_chk_pwr_state(hal_info, req_pwr_state, &mac_sts);
105 if (status == RTW_HAL_STATUS_SUCCESS) {
106 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
107 "[HALPS], %s(): pass time = %d ms.\n",
108 __func__, pass_time);
109 break;
110 }
111
112 if (mac_sts != priv_mac_sts) {
113 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
114 "[HALPS], %s(): mac status %u.\n",
115 __func__, mac_sts);
116 priv_mac_sts = mac_sts;
117 }
118
119 pass_time = phl_get_passing_time_ms(start_time);
120 if (pass_time > PWR_LVL_CHANGE_TOLERANCE)
121 break;
122
123 _os_sleep_us(hal_to_drvpriv(hal_info), 50);
124
125 } while (1);
126
127 #ifdef CONFIG_PHL_PS_FW_DBG
128 rtw_hal_fw_dbg_dump(hal_info);
129 #endif
130
131 if (status != RTW_HAL_STATUS_SUCCESS)
132 PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): polling timeout!\n", __func__);
133
134 return status;
135 }
136
137 static enum rtw_hal_status
_hal_ps_cfg_pwr_state(struct hal_info_t * hal_info,u8 req_pwr_state)138 _hal_ps_cfg_pwr_state(struct hal_info_t *hal_info, u8 req_pwr_state)
139 {
140 #if (HAL_LPS_SKIP_HW_CFG == 0)
141 enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS;
142 u8 cnt = 0;
143
144 for (cnt = 0; cnt < MAX_CHK_PWR_STATE_CHANGE_CNT; cnt++) {
145
146 /* rpwm */
147 status = rtw_hal_mac_req_pwr_state(hal_info, req_pwr_state);
148 if (status != RTW_HAL_STATUS_SUCCESS) {
149 PHL_TRACE(COMP_PHL_PS, _PHL_ERR_,
150 "[HALPS], %s(): set pwr state %s fail!\n",
151 __func__, hal_ps_pwr_state_to_str(req_pwr_state));
152 return RTW_HAL_STATUS_FAILURE;
153 }
154
155 /* cpwm */
156 status = _hal_ps_pwr_state_chk(hal_info, req_pwr_state);
157 if (status == RTW_HAL_STATUS_SUCCESS)
158 break;
159 }
160
161 if (cnt < MAX_CHK_PWR_STATE_CHANGE_CNT) {
162 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_,
163 "[HALPS], %s(): chk pwr state %s success, cnt %d.\n",
164 __func__, hal_ps_pwr_state_to_str(req_pwr_state), cnt);
165 return RTW_HAL_STATUS_SUCCESS;
166 } else {
167 PHL_TRACE(COMP_PHL_PS, _PHL_ERR_,
168 "[HALPS], %s(): chk pwr state %s fail!\n",
169 __func__, hal_ps_pwr_state_to_str(req_pwr_state));
170 return RTW_HAL_STATUS_FAILURE;
171 }
172 #else
173 return RTW_HAL_STATUS_SUCCESS;
174 #endif
175 }
176
177 static enum rtw_hal_status
_hal_ps_cfg_pwr_on(struct rtw_phl_com_t * phl_com,struct hal_info_t * hal_info)178 _hal_ps_cfg_pwr_on(struct rtw_phl_com_t *phl_com,
179 struct hal_info_t *hal_info)
180 {
181 struct rtw_phl_evt_ops *ops = &phl_com->evt_ops;
182 enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS;
183
184 if (hal_info->hal_com->is_hal_init) {
185 #if (HAL_LPS_SKIP_HW_CFG == 0)
186 status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_ACTIVE);
187 #else
188 status = RTW_HAL_STATUS_SUCCESS;
189 #endif
190 } else {
191 #if (HAL_IPS_SKIP_HW_CFG == 0)
192 if (ops->set_rf_state(phlcom_to_drvpriv(phl_com), RTW_RF_ON) == true)
193 status = RTW_HAL_STATUS_SUCCESS;
194 else
195 status = RTW_HAL_STATUS_FAILURE;
196 #else
197 status = RTW_HAL_STATUS_SUCCESS;
198 #endif
199 }
200
201 return status;
202 }
203
204 static enum rtw_hal_status
_hal_ps_cfg_pwr_off(struct rtw_phl_com_t * phl_com,struct hal_info_t * hal_info)205 _hal_ps_cfg_pwr_off(struct rtw_phl_com_t *phl_com,
206 struct hal_info_t *hal_info)
207 {
208 struct rtw_phl_evt_ops *ops = &phl_com->evt_ops;
209
210 #if (HAL_IPS_SKIP_HW_CFG == 0)
211 if (ops->set_rf_state(phlcom_to_drvpriv(phl_com), RTW_RF_OFF) == true)
212 return RTW_HAL_STATUS_SUCCESS;
213 else
214 return RTW_HAL_STATUS_FAILURE;
215 #else
216 return RTW_HAL_STATUS_SUCCESS;
217 #endif
218 }
219
220 static enum rtw_hal_status
_hal_ps_pwr_lvl_cfg(struct rtw_phl_com_t * phl_com,struct hal_info_t * hal_info,u8 req_pwr_lvl)221 _hal_ps_pwr_lvl_cfg(struct rtw_phl_com_t *phl_com, struct hal_info_t *hal_info,
222 u8 req_pwr_lvl)
223 {
224 enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
225
226 switch (req_pwr_lvl) {
227 case PS_PWR_LVL_PWROFF:
228 status = _hal_ps_cfg_pwr_off(phl_com, hal_info);
229 break;
230 case PS_PWR_LVL_PWR_GATED:
231 status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_PWR_GATED);
232 break;
233 case PS_PWR_LVL_CLK_GATED:
234 status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_CLK_GATED);
235 break;
236 case PS_PWR_LVL_RF_OFF:
237 status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_BAND0_RFOFF);
238 break;
239 case PS_PWR_LVL_PWRON:
240 status = _hal_ps_cfg_pwr_on(phl_com, hal_info);
241 break;
242 default:
243 PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[HALPS], %s(): undefined pwr lvl!\n", __func__);
244 break;
245 }
246
247 return status;
248 }
249
_hal_ps_proc_hw_rf_state_done(void * priv,struct phl_msg * msg)250 void _hal_ps_proc_hw_rf_state_done(void* priv, struct phl_msg* msg)
251 {
252 struct rtw_phl_com_t *phl_com = (struct rtw_phl_com_t *)priv;
253
254 if (msg->inbuf && msg->inlen) {
255 _os_kmem_free(phlcom_to_drvpriv(phl_com), msg->inbuf, msg->inlen);
256 }
257 }
258
259 static void
_hal_ps_ntfy_hw_rf_state(struct rtw_phl_com_t * phl_com,enum rtw_rf_state rf_state)260 _hal_ps_ntfy_hw_rf_state(struct rtw_phl_com_t *phl_com,
261 enum rtw_rf_state rf_state)
262 {
263 struct phl_msg msg = {0};
264 struct phl_msg_attribute attr = {0};
265 void *d = phlcom_to_drvpriv(phl_com);
266 enum rtw_rf_state *rf_ntfy = NULL;
267
268 rf_ntfy = (enum rtw_rf_state *)_os_kmem_alloc(d, sizeof(*rf_ntfy));
269 if (rf_ntfy == NULL) {
270 PHL_ERR("[HALPS], %s(): alloc for ntfy fail.\n", __func__);
271 return;
272 }
273
274 *rf_ntfy = rf_state;
275 msg.inbuf = (u8 *)rf_ntfy;
276 msg.inlen = sizeof(*rf_ntfy);
277
278 SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_POWER_MGNT);
279 SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_HW_RF_CHG);
280 attr.completion.completion = _hal_ps_proc_hw_rf_state_done;
281 attr.completion.priv = phl_com;
282 if (rtw_phl_msg_hub_hal_send(phl_com, &attr, &msg) !=
283 RTW_PHL_STATUS_SUCCESS) {
284 PHL_ERR("[HALPS], %s(): send msg failed\n", __func__);
285 _os_kmem_free(d, rf_ntfy, sizeof(*rf_ntfy));
286 }
287 }
288
289 /**
290 * configured requested power level
291 * return success if configure power level ok
292 * @phl_com: see rtw_phl_com_t
293 * @hal: see hal_info_t
294 * @req_pwr_lvl: target power level to configured
295 */
296 enum rtw_hal_status
rtw_hal_ps_pwr_lvl_cfg(struct rtw_phl_com_t * phl_com,void * hal,u8 req_pwr_lvl)297 rtw_hal_ps_pwr_lvl_cfg(struct rtw_phl_com_t *phl_com, void *hal,
298 u8 req_pwr_lvl)
299 {
300 enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
301 struct hal_info_t *hal_info = (struct hal_info_t *)hal;
302
303 status = _hal_ps_pwr_lvl_cfg(phl_com, hal_info, req_pwr_lvl);
304 if (status != RTW_HAL_STATUS_SUCCESS) {
305 PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): pwr lvl cfg fail!\n", __func__);
306 }
307
308 return status;
309 }
310
311 /**
312 * configure the legacy power save (protocol)
313 * return configure lps fail or not
314 * @hal: see hal_info_t
315 * @lps: see rtw_hal_lps_info
316 */
317 enum rtw_hal_status
rtw_hal_ps_lps_cfg(void * hal,struct rtw_hal_lps_info * lps_info)318 rtw_hal_ps_lps_cfg(void *hal, struct rtw_hal_lps_info *lps_info)
319 {
320 enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
321 struct hal_info_t *hal_info = (struct hal_info_t *)hal;
322
323 status = _hal_ps_lps_cfg(hal_info, lps_info);
324
325 if (status == RTW_HAL_STATUS_SUCCESS) {
326 if (lps_info->en == false)
327 status = _hal_ps_lps_chk_leave(hal_info, lps_info->macid);
328 }
329
330 return status;
331 }
332
rtw_hal_ps_pwr_req(struct rtw_phl_com_t * phl_com,u8 src,bool pwr_req)333 enum rtw_hal_status rtw_hal_ps_pwr_req(struct rtw_phl_com_t *phl_com, u8 src, bool pwr_req)
334 {
335 enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
336
337 status = phl_ps_hal_pwr_req(phl_com, src, pwr_req);
338 if (status != RTW_PHL_STATUS_SUCCESS) {
339 PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): fail (pwr_req %d).\n", __func__, pwr_req);
340 return RTW_HAL_STATUS_FAILURE;
341 }
342
343 return RTW_HAL_STATUS_SUCCESS;
344 }
345
rtw_hal_ps_ips_cfg(void * hal,struct rtw_hal_ips_info * ips_info)346 enum rtw_hal_status rtw_hal_ps_ips_cfg(void *hal,
347 struct rtw_hal_ips_info *ips_info)
348 {
349 enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
350 struct hal_info_t *hal_info = (struct hal_info_t *)hal;
351
352 hstatus = rtw_hal_mac_ips_cfg(hal, ips_info->macid, ips_info->en);
353
354 if (hstatus == RTW_HAL_STATUS_SUCCESS && !ips_info->en)
355 hstatus = rtw_hal_mac_ips_chk_leave(hal_info, ips_info->macid);
356
357 return hstatus;
358 }
359
360 void
rtw_hal_ps_chk_hw_rf_state(struct rtw_phl_com_t * phl_com,void * hal)361 rtw_hal_ps_chk_hw_rf_state(struct rtw_phl_com_t *phl_com, void *hal)
362 {
363 struct hal_info_t *hal_info = (struct hal_info_t *)hal;
364 enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
365 enum rtw_rf_state rf_state = RTW_RF_ON;
366 u8 val = 0;
367
368 hstatus = rtw_hal_mac_get_wl_dis_val(hal_info, &val);
369 if (hstatus != RTW_HAL_STATUS_SUCCESS) {
370 PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): get wl dis val fail, status: %d\n",
371 __func__, hstatus);
372 return;
373 }
374
375 /* get new rf state */
376 if (val == 1) {
377 rf_state = RTW_RF_ON;
378 } else if (val == 0) {
379 rf_state = RTW_RF_OFF;
380 } else {
381 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): wl_dis is invalid value: %d\n",
382 __func__, val);
383 return;
384 }
385
386 PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): rf state = %d\n",
387 __func__, rf_state);
388 _hal_ps_ntfy_hw_rf_state(phl_com, rf_state);
389 }
390
rtw_hal_ps_notify_wake(void * hal)391 void rtw_hal_ps_notify_wake(void *hal)
392 {
393 struct hal_info_t *hal_info = (struct hal_info_t *)hal;
394
395 rtw_hal_mac_ps_notify_wake(hal_info);
396 }
397
398 #endif /* CONFIG_POWER_SAVE */
399