ps.c revision 099fb8ab1e57e5d609ac686cc0ab6d1835a79155
10c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/******************************************************************************
20c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
30c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Copyright(c) 2009-2010  Realtek Corporation.
40c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
50c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * This program is free software; you can redistribute it and/or modify it
60c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * under the terms of version 2 of the GNU General Public License as
70c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * published by the Free Software Foundation.
80c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
90c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * This program is distributed in the hope that it will be useful, but WITHOUT
100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * more details.
130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * You should have received a copy of the GNU General Public License along with
150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * this program; if not, write to the Free Software Foundation, Inc.,
160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * The full GNU General Public License is included in this distribution in the
190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * file called LICENSE.
200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Contact Information:
220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * wlanfae <wlanfae@realtek.com>
230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Hsinchu 300, Taiwan.
250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger * Larry Finger <Larry.Finger@lwfinger.net>
270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *
280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *****************************************************************************/
290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "wifi.h"
310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "base.h"
320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger#include "ps.h"
330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_enable_nic(struct ieee80211_hw *hw)
350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<1> reset trx ring */
410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlhal->interface == INTF_PCI)
420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtlpriv->intf_ops->reset_trx_ring(hw);
430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (is_hal_stop(rtlhal))
450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 ("Driver is already down!\n"));
470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<2> Enable Adapter */
490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->hw_init(hw);
500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<3> Enable Interrupt */
530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->enable_interrupt(hw);
540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<enable timer> */
560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtl_watch_dog_timer_callback((unsigned long)hw);
570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
58cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	return true;
590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_enable_nic);
610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_disable_nic(struct ieee80211_hw *hw)
630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<1> Stop all timer */
670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtl_deinit_deferred_work(hw);
680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<2> Disable Interrupt */
700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->disable_interrupt(hw);
710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*<3> Disable Adapter */
730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtlpriv->cfg->ops->hw_disable(hw);
740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
75324732848c42bf79988479ee1b4359e15f08154bLarry Finger	return true;
760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_disable_nic);
780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerbool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 enum rf_pwrstate state_toset,
810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 u32 changesource, bool protect_or_not)
820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	enum rf_pwrstate rtstate;
867ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	bool actionallowed = false;
870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	u16 rfwait_cnt = 0;
880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	unsigned long flag;
890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*protect_or_not = true; */
910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (protect_or_not)
930c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		goto no_protect;
940c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*
960c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *Only one thread can change
970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *the RF state at one time, and others
980c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *should wait to be executed.
990c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 */
1000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	while (true) {
1010c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
1020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (ppsc->rfchange_inprogress) {
1030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock,
1040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					       flag);
1050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
1070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				 ("RF Change in progress!"
1080c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				  "Wait to set..state_toset(%d).\n",
1090c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				  state_toset));
1100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			/* Set RF after the previous action is done.  */
1120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			while (ppsc->rfchange_inprogress) {
1130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rfwait_cnt++;
1140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				mdelay(1);
1150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				/*
1170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				 *Wait too long, return false to avoid
1180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				 *to be stuck here.
1190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				 */
1200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				if (rfwait_cnt > 100)
1210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					return false;
1220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			}
1230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		} else {
1240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->rfchange_inprogress = true;
1250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock,
1260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					       flag);
1270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			break;
1280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerno_protect:
1320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtstate = ppsc->rfpwr_state;
1330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	switch (state_toset) {
1350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFON:
1360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason &= (~changesource);
1370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if ((changesource == RF_CHANGE_BY_HW) &&
1397ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    (ppsc->hwradiooff == true)) {
1407ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->hwradiooff = false;
1410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (!ppsc->rfoff_reason) {
1440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->rfoff_reason = 0;
1457ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			actionallowed = true;
1460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFOFF:
1510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if ((changesource == RF_CHANGE_BY_HW)
1537ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    && (ppsc->hwradiooff == false)) {
1547ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->hwradiooff = true;
1550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
1560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason |= changesource;
1587ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		actionallowed = true;
1590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	case ERFSLEEP:
1620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfoff_reason |= changesource;
1637ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		actionallowed = true;
1640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	default:
1670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
1680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 ("switch case not process\n"));
1690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		break;
1700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1727ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (actionallowed)
1730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);
1740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (!protect_or_not) {
1760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
1770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		ppsc->rfchange_inprogress = false;
1780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
1790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
1800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1817ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	return actionallowed;
1820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
1830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry FingerEXPORT_SYMBOL(rtl_ps_set_rf_state);
1840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic void _rtl_ps_inactive_ps(struct ieee80211_hw *hw)
1860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
1870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
1880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
1890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
1900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
1917ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	ppsc->swrf_processing = true;
1920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
193099fb8ab1e57e5d609ac686cc0ab6d1835a79155Larry Finger	if (ppsc->inactive_pwrstate == ERFON &&
194cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    rtlhal->interface == INTF_PCI) {
1950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) &&
196cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
1970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    rtlhal->interface == INTF_PCI) {
1980c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->intf_ops->disable_aspm(hw);
199cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
2000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
2010c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate,
2040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			    RF_CHANGE_BY_IPS, false);
2050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ppsc->inactive_pwrstate == ERFOFF &&
2070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	    rtlhal->interface == INTF_PCI) {
208cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
209cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			!RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
2100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->intf_ops->enable_aspm(hw);
211cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
2120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
2130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2157ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	ppsc->swrf_processing = false;
2160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_off_wq_callback(void *data)
2190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_works *rtlworks =
2210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	    container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq);
2220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct ieee80211_hw *hw = rtlworks->hw;
2230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
2240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
2250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
2260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
2270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	enum rf_pwrstate rtstate;
2280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode != NL80211_IFTYPE_STATION) {
2300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
2310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 ("not station return\n"));
2320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
2330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
235cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->link_state > MAC80211_NOLINK)
236cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
237cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
2380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (is_hal_stop(rtlhal))
2390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
2400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlpriv->sec.being_setkey)
2420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
2430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2447ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (ppsc->inactiveps) {
2450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtstate = ppsc->rfpwr_state;
2460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		/*
2480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *Do not enter IPS in the following conditions:
2490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(1) RF is already OFF or Sleep
2507ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		 *(2) swrf_processing (indicates the IPS is still under going)
2510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(3) Connectted (only disconnected can trigger IPS)
2520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(4) IBSS (send Beacon)
2530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(5) AP mode (send Beacon)
2540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 *(6) monitor mode (rcv packet)
2550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		 */
2560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (rtstate == ERFON &&
2587ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    !ppsc->swrf_processing &&
2590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    (mac->link_state == MAC80211_NOLINK) &&
2600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    !mac->act_scanning) {
2610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
2620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				 ("IPSEnter(): Turn off RF.\n"));
2630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->inactive_pwrstate = ERFOFF;
2657ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->in_powersavemode = true;
2660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			/*rtl_pci_reset_trx_ring(hw); */
2680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			_rtl_ps_inactive_ps(hw);
2690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
2700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
2710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_off(struct ieee80211_hw *hw)
2740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
2760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*
2780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *because when link with ap, mac80211 will ask us
2790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *to disable nic quickly after scan before linking,
2800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *this will cause link failed, so we delay 100ms here
2810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 */
2820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	queue_delayed_work(rtlpriv->works.rtl_wq,
2830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			   &rtlpriv->works.ips_nic_off_wq, MSECS(100));
2840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
2850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_ips_nic_on(struct ieee80211_hw *hw)
2870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
2880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
289cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
2900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
2910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	enum rf_pwrstate rtstate;
292d704300fa546a613ec3821b908528b20685cb92aLarry Finger	unsigned long flags;
2930c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
294cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->opmode != NL80211_IFTYPE_STATION)
295cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
296cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
297d704300fa546a613ec3821b908528b20685cb92aLarry Finger	spin_lock_irqsave(&rtlpriv->locks.ips_lock, flags);
2980c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
2997ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (ppsc->inactiveps) {
3000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		rtstate = ppsc->rfpwr_state;
3010c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (rtstate != ERFON &&
3037ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		    !ppsc->swrf_processing &&
3040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		    ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) {
3050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3060c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			ppsc->inactive_pwrstate = ERFON;
3077ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			ppsc->in_powersavemode = false;
3080c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3090c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			_rtl_ps_inactive_ps(hw);
3100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
3110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
3120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
313d704300fa546a613ec3821b908528b20685cb92aLarry Finger	spin_unlock_irqrestore(&rtlpriv->locks.ips_lock, flags);
3140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
3150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*for FW LPS*/
3170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*
3190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *Determine if we can set Fw into PS mode
3200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *in current condition.Return TRUE if it
3210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger *can enter PS mode.
3220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger */
3230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic bool rtl_get_fwlps_doze(struct ieee80211_hw *hw)
3240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
3250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
3260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
3280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	u32 ps_timediff;
3290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	ps_timediff = jiffies_to_msecs(jiffies -
3310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				       ppsc->last_delaylps_stamp_jiffies);
3320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3330c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ps_timediff < 2000) {
3340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
3350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			 ("Delay enter Fw LPS for DHCP, ARP,"
3360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			  " or EAPOL exchanging state.\n"));
3370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
3380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
3390c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
3410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
3420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return false;
3450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	return true;
3470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
3480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/* Change current and default preamble mode.*/
3500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingerstatic void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
3510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
3520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
3530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
3540c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
3550c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	u8 rpwm_val, fw_pwrmode;
3560c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3570c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
3580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3600c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
3610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3630c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (ppsc->dot11_psmode == rt_psmode)
3640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
3650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/* Update power save mode configured. */
3670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	ppsc->dot11_psmode = rt_psmode;
3680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*
3700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *<FW control LPS>
3710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *1. Enter PS mode
3720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   Set RPWM to Fw to turn RF off and send H2C fw_pwrmode
3730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   cmd to set Fw into PS mode.
3740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *2. Leave PS mode
3750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   Send H2C fw_pwrmode cmd to Fw to set Fw into Active
3760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 *   mode and set RPWM to turn RF on.
3770c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	 */
3780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
379cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((ppsc->fwctrl_lps) && ppsc->report_linked) {
3807ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger		bool fw_current_inps;
3810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (ppsc->dot11_psmode == EACTIVE) {
3820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
3830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				 ("FW LPS leave ps_mode:%x\n",
3840c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				  FW_PS_ACTIVE_MODE));
3850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rpwm_val = 0x0C;	/* RF on */
3870c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			fw_pwrmode = FW_PS_ACTIVE_MODE;
3880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SET_RPWM,
3890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					(u8 *) (&rpwm_val));
3900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw,
3910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					HW_VAR_H2C_FW_PWRMODE,
3920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					(u8 *) (&fw_pwrmode));
3937ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger			fw_current_inps = false;
3940c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->cfg->ops->set_hw_reg(hw,
3960c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					HW_VAR_FW_PSMODE_STATUS,
3977ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger					(u8 *) (&fw_current_inps));
3980c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
3990c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		} else {
4000c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			if (rtl_get_fwlps_doze(hw)) {
4010c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
4020c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger						("FW LPS enter ps_mode:%x\n",
4030c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger						 ppsc->fwctrl_psmode));
4040c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4050c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rpwm_val = 0x02;	/* RF off */
4067ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger				fw_current_inps = true;
4070c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->cfg->ops->set_hw_reg(hw,
4080c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger						HW_VAR_FW_PSMODE_STATUS,
4097ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger						(u8 *) (&fw_current_inps));
4100c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->cfg->ops->set_hw_reg(hw,
4110c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger						HW_VAR_H2C_FW_PWRMODE,
4120c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger						(u8 *) (&ppsc->fwctrl_psmode));
4130c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4140c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->cfg->ops->set_hw_reg(hw,
4150c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger						HW_VAR_SET_RPWM,
4160c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger						(u8 *) (&rpwm_val));
4170c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			} else {
4180c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				/* Reset the power save related parameters. */
4190c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				ppsc->dot11_psmode = EACTIVE;
4200c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			}
4210c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4220c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
4230c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
4240c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4250c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*Enter the leisure power save mode.*/
4260c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_lps_enter(struct ieee80211_hw *hw)
4270c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
4280c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
4290c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
4300c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
4310c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	unsigned long flag;
4320c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
433cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!ppsc->fwctrl_lps)
4340c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4350c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4360c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (rtlpriv->sec.being_setkey)
4370c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4380c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4397ea4724036ed17ec811cb8082af7760f04484ef7Larry Finger	if (rtlpriv->link_info.busytraffic)
4400c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4410c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4420c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
4430c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->cnt_after_linked < 5)
4440c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4450c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4460c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->opmode == NL80211_IFTYPE_ADHOC)
4470c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4480c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4490c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	if (mac->link_state != MAC80211_LINKED)
4500c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		return;
4510c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4520c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
4530c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
454cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* Idle for a while if we connect to AP a while ago. */
455cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->cnt_after_linked >= 2) {
456cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ppsc->dot11_psmode == EACTIVE) {
457cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
4580c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger					("Enter 802.11 power save mode...\n"));
4590c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
460cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtl_lps_set_psmode(hw, EAUTOPS);
4610c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4620c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
463cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
4640c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
4650c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
4660c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4670c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger/*Leave the leisure power save mode.*/
4680c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Fingervoid rtl_lps_leave(struct ieee80211_hw *hw)
4690c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger{
4700c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_priv *rtlpriv = rtl_priv(hw);
4710c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
4720c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
4730c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	unsigned long flag;
4740c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4750c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
4760c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
477cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->fwctrl_lps) {
4780c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		if (ppsc->dot11_psmode != EACTIVE) {
4790c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4800c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			/*FIX ME */
4810c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtlpriv->cfg->ops->enable_interrupt(hw);
4820c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4830c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
484cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
4850c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			    rtlhal->interface == INTF_PCI) {
4860c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				rtlpriv->intf_ops->disable_aspm(hw);
487cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
4880c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			}
4890c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4900c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
4910c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger				 ("Busy Traffic,Leave 802.11 power save..\n"));
4920c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger
4930c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger			rtl_lps_set_psmode(hw, EACTIVE);
4940c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger		}
4950c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	}
4960c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
4970c8173385e549f95cd80c3fff5aab87b4f881d8dLarry Finger}
498cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
499cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li/* For sw LPS*/
500cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
501cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
502cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
503cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
504cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_hdr *hdr = (void *) data;
505cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_tim_ie *tim_ie;
506cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 *tim;
507cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 tim_len;
508cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool u_buffed;
509cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool m_buffed;
510cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
511cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->opmode != NL80211_IFTYPE_STATION)
512cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
513cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
514cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.swctrl_lps)
515cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
516cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
517cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->mac80211.link_state != MAC80211_LINKED)
518cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
519cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
520cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.sw_ps_enabled)
521cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
522cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
523cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.fwctrl_lps)
524cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
525cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
526cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (likely(!(hw->conf.flags & IEEE80211_CONF_PS)))
527cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
528cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
529cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* check if this really is a beacon */
530cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!ieee80211_is_beacon(hdr->frame_control))
531cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
532cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
533cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* min. beacon length + FCS_LEN */
534cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (len <= 40 + FCS_LEN)
535cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
536cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
537cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* and only beacons from the associated BSSID, please */
538cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (compare_ether_addr(hdr->addr3, rtlpriv->mac80211.bssid))
539cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
540cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
541cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtlpriv->psc.last_beacon = jiffies;
542cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
543cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
544cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!tim)
545cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
546cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
547cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (tim[1] < sizeof(*tim_ie))
548cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
549cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
550cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim_len = tim[1];
551cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	tim_ie = (struct ieee80211_tim_ie *) &tim[2];
552cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
553cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period))
554cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.dtim_counter = tim_ie->dtim_count;
555cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
556cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* Check whenever the PHY can be turned off again. */
557cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
558cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* 1. What about buffered unicast traffic for our AID? */
559cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u_buffed = ieee80211_check_tim(tim_ie, tim_len,
560cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				       rtlpriv->mac80211.assoc_id);
561cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
562cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* 2. Maybe the AP wants to send multicast/broadcast data? */
563cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	m_buffed = tim_ie->bitmap_ctrl & 0x01;
564cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtlpriv->psc.multi_buffered = m_buffed;
565cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
566cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* unicast will process by mac80211 through
567cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * set ~IEEE80211_CONF_PS, So we just check
568cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * multicast frames here */
569cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!m_buffed) {
570cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		/* back to low-power land. and delay is
571cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		 * prevent null power save frame tx fail */
572cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		queue_delayed_work(rtlpriv->works.rtl_wq,
573cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				&rtlpriv->works.ps_work, MSECS(5));
574cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	} else {
575cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG, ("u_bufferd: %x, "
576cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				"m_buffered: %x\n", u_buffed, m_buffed));
577cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
578cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
579cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
580cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rf_awake(struct ieee80211_hw *hw)
581cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
582cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
583cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
584cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
585cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	unsigned long flag;
586cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
587cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.swctrl_lps)
588cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
589cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (mac->link_state != MAC80211_LINKED)
590cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
591cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
592cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
593cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
594cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->intf_ops->disable_aspm(hw);
595cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
596cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
597cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
598cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
599cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS, false);
600cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
601cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
602cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
603cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rfon_wq_callback(void *data)
604cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
605cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_works *rtlworks =
606cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq);
607cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_hw *hw = rtlworks->hw;
608cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
609cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtl_swlps_rf_awake(hw);
610cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
611cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
612cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
613cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
614cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
615cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
616cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
617cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	unsigned long flag;
618cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	u8 sleep_intv;
619cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
620cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (!rtlpriv->psc.sw_ps_enabled)
621cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
622cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
623cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((rtlpriv->sec.being_setkey) ||
624cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	    (mac->opmode == NL80211_IFTYPE_ADHOC))
625cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
626cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
627cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
628cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5))
629cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
630cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
631cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->link_info.busytraffic)
632cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
633cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
634cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flag);
635cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.rfchange_inprogress) {
636cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
637cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		return;
638cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
639cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	spin_unlock_irqrestore(&rtlpriv->locks.rf_ps_lock, flag);
640cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
641cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
642cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS, false);
643cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
644cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
645cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
646cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		!RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
647cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->intf_ops->enable_aspm(hw);
648cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
649cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
650cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
651cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* here is power save alg, when this beacon is DTIM
652cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * we will set sleep time to dtim_period * n;
653cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * when this beacon is not DTIM, we will set sleep
654cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * time to sleep_intv = rtlpriv->psc.dtim_counter or
655cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * MAX_SW_LPS_SLEEP_INTV(default set to 5) */
656cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
657cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.dtim_counter == 0) {
658cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (hw->conf.ps_dtim_period == 1)
659cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			sleep_intv = hw->conf.ps_dtim_period * 2;
660cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		else
661cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			sleep_intv = hw->conf.ps_dtim_period;
662cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	} else {
663cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		sleep_intv = rtlpriv->psc.dtim_counter;
664cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
665cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
666cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (sleep_intv > MAX_SW_LPS_SLEEP_INTV)
667cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		sleep_intv = MAX_SW_LPS_SLEEP_INTV;
668cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
669cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* this print should always be dtim_conter = 0 &
670cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * sleep  = dtim_period, that meaons, we should
671cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	 * awake before every dtim */
672cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
673cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		 ("dtim_counter:%x will sleep :%d"
674cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		 " beacon_intv\n", rtlpriv->psc.dtim_counter, sleep_intv));
675cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
676cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* we tested that 40ms is enough for sw & hw sw delay */
677cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq,
678cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40));
679cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
680cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
681cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
682cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Livoid rtl_swlps_wq_callback(void *data)
683cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li{
684cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_works *rtlworks = container_of_dwork_rtl(data,
685cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				     struct rtl_works,
686cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li				     ps_work);
687cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct ieee80211_hw *hw = rtlworks->hw;
688cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	struct rtl_priv *rtlpriv = rtl_priv(hw);
689cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	bool ps = false;
690cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
691cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	ps = (hw->conf.flags & IEEE80211_CONF_PS);
692cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
693cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	/* we can sleep after ps null send ok */
694cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	if (rtlpriv->psc.state_inap) {
695cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtl_swlps_rf_sleep(hw);
696cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
697cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (rtlpriv->psc.state && !ps) {
698cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies -
699cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li					rtlpriv->psc.last_action);
700cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		}
701cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
702cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		if (ps)
703cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li			rtlpriv->psc.last_slept = jiffies;
704cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li
705cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.last_action = jiffies;
706cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li		rtlpriv->psc.state = ps;
707cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li	}
708cc7dc0c4ff7c091fb70ff0436f7e3b557e0ac1c3Chaoming_Li}
709