Files
linux-nv-oot/drivers/net/wireless/realtek/rtl8852ce/phl/phl_ps.c
Shobek Attupurath c9aa3b2f1b rtl8852ce: Add v126-10 to fix disconnect with new channel
Issue - When WiFi operating channel is switched, at times the wifi
role index and role bitmap show that there is already a role
assigned for the channel context and this causes a failure in association. Kernel warning is shown when this occurs.

Fix - Update driver to v126-10 that fixes this issue.

[   57.590860] Call trace:
[   57.590861]  rtw_phl_chanctx_add+0x528/0x8f4 [rtl8852ce]
[   57.590947]  rtw_clear_is_accepted_status+0x4a4/0xbb8 [rtl8852ce]
[   57.591033]  cur_req_hdl+0x3c/0x4c [rtl8852ce]
[   57.591118]  msg_dispatch+0x2dc/0x3f8 [rtl8852ce]
[   57.591204]  dispr_thread_loop_hdl+0x270/0x2dc [rtl8852ce]
[   57.591289]  dispr_share_thread_loop_hdl+0x10/0x1c [rtl8852ce]
[   57.591374]  share_thread_hdl+0xb8/0x1a0 [rtl8852ce]
[   57.591459]  kthread+0x110/0x124
[   57.591466]  ret_from_fork+0x10/0x20

Bug 5440351
Bug 5442104

Change-Id: Ie78c70c1ea7a789351a2ba4ad445c4d0062281da
Signed-off-by: Shobek Attupurath <sattupurath@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3426784
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Ashutosh Jha <ajha@nvidia.com>
2025-08-11 20:27:49 -07:00

738 lines
21 KiB
C

