// SPDX-License-Identifier: GPL-2.0
/******************************************************************************
*
* Copyright(c) 2009-2012 Realtek Corporation.
*
* Contact Information:
* wlanfae <wlanfae@realtek.com>
* Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
* Hsinchu 300, Taiwan.
*
* Larry Finger <Larry.Finger@lwfinger.net>
*
*****************************************************************************/
#include "wifi.h"
#include "base.h"
#include "ps.h"
#include <linux/export.h>
#include "btcoexist/rtl_btc.h"
bool rtl_ps_enable_nic(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw));
/*<1> reset trx ring */
if (rtlhal->interface == INTF_PCI)
rtlpriv->intf_ops->reset_trx_ring(hw);
if (is_hal_stop(rtlhal))
RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
"Driver is already down!\n");
/*<2> Enable Adapter */
if (rtlpriv->cfg->ops->hw_init(hw))
return false;
rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT,
&rtlmac->retry_long);
RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
/*<2.1> Switch Channel & Bandwidth to last rtl_op_config setting*/
rtlpriv->cfg->ops->switch_channel(hw);
rtlpriv->cfg->ops->set_channel_access(hw);
rtlpriv->cfg->ops->set_bw_mode(hw,
cfg80211_get_chandef_type(&hw->conf.chandef));
/*<3> Enable Interrupt */
rtlpriv->cfg->ops->enable_interrupt(hw);
/*<enable timer> */
rtl_watch_dog_timer_callback(&rtlpriv->works.watchdog_timer);
return true;
}
bool rtl_ps_disable_nic(struct ieee80211_hw *hw)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
/*<1> Stop all timer */
rtl_deinit_deferred_work(hw);
/*<2> Disable Interrupt */
rtlpriv->cfg->ops->disable_interrupt(hw);
tasklet_kill(&rtlpriv->works.irq_tasklet);
/*<3> Disable Adapter */
rtlpriv->cfg->ops->hw_disable(hw);
return true;
}
static bool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
enum rf_pwrstate state_toset,
u32 changesource)
{
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
enum rf_pwrstate rtstate;
bool actionallowed = false;
u16 rfwait_cnt = 0;
/*Only one thread can change
*the RF state at one time, and others
*should wait to be executed.
*/
while (true) {
spin_lock(&rtlpriv->locks.rf_ps_lock);
if (ppsc->rfchange_inprogress) {
spin_unlock(&rtlpriv->locks.rf_ps_lock);
RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
"RF Change in progress! Wait to set..state_toset(%d).\n",
state_toset);
/* Set RF after the previous action is done. */
while (ppsc->rfchange_inprogress) {
rfwait_cnt++;
mdelay(1);
/*Wait too long, return false to avoid
*to be stuck here.
*/
if (rfwait_cnt > 100)
return false;
}
} else {
ppsc->rfchange_inprogress = true;
spin_unlock(&rtlpriv->locks.rf_ps_lock);
break;
}
}
rtstate = ppsc->rfpwr_state;
switch (state_toset) {
case ERFON:
ppsc->rfoff_reason &= (~changesource);
if ((changesource == RF_CHANGE_BY_HW) &&
(ppsc->hwradiooff)) {
ppsc->hwradiooff = false;
}
if (!ppsc->rfoff_reason) {
ppsc->rfoff_reason = 0;
actionallowed = true;
}
break;
case ERFOFF:
if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff)
ppsc->hwradiooff = true;
ppsc->rfoff_reason |= changesource;
actionallowed = true;
break;
case ERFSLEEP:
ppsc->rfoff_reason |= changesource;
actionallowed = true;
break;
default:
pr_err("switch case %#x not processed\n", state_toset);
break;
}
if (actionallowed)
rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);
spin_lock(&rtlpriv->locks.rf_ps_lock);
ppsc->rfchange_inprogress = false;
spin_unlock(&rtlpriv->locks.rf_ps_lock);
return actionallowed;
}
static void