/****************************************************************************** * * Copyright(c) 2019 Realtek Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * *****************************************************************************/ #define _HAL_PS_C_ #include "hal_headers.h" #ifdef CONFIG_POWER_SAVE #define case_pwr_state(src) \ case PS_PWR_STATE_##src: return #src const char *hal_ps_pwr_state_to_str(u8 pwr_state) { switch (pwr_state) { case_pwr_state(ACTIVE); case_pwr_state(BAND0_RFON); case_pwr_state(BAND1_RFON); case_pwr_state(BAND0_RFOFF); case_pwr_state(BAND1_RFOFF); case_pwr_state(CLK_GATED); case_pwr_state(PWR_GATED); case_pwr_state(MAX); default: return "Undefined"; } } #define LPS_PROTOCAL_LEAVE_TOLERANCE 50 /* ms */ #define PWR_LVL_CHANGE_TOLERANCE 50 /* ms */ #define MAX_CHK_PWR_STATE_CHANGE_CNT 5 static enum rtw_hal_status _hal_ps_lps_chk_leave(struct hal_info_t *hal_info, u16 macid) { enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS; u32 mac_sts = 0; u32 priv_mac_sts = 0; u32 start_time = _os_get_cur_time_ms(); u32 pass_time = 0; do { status = rtw_hal_mac_lps_chk_leave(hal_info, macid, &mac_sts); if (status == RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): pass time = %d ms.\n", __func__, pass_time); break; } if (mac_sts != priv_mac_sts) { PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): mac status %u.\n", __func__, mac_sts); priv_mac_sts = mac_sts; } pass_time = phl_get_passing_time_ms(start_time); if (pass_time > LPS_PROTOCAL_LEAVE_TOLERANCE) break; _os_sleep_us(hal_to_drvpriv(hal_info), 50); } while (1); #ifdef CONFIG_PHL_PS_FW_DBG rtw_hal_fw_dbg_dump(hal_info); #endif if (status != RTW_HAL_STATUS_SUCCESS) PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): polling timeout!\n", __func__); return status; } static enum rtw_hal_status _hal_ps_lps_cfg(struct hal_info_t *hal_info, struct rtw_hal_lps_info *lps_info) { PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): mode(%d), listen bcn mode(%d), awake interval(%d), smart_ps_mode(%d).\n", __func__, lps_info->en, lps_info->listen_bcn_mode, lps_info->awake_interval, lps_info->smart_ps_mode); return rtw_hal_mac_lps_cfg(hal_info, lps_info); } static enum rtw_hal_status _hal_ps_pwr_state_chk(struct hal_info_t *hal_info, u8 req_pwr_state) { enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS; u32 mac_sts = 0; u32 priv_mac_sts = 0; u32 start_time = _os_get_cur_time_ms(); u32 pass_time = 0; do { status = rtw_hal_mac_chk_pwr_state(hal_info, req_pwr_state, &mac_sts); if (status == RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): pass time = %d ms.\n", __func__, pass_time); break; } if (mac_sts != priv_mac_sts) { PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): mac status %u.\n", __func__, mac_sts); priv_mac_sts = mac_sts; } pass_time = phl_get_passing_time_ms(start_time); if (pass_time > PWR_LVL_CHANGE_TOLERANCE) break; _os_sleep_us(hal_to_drvpriv(hal_info), 50); } while (1); #ifdef CONFIG_PHL_PS_FW_DBG rtw_hal_fw_dbg_dump(hal_info); #endif if (status != RTW_HAL_STATUS_SUCCESS) PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): polling timeout!\n", __func__); return status; } static enum rtw_hal_status _hal_ps_cfg_pwr_state(struct hal_info_t *hal_info, u8 req_pwr_state) { #if (HAL_LPS_SKIP_HW_CFG == 0) enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS; u8 cnt = 0; for (cnt = 0; cnt < MAX_CHK_PWR_STATE_CHANGE_CNT; cnt++) { /* rpwm */ status = rtw_hal_mac_req_pwr_state(hal_info, req_pwr_state); if (status != RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): set pwr state %s fail!\n", __func__, hal_ps_pwr_state_to_str(req_pwr_state)); return RTW_HAL_STATUS_FAILURE; } /* cpwm */ status = _hal_ps_pwr_state_chk(hal_info, req_pwr_state); if (status == RTW_HAL_STATUS_SUCCESS) break; } if (cnt < MAX_CHK_PWR_STATE_CHANGE_CNT) { PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): chk pwr state %s success, cnt %d.\n", __func__, hal_ps_pwr_state_to_str(req_pwr_state), cnt); return RTW_HAL_STATUS_SUCCESS; } else { PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): chk pwr state %s fail!\n", __func__, hal_ps_pwr_state_to_str(req_pwr_state)); return RTW_HAL_STATUS_FAILURE; } #else return RTW_HAL_STATUS_SUCCESS; #endif } static enum rtw_hal_status _hal_ps_cfg_pwr_on(struct rtw_phl_com_t *phl_com, struct hal_info_t *hal_info) { struct rtw_phl_evt_ops *ops = &phl_com->evt_ops; enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS; if (hal_info->hal_com->is_hal_init) { #if (HAL_LPS_SKIP_HW_CFG == 0) status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_ACTIVE); #else status = RTW_HAL_STATUS_SUCCESS; #endif } else { #if (HAL_IPS_SKIP_HW_CFG == 0) if (ops->set_rf_state(phlcom_to_drvpriv(phl_com), RTW_RF_ON) == true) status = RTW_HAL_STATUS_SUCCESS; else status = RTW_HAL_STATUS_FAILURE; #else status = RTW_HAL_STATUS_SUCCESS; #endif } return status; } static enum rtw_hal_status _hal_ps_cfg_pwr_off(struct rtw_phl_com_t *phl_com, struct hal_info_t *hal_info) { struct rtw_phl_evt_ops *ops = &phl_com->evt_ops; #if (HAL_IPS_SKIP_HW_CFG == 0) if (ops->set_rf_state(phlcom_to_drvpriv(phl_com), RTW_RF_OFF) == true) return RTW_HAL_STATUS_SUCCESS; else return RTW_HAL_STATUS_FAILURE; #else return RTW_HAL_STATUS_SUCCESS; #endif } 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) { enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE; switch (req_pwr_lvl) { case PS_PWR_LVL_PWROFF: status = _hal_ps_cfg_pwr_off(phl_com, hal_info); break; case PS_PWR_LVL_PWR_GATED: status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_PWR_GATED); break; case PS_PWR_LVL_CLK_GATED: status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_CLK_GATED); break; case PS_PWR_LVL_RF_OFF: status = _hal_ps_cfg_pwr_state(hal_info, PS_PWR_STATE_BAND0_RFOFF); break; case PS_PWR_LVL_PWRON: status = _hal_ps_cfg_pwr_on(phl_com, hal_info); break; default: PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[HALPS], %s(): undefined pwr lvl!\n", __func__); break; } return status; } void _hal_ps_proc_hw_rf_state_done(void* priv, struct phl_msg* msg) { struct rtw_phl_com_t *phl_com = (struct rtw_phl_com_t *)priv; if (msg->inbuf && msg->inlen) { _os_kmem_free(phlcom_to_drvpriv(phl_com), msg->inbuf, msg->inlen); } } static void _hal_ps_ntfy_hw_rf_state(struct rtw_phl_com_t *phl_com, enum rtw_rf_state rf_state) { struct phl_msg msg = {0}; struct phl_msg_attribute attr = {0}; void *d = phlcom_to_drvpriv(phl_com); enum rtw_rf_state *rf_ntfy = NULL; rf_ntfy = (enum rtw_rf_state *)_os_kmem_alloc(d, sizeof(*rf_ntfy)); if (rf_ntfy == NULL) { PHL_ERR("[HALPS], %s(): alloc for ntfy fail.\n", __func__); return; } *rf_ntfy = rf_state; msg.inbuf = (u8 *)rf_ntfy; msg.inlen = sizeof(*rf_ntfy); SET_MSG_MDL_ID_FIELD(msg.msg_id, PHL_MDL_POWER_MGNT); SET_MSG_EVT_ID_FIELD(msg.msg_id, MSG_EVT_HW_RF_CHG); attr.completion.completion = _hal_ps_proc_hw_rf_state_done; attr.completion.priv = phl_com; if (rtw_phl_msg_hub_hal_send(phl_com, &attr, &msg) != RTW_PHL_STATUS_SUCCESS) { PHL_ERR("[HALPS], %s(): send msg failed\n", __func__); _os_kmem_free(d, rf_ntfy, sizeof(*rf_ntfy)); } } /** * configured requested power level * return success if configure power level ok * @phl_com: see rtw_phl_com_t * @hal: see hal_info_t * @req_pwr_lvl: target power level to configured */ enum rtw_hal_status rtw_hal_ps_pwr_lvl_cfg(struct rtw_phl_com_t *phl_com, void *hal, u8 req_pwr_lvl) { enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE; struct hal_info_t *hal_info = (struct hal_info_t *)hal; status = _hal_ps_pwr_lvl_cfg(phl_com, hal_info, req_pwr_lvl); if (status != RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): pwr lvl cfg fail!\n", __func__); } return status; } /** * configure the legacy power save (protocol) * return configure lps fail or not * @hal: see hal_info_t * @lps: see rtw_hal_lps_info */ enum rtw_hal_status rtw_hal_ps_lps_cfg(void *hal, struct rtw_hal_lps_info *lps_info) { enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE; struct hal_info_t *hal_info = (struct hal_info_t *)hal; status = _hal_ps_lps_cfg(hal_info, lps_info); if (status == RTW_HAL_STATUS_SUCCESS) { if (lps_info->en == false) status = _hal_ps_lps_chk_leave(hal_info, lps_info->macid); } return status; } enum rtw_hal_status rtw_hal_ps_pwr_req(struct rtw_phl_com_t *phl_com, u8 src, bool pwr_req) { enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE; status = phl_ps_hal_pwr_req(phl_com, src, pwr_req); if (status != RTW_PHL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): fail (pwr_req %d).\n", __func__, pwr_req); return RTW_HAL_STATUS_FAILURE; } return RTW_HAL_STATUS_SUCCESS; } enum rtw_hal_status rtw_hal_ps_ips_cfg(void *hal, struct rtw_hal_ips_info *ips_info) { enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS; struct hal_info_t *hal_info = (struct hal_info_t *)hal; hstatus = rtw_hal_mac_ips_cfg(hal, ips_info->macid, ips_info->en); if (hstatus == RTW_HAL_STATUS_SUCCESS && !ips_info->en) hstatus = rtw_hal_mac_ips_chk_leave(hal_info, ips_info->macid); return hstatus; } void rtw_hal_ps_chk_hw_rf_state(struct rtw_phl_com_t *phl_com, void *hal) { struct hal_info_t *hal_info = (struct hal_info_t *)hal; enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS; enum rtw_rf_state rf_state = RTW_RF_ON; u8 val = 0; hstatus = rtw_hal_mac_get_wl_dis_val(hal_info, &val); if (hstatus != RTW_HAL_STATUS_SUCCESS) { PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[HALPS], %s(): get wl dis val fail, status: %d\n", __func__, hstatus); return; } /* get new rf state */ if (val == 1) { rf_state = RTW_RF_ON; } else if (val == 0) { rf_state = RTW_RF_OFF; } else { PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): wl_dis is invalid value: %d\n", __func__, val); return; } PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[HALPS], %s(): rf state = %d\n", __func__, rf_state); _hal_ps_ntfy_hw_rf_state(phl_com, rf_state); } void rtw_hal_ps_notify_wake(void *hal) { struct hal_info_t *hal_info = (struct hal_info_t *)hal; rtw_hal_mac_ps_notify_wake(hal_info); } #endif /* CONFIG_POWER_SAVE */