/******************************************************************************
*
* Copyright(c) 2021 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 _PHL_PS_C_
#include "phl_headers.h"
#ifdef CONFIG_POWER_SAVE
const char *phl_ps_op_mode_to_str(u8 op_mode)
{
switch (op_mode) {
case PS_OP_MODE_DISABLED:
return "Disabled";
case PS_OP_MODE_FORCE_ENABLED:
return "Force Enabled";
case PS_OP_MODE_AUTO:
return "Auto";
default:
return "-";
}
}
const char *phl_ps_ps_mode_to_str(u8 ps_mode)
{
switch (ps_mode) {
case PS_MODE_IPS:
return "IPS";
case PS_MODE_LPS:
return "LPS";
default:
return "NONE";
}
}
#define case_pwr_lvl(src) \
case PS_PWR_LVL_##src: return #src
const char *phl_ps_pwr_lvl_to_str(u8 pwr_lvl)
{
switch (pwr_lvl) {
case_pwr_lvl(PWROFF);
case_pwr_lvl(PWR_GATED);
case_pwr_lvl(CLK_GATED);
case_pwr_lvl(RF_OFF);
case_pwr_lvl(PWRON);
case_pwr_lvl(MAX);
default:
return "Undefined";
}
}
u8 phl_ps_judge_pwr_lvl(u8 ps_cap, u8 ps_mode, u8 ps_en)
{
if (!ps_en)
return PS_PWR_LVL_PWRON;
if (ps_mode == PS_MODE_IPS) {
if (ps_cap & PS_CAP_PWR_OFF)
return PS_PWR_LVL_PWROFF;
else if (ps_cap & PS_CAP_PWR_GATED)
return PS_PWR_LVL_PWR_GATED;
else if (ps_cap & PS_CAP_CLK_GATED)
return PS_PWR_LVL_CLK_GATED;
else if (ps_cap & PS_CAP_RF_OFF)
return PS_PWR_LVL_RF_OFF;
else if (ps_cap & PS_CAP_PWRON)
return PS_PWR_LVL_PWRON;
else
return PS_PWR_LVL_PWROFF; /* ips default support power off */
} else if (ps_mode == PS_MODE_LPS) {
if (ps_cap & PS_CAP_PWR_GATED)
return PS_PWR_LVL_PWR_GATED;
else if (ps_cap & PS_CAP_CLK_GATED)
return PS_PWR_LVL_CLK_GATED;
else if (ps_cap & PS_CAP_RF_OFF)
return PS_PWR_LVL_RF_OFF;
else if (ps_cap & PS_CAP_PWRON)
return PS_PWR_LVL_PWRON;
else
return PS_PWR_LVL_PWRON; /* lps default support protocol */
} else {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS], %s(): unknown ps mode!\n", __func__);
}
return PS_PWR_LVL_PWRON;
}
static void _ps_ntfy_before_pwr_cfg(struct phl_info_t *phl_info, u8 ps_mode,
u8 cur_pwr_lvl, u8 req_pwr_lvl)
{
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS], %s(): \n", __func__);
if (cur_pwr_lvl == PS_PWR_LVL_PWRON) { /* enter ps */
#ifdef RTW_WKARD_DYNAMIC_PCIE_GEN
rtw_hal_pcie_gen_set(phl_info->hal, RTW_PCIE_GEN_2);
#endif
if (req_pwr_lvl == PS_PWR_LVL_PWROFF) {
#ifdef CONFIG_BTCOEX
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_WL_OFF);
#endif
#if defined(CONFIG_PCI_HCI) && defined(RTW_WKARD_DYNAMIC_LTR)
phl_ltr_sw_ctrl_ntfy(phl_info->phl_com, false);
#endif
} else if (req_pwr_lvl <= PS_PWR_LVL_RF_OFF) {
#ifdef CONFIG_BTCOEX
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_FW_CTRL);
#endif
#if defined(CONFIG_PCI_HCI) && defined(RTW_WKARD_DYNAMIC_LTR)
if (req_pwr_lvl == PS_PWR_LVL_PWR_GATED)
phl_ltr_sw_ctrl_ntfy(phl_info->phl_com, false);
#endif
}
}
}
static void _ps_ntfy_after_pwr_cfg(struct phl_info_t *phl_info, u8 ps_mode,
u8 cur_pwr_lvl, u8 req_pwr_lvl, u8 cfg_ok)
{
PHL_TRACE(COMP_PHL_PS, _PHL_DEBUG_, "[PS], %s(): \n", __func__);
if (cur_pwr_lvl > req_pwr_lvl) { /* enter ps */
if (!cfg_ok) { /* fail */
if (req_pwr_lvl == PS_PWR_LVL_PWROFF) { /* driver ips */
#ifdef CONFIG_BTCOEX
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_WL_ON);
#endif
#if defined(CONFIG_PCI_HCI) && defined(RTW_WKARD_DYNAMIC_LTR)
phl_ltr_sw_ctrl_ntfy(phl_info->phl_com, true);
#endif
} else { /* fw control */
#ifdef CONFIG_BTCOEX
if (ps_mode == PS_MODE_LPS)
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_LPS_WL_ON);
else
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_WL_ON);
#endif
#if defined(CONFIG_PCI_HCI) && defined(RTW_WKARD_DYNAMIC_LTR)
phl_ltr_sw_ctrl_ntfy(phl_info->phl_com, true);
#endif
}
}
} else { /* leave ps */
if (cfg_ok) { /* ok */
if (cur_pwr_lvl == PS_PWR_LVL_PWROFF) { /* driver ips */
if (req_pwr_lvl == PS_PWR_LVL_PWRON) {
#ifdef CONFIG_BTCOEX
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_WL_ON);
#endif
#if defined(CONFIG_PCI_HCI) && defined(RTW_WKARD_DYNAMIC_LTR)
phl_ltr_sw_ctrl_ntfy(phl_info->phl_com, true);
#endif
}
} else { /* fw control */
if (req_pwr_lvl == PS_PWR_LVL_PWRON) {
#ifdef CONFIG_BTCOEX
if (ps_mode == PS_MODE_LPS)
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_LPS_WL_ON);
else
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_WL_ON);
#endif
#if defined(CONFIG_PCI_HCI) && defined(RTW_WKARD_DYNAMIC_LTR)
phl_ltr_sw_ctrl_ntfy(phl_info->phl_com, true);
#endif
}
}
}
}
}
enum rtw_phl_status
_phl_ps_ctrl_datapath(struct phl_info_t *phl_info, bool pause)
{
struct phl_data_ctl_t ctl = {0};
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
ctl.id = PHL_MDL_POWER_MGNT;
ctl.cmd = (pause == true ? PHL_DATA_CTL_SW_TX_PAUSE : PHL_DATA_CTL_SW_TX_RESUME);
status = phl_data_ctrler(phl_info, &ctl, NULL);
if (status != RTW_PHL_STATUS_SUCCESS)
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): control tx fail (pause %d).\n", __func__, pause);
ctl.cmd = (pause == true ? PHL_DATA_CTL_SW_RX_PAUSE : PHL_DATA_CTL_SW_RX_RESUME);
status = phl_data_ctrler(phl_info, &ctl, NULL);
if (status != RTW_PHL_STATUS_SUCCESS)
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): control rx fail (pause %d).\n", __func__, pause);
return status;
}
static enum rtw_phl_status
_ps_ps_cfg_int(struct phl_info_t *phl_info, u8 ps_mode, enum phl_ps_ps_int_cfg_step step)
{
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
struct hal_spec_t *hal_spec = phl_get_ic_spec(phl_info->phl_com);
struct rtw_phl_evt_ops *evt_ops = NULL;
evt_ops = &phl_info->phl_com->evt_ops;
PHL_INFO("%s: ps_mode %d, step %d\n", __func__, ps_mode, step);
if (step == PS_LEAVE_CFG_INT_MAX)
return RTW_PHL_STATUS_SUCCESS;
/* check whether to config imr during ps */
if (!hal_spec->ps_cfg_int)
return RTW_PHL_STATUS_SUCCESS;
switch (step) {
case PS_ENTER_CFG_INT_PRE_PHASE:
/**
* 1. disable imr
* 2. stop datapath
*/
rtw_phl_disable_interrupt_sync(phl_info->phl_com);
_phl_ps_ctrl_datapath(phl_info, true);
break;
case PS_ENTER_CFG_INT_POST_PHASE:
/**
* 1. set imr using during low power
* 2. start datapath
* 3. enable imr
*/
rtw_hal_init_int_default_value(phl_info->phl_com, phl_info->hal, INT_SET_OPT_PS_START);
if (ps_mode == PS_MODE_LPS)
_phl_ps_ctrl_datapath(phl_info, false);
rtw_phl_enable_interrupt_sync(phl_info->phl_com);
break;
case PS_LEAVE_CFG_INT_PRE_PHASE:
/**
* 1. disable imr
* 2. stop datapath
*/
rtw_phl_disable_interrupt_sync(phl_info->phl_com);
if (ps_mode == PS_MODE_LPS)
_phl_ps_ctrl_datapath(phl_info, true);
break;
case PS_LEAVE_CFG_INT_POST_PHASE:
/**
* 1. set imr using after low power
* 2. start datapath
* 3. enable imr
*/
rtw_hal_init_int_default_value(phl_info->phl_com, phl_info->hal, INT_SET_OPT_PS_STOP);
_phl_ps_ctrl_datapath(phl_info, false);
rtw_phl_enable_interrupt_sync(phl_info->phl_com);
break;
default:
PHL_ERR("%s(): unknown step!\n", __func__);
break;
}
return status;
}
static enum phl_ps_ps_int_cfg_step
_judge_ps_cfg_int_step(u8 cur_pwr_lvl, u8 req_pwr_lvl, bool pre_phase)
{
switch (cur_pwr_lvl) {
case PS_PWR_LVL_PWRON: /* enter low power */
if (req_pwr_lvl == PS_PWR_LVL_CLK_GATED || req_pwr_lvl == PS_PWR_LVL_PWR_GATED)
return (pre_phase ? PS_ENTER_CFG_INT_PRE_PHASE : PS_ENTER_CFG_INT_POST_PHASE);
break;
case PS_PWR_LVL_CLK_GATED: /* leave low power */
case PS_PWR_LVL_PWR_GATED:
if (req_pwr_lvl == PS_PWR_LVL_PWRON)
return (pre_phase ? PS_LEAVE_CFG_INT_PRE_PHASE : PS_LEAVE_CFG_INT_POST_PHASE);
break;
default:
break;
}
return PS_LEAVE_CFG_INT_MAX;
}
enum rtw_phl_status
phl_ps_cfg_pwr_lvl(struct phl_info_t *phl_info, u8 ps_mode, u8 cur_pwr_lvl, u8 req_pwr_lvl)
{
enum rtw_hal_status hstatus = RTW_HAL_STATUS_SUCCESS;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): from %s to %s.\n",
__func__, phl_ps_pwr_lvl_to_str(cur_pwr_lvl), phl_ps_pwr_lvl_to_str(req_pwr_lvl));
if (cur_pwr_lvl == req_pwr_lvl) {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS], %s(): pwr lvl is not change!\n", __func__);
return RTW_HAL_STATUS_SUCCESS;
}
_ps_ntfy_before_pwr_cfg(phl_info, ps_mode, cur_pwr_lvl, req_pwr_lvl);
_ps_ps_cfg_int(phl_info, ps_mode, _judge_ps_cfg_int_step(cur_pwr_lvl, req_pwr_lvl, true));
hstatus = rtw_hal_ps_pwr_lvl_cfg(phl_info->phl_com, phl_info->hal,
req_pwr_lvl);
if (cur_pwr_lvl == PS_PWR_LVL_PWR_GATED)
hstatus = rtw_hal_pg_redownload_fw(phl_info->phl_com, phl_info->hal);
_ps_ps_cfg_int(phl_info, ps_mode, _judge_ps_cfg_int_step(cur_pwr_lvl, req_pwr_lvl, false));
_ps_ntfy_after_pwr_cfg(phl_info, ps_mode, cur_pwr_lvl, req_pwr_lvl,
(hstatus == RTW_HAL_STATUS_SUCCESS ? true : false));
return (hstatus == RTW_HAL_STATUS_SUCCESS) ? RTW_PHL_STATUS_SUCCESS : RTW_PHL_STATUS_FAILURE;
}
static enum rtw_phl_status _ps_ntfy_before_lps_proto_cfg(
struct phl_info_t *phl_info, u8 lps_en, u16 macid)
{
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): \n", __func__);
if (lps_en) { /* enter lps */
#ifdef CONFIG_BTCOEX
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_LPS_WL_ON);
#endif
if (RTW_HAL_STATUS_SUCCESS !=
rtw_hal_bb_lps_info_update(phl_info->hal, macid))
return RTW_PHL_STATUS_FAILURE;
}
return status;
}
static void
_ps_ntfy_after_lps_proto_cfg(struct phl_info_t *phl_info, u8 lps_en, u8 cfg_ok)
{
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): \n", __func__);
if (lps_en) { /* enter lps */
if (!cfg_ok) { /* fail */
#ifdef CONFIG_BTCOEX
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_WL_ON);
#endif
}
} else { /* leave lps */
if (cfg_ok) { /* ok */
#ifdef CONFIG_BTCOEX
rtw_hal_btc_radio_state_ntfy(phl_info->hal, BTC_RFCTRL_WL_ON);
#endif
}
}
}
static void
_phl_lps_role_config_tbtt_agg(struct phl_info_t *phl_info,
struct rtw_wifi_role_t *cur_wrole,
u32 tbtt_agg_val)
{
u8 role_idx, idx = 0;
struct rtw_wifi_role_t *wrole;
u32 tbtt_agg = tbtt_agg_val;
if (cur_wrole == NULL) {
PHL_ERR("%s cur role is NULL\n", __func__);
return;
}
for (role_idx = 0; role_idx < MAX_WIFI_ROLE_NUMBER; role_idx++) {
wrole = phl_get_wrole_by_ridx(phl_info, role_idx);
if(wrole == NULL || !wrole->active)
continue;
if (wrole == cur_wrole)
continue;
PHL_INFO("%s role %d config tbtt agg = %d\n",
__func__, role_idx, tbtt_agg_val);
for (idx = 0; idx < wrole->rlink_num; idx++) {
rtw_hal_role_cfg_ex(phl_info->hal, get_rlink(wrole, idx),
PCFG_TBTT_AGG, &tbtt_agg);
}
}
}
static void
_phl_ips_role_config_tbtt_agg(struct phl_info_t *phl_info, u32 tbtt_agg_val)
{
u8 role_idx, idx = 0;
struct rtw_wifi_role_t *wrole;
u32 tbtt_agg = tbtt_agg_val;
for (role_idx = 0; role_idx < MAX_WIFI_ROLE_NUMBER; role_idx++) {
wrole = phl_get_wrole_by_ridx(phl_info, role_idx);
if (wrole == NULL || !wrole->active)
continue;
PHL_INFO("%s role %d config tbtt agg = %d\n", __func__, role_idx,
tbtt_agg_val);
for (idx = 0; idx < wrole->rlink_num; idx++) {
rtw_hal_role_cfg_ex(phl_info->hal, get_rlink(wrole, idx),
PCFG_TBTT_AGG, &tbtt_agg);
}
}
}
enum rtw_phl_status
phl_ps_ips_cfg(struct phl_info_t *phl_info, struct ps_cfg *cfg, u8 en)
{
u32 tbtt_agg = en ? 0 : RTW_MAC_TBTT_AGG_DEF;
struct rtw_hal_ips_info ips_info = {0};
/* avoid waking up at each TBTT under disconnected standby */
_phl_ips_role_config_tbtt_agg(phl_info, tbtt_agg);
ips_info.en = en;
ips_info.macid = cfg->macid;
return rtw_hal_ps_ips_cfg(phl_info->hal, &ips_info);
}
enum rtw_phl_status
phl_ps_lps_cfg(struct phl_info_t *phl_info, struct ps_cfg *cfg, u8 en)
{
enum rtw_phl_status status = RTW_PHL_STATUS_SUCCESS;
struct rtw_hal_lps_info lps_info = {0};
struct rtw_wifi_role_t *wrole = NULL;
struct rtw_phl_stainfo_t *sta = NULL;
sta = rtw_phl_get_stainfo_by_macid(phl_info, cfg->macid);
if (sta != NULL) {
wrole = sta->wrole;
} else {
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS], %s(): cannot get sta!\n", __func__);
}
if (cfg->wow == false) {
status = phl_snd_cmd_ntfy_ps(phl_info, wrole, en);
if (RTW_PHL_STATUS_SUCCESS != status) {
status = RTW_PHL_STATUS_FAILURE;
return status;
}
}
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): %s lps, macid %d, wow %d.\n",
__func__, (en ? "enter" : "leave") ,cfg->macid, cfg->wow);
if (en) {
_phl_lps_role_config_tbtt_agg(phl_info, wrole, 0);
} else {
_phl_lps_role_config_tbtt_agg(phl_info, wrole, RTW_MAC_TBTT_AGG_DEF);
}
lps_info.en = en;
lps_info.macid = cfg->macid;
lps_info.listen_bcn_mode = cfg->listen_bcn_mode;
lps_info.awake_interval = cfg->awake_interval;
lps_info.smart_ps_mode = cfg->smart_ps_mode;
lps_info.bcnnohit_en = cfg->bcnnohit_en;
lps_info.dyntxant_en = (cfg->lps_force_tx > 0)?1:0;
lps_info.maxtxant = phl_info->phl_com->phy_cap[0].tx_path_num;
lps_info.lpstxant = (cfg->lps_force_tx == 1)?1:0;
if (RTW_PHL_STATUS_SUCCESS !=
_ps_ntfy_before_lps_proto_cfg(phl_info, en, lps_info.macid)) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): ntfy before lps fail.\n", __func__);
status = RTW_PHL_STATUS_FAILURE;
goto exit;
}
if (rtw_hal_ps_lps_cfg(phl_info->hal, &lps_info) != RTW_HAL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config lps fail.\n", __func__);
rtw_hal_notification(phl_info->hal, MSG_EVT_DBG_TX_DUMP, HW_PHY_0);
status = RTW_PHL_STATUS_FAILURE;
}
exit:
_ps_ntfy_after_lps_proto_cfg(phl_info, en, (status == RTW_PHL_STATUS_SUCCESS ? true : false));
return status;
}
static enum rtw_phl_status _lps_enter_proto_cfg(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
struct rtw_pkt_ofld_null_info null_info = {0};
struct rtw_phl_stainfo_t *phl_sta = NULL;
void *d = phl_to_drvpriv(phl_info);
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): \n", __func__);
#ifdef CONFIG_PHL_PS_FW_DBG
rtw_hal_en_fw_log_comp(phl_info->hal, FL_COMP_PS, true);
#endif
phl_sta = rtw_phl_get_stainfo_by_macid(phl_info, cfg->macid);
if (phl_sta == NULL)
return RTW_PHL_STATUS_FAILURE;
_os_mem_cpy(d, &(null_info.a1[0]), &(phl_sta->mac_addr[0]),
MAC_ADDRESS_LENGTH);
_os_mem_cpy(d,&(null_info.a2[0]), &(phl_sta->wrole->mac_addr[0]),
MAC_ADDRESS_LENGTH);
_os_mem_cpy(d, &(null_info.a3[0]), &(phl_sta->mac_addr[0]),
MAC_ADDRESS_LENGTH);
status = rtw_phl_pkt_ofld_request(phl_info, cfg->macid,
PKT_TYPE_NULL_DATA, cfg->token, &null_info, __func__);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): add null pkt ofld fail!\n", __func__);
return status;
}
status = phl_ps_lps_cfg(phl_info, cfg, true);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config lps fail!\n", __func__);
return status;
}
return RTW_PHL_STATUS_SUCCESS;
}
static enum rtw_phl_status _lps_leave_proto_cfg(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): \n", __func__);
status = phl_ps_lps_cfg(phl_info, cfg, false);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config lps fail!\n", __func__);
return status;
}
status = rtw_phl_pkt_ofld_cancel(phl_info, cfg->macid,
PKT_TYPE_NULL_DATA, cfg->token);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): del null pkt ofld fail!\n", __func__);
return status;
}
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status phl_ps_lps_proto_cfg(struct phl_info_t *phl_info, struct ps_cfg *cfg, bool lps_en)
{
if (lps_en)
return _lps_enter_proto_cfg(phl_info, cfg);
else
return _lps_leave_proto_cfg(phl_info, cfg);
}
enum rtw_phl_status phl_ps_lps_enter(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
if (cfg->proto_cfg) {
status = phl_ps_lps_proto_cfg(phl_info, cfg, true);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config lps protocol fail!\n", __func__);
return status;
}
}
if (cfg->pwr_cfg) {
status = phl_ps_cfg_pwr_lvl(phl_info, cfg->ps_mode, cfg->cur_pwr_lvl, cfg->pwr_lvl);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config lps pwr lvl fail!\n", __func__);
return status;
}
}
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status phl_ps_lps_leave(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
if (cfg->pwr_cfg) {
status = phl_ps_cfg_pwr_lvl(phl_info, cfg->ps_mode, cfg->cur_pwr_lvl, cfg->pwr_lvl);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config lps pwr lvl fail!\n", __func__);
return status;
}
}
if (cfg->proto_cfg) {
status = phl_ps_lps_proto_cfg(phl_info, cfg, false);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config lps protocol fail!\n", __func__);
return status;
}
}
return RTW_PHL_STATUS_SUCCESS;
}
enum rtw_phl_status phl_ps_ips_proto_cfg(struct phl_info_t *phl_info, struct ps_cfg *cfg, bool ips_en)
{
/* ips protocol config */
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): \n", __func__);
#ifdef CONFIG_PHL_PS_FW_DBG
rtw_hal_en_fw_log_comp(phl_info->hal, FL_COMP_PS, (ips_en ? true : false));
#endif
return phl_ps_ips_cfg(phl_info, cfg, ips_en);
}
enum rtw_phl_status phl_ps_ips_enter(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
if (cfg->proto_cfg) {
status = phl_ps_ips_proto_cfg(phl_info, cfg, true);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config ips protocol fail!\n", __func__);
return status;
}
}
if (cfg->pwr_cfg) {
status = phl_ps_cfg_pwr_lvl(phl_info, cfg->ps_mode, cfg->cur_pwr_lvl, cfg->pwr_lvl);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config ips pwr lvl fail!\n", __func__);
return status;
}
}
return status;
}
enum rtw_phl_status phl_ps_ips_leave(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
if (cfg->pwr_cfg) {
status = phl_ps_cfg_pwr_lvl(phl_info, cfg->ps_mode, cfg->cur_pwr_lvl, cfg->pwr_lvl);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config ips pwr lvl fail!\n", __func__);
return status;
}
}
if (cfg->proto_cfg) {
status = phl_ps_ips_proto_cfg(phl_info, cfg, false);
if (status != RTW_PHL_STATUS_SUCCESS) {
PHL_TRACE(COMP_PHL_PS, _PHL_ERR_, "[PS], %s(): config ips protocol fail!\n", __func__);
return status;
}
}
return status;
}
static void _ps_ntfy(struct phl_info_t *phl_info, struct ps_cfg *cfg, enum phl_msg_evt_id event)
{
struct rtw_wifi_role_link_t *rlink = NULL;
struct rtw_phl_stainfo_t *sta = NULL;
sta = rtw_phl_get_stainfo_by_macid(phl_info, cfg->macid);
if (sta != NULL)
rlink = sta->rlink;
else
PHL_TRACE(COMP_PHL_PS, _PHL_WARNING_, "[PS], %s(): cannot get sta!\n", __func__);
if (rlink != NULL)
rtw_hal_notification(phl_info->hal, event, rlink->hw_band);
}
enum rtw_phl_status
phl_ps_enter_ps(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): ps mode(%s), pwr lvl(%s), macid(%d), proto_cfg(%d), pwr_cfg(%d)\n",
__func__, phl_ps_ps_mode_to_str(cfg->ps_mode), phl_ps_pwr_lvl_to_str(cfg->pwr_lvl),
cfg->macid, cfg->proto_cfg, cfg->pwr_cfg);
if (cfg->ps_mode == PS_MODE_LPS) {
status = phl_ps_lps_enter(phl_info, cfg);
if (status == RTW_PHL_STATUS_SUCCESS)
_ps_ntfy(phl_info, cfg, MSG_EVT_PS_LPS_ENTER);
} else if (cfg->ps_mode == PS_MODE_IPS) {
status = phl_ps_ips_enter(phl_info, cfg);
}
return status;
}
enum rtw_phl_status
phl_ps_leave_ps(struct phl_info_t *phl_info, struct ps_cfg *cfg)
{
enum rtw_phl_status status = RTW_PHL_STATUS_FAILURE;
PHL_TRACE(COMP_PHL_PS, _PHL_INFO_, "[PS], %s(): current ps mode(%s), pwr lvl(%s), macid(%d), proto_cfg(%d), pwr_cfg(%d)\n",
__func__, phl_ps_ps_mode_to_str(cfg->ps_mode), phl_ps_pwr_lvl_to_str(cfg->pwr_lvl),
cfg->macid, cfg->proto_cfg, cfg->pwr_cfg);
if (cfg->ps_mode == PS_MODE_LPS) {
status = phl_ps_lps_leave(phl_info, cfg);
if (status == RTW_PHL_STATUS_SUCCESS)
_ps_ntfy(phl_info, cfg, MSG_EVT_PS_LPS_LEAVE);
} else if (cfg->ps_mode == PS_MODE_IPS) {
status = phl_ps_ips_leave(phl_info, cfg);
}
return status;
}
bool
phl_ps_sta_in_lps(struct phl_info_t *phl, struct rtw_phl_stainfo_t *sta)
{
if (PS_MODE_LPS != phl_ps_get_cur_ps_mode(phl))
return false;
else
return true;
}
void
phl_ps_sta_ext_trx_nty(struct phl_info_t *phl, u16 macid)
{
struct rtw_phl_lps_adv_cfg cfg = phl_ps_get_cur_adv_cfg(phl);
rtw_hal_lps_ext_trx_ntfy(phl->hal, &cfg, macid);
}
#endif