xref: /OK3568_Linux_fs/external/rkwifibt/drivers/rtl8852bs/phl/hal_g6/hal_ps.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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