xref: /linux/drivers/net/wireless/realtek/rtlwifi/ps.c (revision 7c51d17c027e52ef6ccc075e31c2eb44c13e3a32)
1f1d2b4d3SLarry Finger /******************************************************************************
2f1d2b4d3SLarry Finger  *
3f1d2b4d3SLarry Finger  * Copyright(c) 2009-2012  Realtek Corporation.
4f1d2b4d3SLarry Finger  *
5f1d2b4d3SLarry Finger  * This program is free software; you can redistribute it and/or modify it
6f1d2b4d3SLarry Finger  * under the terms of version 2 of the GNU General Public License as
7f1d2b4d3SLarry Finger  * published by the Free Software Foundation.
8f1d2b4d3SLarry Finger  *
9f1d2b4d3SLarry Finger  * This program is distributed in the hope that it will be useful, but WITHOUT
10f1d2b4d3SLarry Finger  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11f1d2b4d3SLarry Finger  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12f1d2b4d3SLarry Finger  * more details.
13f1d2b4d3SLarry Finger  *
14f1d2b4d3SLarry Finger  * The full GNU General Public License is included in this distribution in the
15f1d2b4d3SLarry Finger  * file called LICENSE.
16f1d2b4d3SLarry Finger  *
17f1d2b4d3SLarry Finger  * Contact Information:
18f1d2b4d3SLarry Finger  * wlanfae <wlanfae@realtek.com>
19f1d2b4d3SLarry Finger  * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
20f1d2b4d3SLarry Finger  * Hsinchu 300, Taiwan.
21f1d2b4d3SLarry Finger  *
22f1d2b4d3SLarry Finger  * Larry Finger <Larry.Finger@lwfinger.net>
23f1d2b4d3SLarry Finger  *
24f1d2b4d3SLarry Finger  *****************************************************************************/
25f1d2b4d3SLarry Finger 
26f1d2b4d3SLarry Finger #include "wifi.h"
27f1d2b4d3SLarry Finger #include "base.h"
28f1d2b4d3SLarry Finger #include "ps.h"
29f1d2b4d3SLarry Finger #include <linux/export.h>
30f1d2b4d3SLarry Finger #include "btcoexist/rtl_btc.h"
31f1d2b4d3SLarry Finger 
32f1d2b4d3SLarry Finger bool rtl_ps_enable_nic(struct ieee80211_hw *hw)
33f1d2b4d3SLarry Finger {
34f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
35f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
36f1d2b4d3SLarry Finger 	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
378d0d43e3SPing-Ke Shih 	struct rtl_mac *rtlmac = rtl_mac(rtl_priv(hw));
38f1d2b4d3SLarry Finger 
39f1d2b4d3SLarry Finger 	/*<1> reset trx ring */
40f1d2b4d3SLarry Finger 	if (rtlhal->interface == INTF_PCI)
41f1d2b4d3SLarry Finger 		rtlpriv->intf_ops->reset_trx_ring(hw);
42f1d2b4d3SLarry Finger 
43f1d2b4d3SLarry Finger 	if (is_hal_stop(rtlhal))
44f1d2b4d3SLarry Finger 		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
45f1d2b4d3SLarry Finger 			 "Driver is already down!\n");
46f1d2b4d3SLarry Finger 
47f1d2b4d3SLarry Finger 	/*<2> Enable Adapter */
48f1d2b4d3SLarry Finger 	if (rtlpriv->cfg->ops->hw_init(hw))
49f1d2b4d3SLarry Finger 		return false;
508d0d43e3SPing-Ke Shih 	rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RETRY_LIMIT,
518d0d43e3SPing-Ke Shih 			&rtlmac->retry_long);
52f1d2b4d3SLarry Finger 	RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
53f1d2b4d3SLarry Finger 
54f1d2b4d3SLarry Finger 	/*<3> Enable Interrupt */
55f1d2b4d3SLarry Finger 	rtlpriv->cfg->ops->enable_interrupt(hw);
56f1d2b4d3SLarry Finger 
57f1d2b4d3SLarry Finger 	/*<enable timer> */
58*7c51d17cSKees Cook 	rtl_watch_dog_timer_callback(&rtlpriv->works.watchdog_timer);
59f1d2b4d3SLarry Finger 
60f1d2b4d3SLarry Finger 	return true;
61f1d2b4d3SLarry Finger }
62f1d2b4d3SLarry Finger EXPORT_SYMBOL(rtl_ps_enable_nic);
63f1d2b4d3SLarry Finger 
64f1d2b4d3SLarry Finger bool rtl_ps_disable_nic(struct ieee80211_hw *hw)
65f1d2b4d3SLarry Finger {
66f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
67f1d2b4d3SLarry Finger 
68f1d2b4d3SLarry Finger 	/*<1> Stop all timer */
69f1d2b4d3SLarry Finger 	rtl_deinit_deferred_work(hw);
70f1d2b4d3SLarry Finger 
71f1d2b4d3SLarry Finger 	/*<2> Disable Interrupt */
72f1d2b4d3SLarry Finger 	rtlpriv->cfg->ops->disable_interrupt(hw);
73f1d2b4d3SLarry Finger 	tasklet_kill(&rtlpriv->works.irq_tasklet);
74f1d2b4d3SLarry Finger 
75f1d2b4d3SLarry Finger 	/*<3> Disable Adapter */
76f1d2b4d3SLarry Finger 	rtlpriv->cfg->ops->hw_disable(hw);
77f1d2b4d3SLarry Finger 
78f1d2b4d3SLarry Finger 	return true;
79f1d2b4d3SLarry Finger }
80f1d2b4d3SLarry Finger EXPORT_SYMBOL(rtl_ps_disable_nic);
81f1d2b4d3SLarry Finger 
8230462b51SLarry Finger static bool rtl_ps_set_rf_state(struct ieee80211_hw *hw,
83f1d2b4d3SLarry Finger 				enum rf_pwrstate state_toset,
8430462b51SLarry Finger 				u32 changesource)
85f1d2b4d3SLarry Finger {
86f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
87f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
88f1d2b4d3SLarry Finger 	enum rf_pwrstate rtstate;
89f1d2b4d3SLarry Finger 	bool actionallowed = false;
90f1d2b4d3SLarry Finger 	u16 rfwait_cnt = 0;
91f1d2b4d3SLarry Finger 
92f1d2b4d3SLarry Finger 	/*Only one thread can change
93f1d2b4d3SLarry Finger 	 *the RF state at one time, and others
94f1d2b4d3SLarry Finger 	 *should wait to be executed.
95f1d2b4d3SLarry Finger 	 */
96f1d2b4d3SLarry Finger 	while (true) {
97f1d2b4d3SLarry Finger 		spin_lock(&rtlpriv->locks.rf_ps_lock);
98f1d2b4d3SLarry Finger 		if (ppsc->rfchange_inprogress) {
99f1d2b4d3SLarry Finger 			spin_unlock(&rtlpriv->locks.rf_ps_lock);
100f1d2b4d3SLarry Finger 
101f1d2b4d3SLarry Finger 			RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
102f1d2b4d3SLarry Finger 				 "RF Change in progress! Wait to set..state_toset(%d).\n",
103f1d2b4d3SLarry Finger 				  state_toset);
104f1d2b4d3SLarry Finger 
105f1d2b4d3SLarry Finger 			/* Set RF after the previous action is done.  */
106f1d2b4d3SLarry Finger 			while (ppsc->rfchange_inprogress) {
107f1d2b4d3SLarry Finger 				rfwait_cnt++;
108f1d2b4d3SLarry Finger 				mdelay(1);
109f1d2b4d3SLarry Finger 				/*Wait too long, return false to avoid
110f1d2b4d3SLarry Finger 				 *to be stuck here.
111f1d2b4d3SLarry Finger 				 */
112f1d2b4d3SLarry Finger 				if (rfwait_cnt > 100)
113f1d2b4d3SLarry Finger 					return false;
114f1d2b4d3SLarry Finger 			}
115f1d2b4d3SLarry Finger 		} else {
116f1d2b4d3SLarry Finger 			ppsc->rfchange_inprogress = true;
117f1d2b4d3SLarry Finger 			spin_unlock(&rtlpriv->locks.rf_ps_lock);
118f1d2b4d3SLarry Finger 			break;
119f1d2b4d3SLarry Finger 		}
120f1d2b4d3SLarry Finger 	}
121f1d2b4d3SLarry Finger 
122f1d2b4d3SLarry Finger 	rtstate = ppsc->rfpwr_state;
123f1d2b4d3SLarry Finger 
124f1d2b4d3SLarry Finger 	switch (state_toset) {
125f1d2b4d3SLarry Finger 	case ERFON:
126f1d2b4d3SLarry Finger 		ppsc->rfoff_reason &= (~changesource);
127f1d2b4d3SLarry Finger 
128f1d2b4d3SLarry Finger 		if ((changesource == RF_CHANGE_BY_HW) &&
129f1d2b4d3SLarry Finger 		    (ppsc->hwradiooff)) {
130f1d2b4d3SLarry Finger 			ppsc->hwradiooff = false;
131f1d2b4d3SLarry Finger 		}
132f1d2b4d3SLarry Finger 
133f1d2b4d3SLarry Finger 		if (!ppsc->rfoff_reason) {
134f1d2b4d3SLarry Finger 			ppsc->rfoff_reason = 0;
135f1d2b4d3SLarry Finger 			actionallowed = true;
136f1d2b4d3SLarry Finger 		}
137f1d2b4d3SLarry Finger 
138f1d2b4d3SLarry Finger 		break;
139f1d2b4d3SLarry Finger 
140f1d2b4d3SLarry Finger 	case ERFOFF:
141f1d2b4d3SLarry Finger 
142f1d2b4d3SLarry Finger 		if ((changesource == RF_CHANGE_BY_HW) && !ppsc->hwradiooff) {
143f1d2b4d3SLarry Finger 			ppsc->hwradiooff = true;
144f1d2b4d3SLarry Finger 		}
145f1d2b4d3SLarry Finger 
146f1d2b4d3SLarry Finger 		ppsc->rfoff_reason |= changesource;
147f1d2b4d3SLarry Finger 		actionallowed = true;
148f1d2b4d3SLarry Finger 		break;
149f1d2b4d3SLarry Finger 
150f1d2b4d3SLarry Finger 	case ERFSLEEP:
151f1d2b4d3SLarry Finger 		ppsc->rfoff_reason |= changesource;
152f1d2b4d3SLarry Finger 		actionallowed = true;
153f1d2b4d3SLarry Finger 		break;
154f1d2b4d3SLarry Finger 
155f1d2b4d3SLarry Finger 	default:
156b03d968bSLarry Finger 		pr_err("switch case %#x not processed\n", state_toset);
157f1d2b4d3SLarry Finger 		break;
158f1d2b4d3SLarry Finger 	}
159f1d2b4d3SLarry Finger 
160f1d2b4d3SLarry Finger 	if (actionallowed)
161f1d2b4d3SLarry Finger 		rtlpriv->cfg->ops->set_rf_power_state(hw, state_toset);
162f1d2b4d3SLarry Finger 
163f1d2b4d3SLarry Finger 	spin_lock(&rtlpriv->locks.rf_ps_lock);
164f1d2b4d3SLarry Finger 	ppsc->rfchange_inprogress = false;
165f1d2b4d3SLarry Finger 	spin_unlock(&rtlpriv->locks.rf_ps_lock);
166f1d2b4d3SLarry Finger 
167f1d2b4d3SLarry Finger 	return actionallowed;
168f1d2b4d3SLarry Finger }
169f1d2b4d3SLarry Finger 
170f1d2b4d3SLarry Finger static void _rtl_ps_inactive_ps(struct ieee80211_hw *hw)
171f1d2b4d3SLarry Finger {
172f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
173f1d2b4d3SLarry Finger 	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
174f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
175f1d2b4d3SLarry Finger 
176f1d2b4d3SLarry Finger 	ppsc->swrf_processing = true;
177f1d2b4d3SLarry Finger 
178f1d2b4d3SLarry Finger 	if (ppsc->inactive_pwrstate == ERFON &&
179f1d2b4d3SLarry Finger 	    rtlhal->interface == INTF_PCI) {
180f1d2b4d3SLarry Finger 		if ((ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM) &&
181f1d2b4d3SLarry Finger 		    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
182f1d2b4d3SLarry Finger 		    rtlhal->interface == INTF_PCI) {
183f1d2b4d3SLarry Finger 			rtlpriv->intf_ops->disable_aspm(hw);
184f1d2b4d3SLarry Finger 			RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
185f1d2b4d3SLarry Finger 		}
186f1d2b4d3SLarry Finger 	}
187f1d2b4d3SLarry Finger 
188f1d2b4d3SLarry Finger 	rtl_ps_set_rf_state(hw, ppsc->inactive_pwrstate,
18930462b51SLarry Finger 			    RF_CHANGE_BY_IPS);
190f1d2b4d3SLarry Finger 
191f1d2b4d3SLarry Finger 	if (ppsc->inactive_pwrstate == ERFOFF &&
192f1d2b4d3SLarry Finger 	    rtlhal->interface == INTF_PCI) {
193f1d2b4d3SLarry Finger 		if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
194f1d2b4d3SLarry Finger 		    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
195f1d2b4d3SLarry Finger 			rtlpriv->intf_ops->enable_aspm(hw);
196f1d2b4d3SLarry Finger 			RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
197f1d2b4d3SLarry Finger 		}
198f1d2b4d3SLarry Finger 	}
199f1d2b4d3SLarry Finger 
200f1d2b4d3SLarry Finger 	ppsc->swrf_processing = false;
201f1d2b4d3SLarry Finger }
202f1d2b4d3SLarry Finger 
203f1d2b4d3SLarry Finger void rtl_ips_nic_off_wq_callback(void *data)
204f1d2b4d3SLarry Finger {
205f1d2b4d3SLarry Finger 	struct rtl_works *rtlworks =
206f1d2b4d3SLarry Finger 	    container_of_dwork_rtl(data, struct rtl_works, ips_nic_off_wq);
207f1d2b4d3SLarry Finger 	struct ieee80211_hw *hw = rtlworks->hw;
208f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
209f1d2b4d3SLarry Finger 	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
210f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
211f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
212f1d2b4d3SLarry Finger 	enum rf_pwrstate rtstate;
213f1d2b4d3SLarry Finger 
214f1d2b4d3SLarry Finger 	if (mac->opmode != NL80211_IFTYPE_STATION) {
215f1d2b4d3SLarry Finger 		RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
216f1d2b4d3SLarry Finger 			 "not station return\n");
217f1d2b4d3SLarry Finger 		return;
218f1d2b4d3SLarry Finger 	}
219f1d2b4d3SLarry Finger 
220f1d2b4d3SLarry Finger 	if (mac->p2p_in_use)
221f1d2b4d3SLarry Finger 		return;
222f1d2b4d3SLarry Finger 
223f1d2b4d3SLarry Finger 	if (mac->link_state > MAC80211_NOLINK)
224f1d2b4d3SLarry Finger 		return;
225f1d2b4d3SLarry Finger 
226f1d2b4d3SLarry Finger 	if (is_hal_stop(rtlhal))
227f1d2b4d3SLarry Finger 		return;
228f1d2b4d3SLarry Finger 
229f1d2b4d3SLarry Finger 	if (rtlpriv->sec.being_setkey)
230f1d2b4d3SLarry Finger 		return;
231f1d2b4d3SLarry Finger 
232f1d2b4d3SLarry Finger 	if (rtlpriv->cfg->ops->bt_coex_off_before_lps)
233f1d2b4d3SLarry Finger 		rtlpriv->cfg->ops->bt_coex_off_before_lps(hw);
234f1d2b4d3SLarry Finger 
235f1d2b4d3SLarry Finger 	if (ppsc->inactiveps) {
236f1d2b4d3SLarry Finger 		rtstate = ppsc->rfpwr_state;
237f1d2b4d3SLarry Finger 
238f1d2b4d3SLarry Finger 		/*
239f1d2b4d3SLarry Finger 		 *Do not enter IPS in the following conditions:
240f1d2b4d3SLarry Finger 		 *(1) RF is already OFF or Sleep
241f1d2b4d3SLarry Finger 		 *(2) swrf_processing (indicates the IPS is still under going)
242f1d2b4d3SLarry Finger 		 *(3) Connectted (only disconnected can trigger IPS)
243f1d2b4d3SLarry Finger 		 *(4) IBSS (send Beacon)
244f1d2b4d3SLarry Finger 		 *(5) AP mode (send Beacon)
245f1d2b4d3SLarry Finger 		 *(6) monitor mode (rcv packet)
246f1d2b4d3SLarry Finger 		 */
247f1d2b4d3SLarry Finger 
248f1d2b4d3SLarry Finger 		if (rtstate == ERFON &&
249f1d2b4d3SLarry Finger 		    !ppsc->swrf_processing &&
250f1d2b4d3SLarry Finger 		    (mac->link_state == MAC80211_NOLINK) &&
251f1d2b4d3SLarry Finger 		    !mac->act_scanning) {
252f1d2b4d3SLarry Finger 			RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE,
253f1d2b4d3SLarry Finger 				 "IPSEnter(): Turn off RF\n");
254f1d2b4d3SLarry Finger 
255f1d2b4d3SLarry Finger 			ppsc->inactive_pwrstate = ERFOFF;
256f1d2b4d3SLarry Finger 			ppsc->in_powersavemode = true;
257f1d2b4d3SLarry Finger 
258f1d2b4d3SLarry Finger 			/* call before RF off */
259f1d2b4d3SLarry Finger 			if (rtlpriv->cfg->ops->get_btc_status())
260f1d2b4d3SLarry Finger 				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
261f1d2b4d3SLarry Finger 									ppsc->inactive_pwrstate);
262f1d2b4d3SLarry Finger 
263f1d2b4d3SLarry Finger 			/*rtl_pci_reset_trx_ring(hw); */
264f1d2b4d3SLarry Finger 			_rtl_ps_inactive_ps(hw);
265f1d2b4d3SLarry Finger 		}
266f1d2b4d3SLarry Finger 	}
267f1d2b4d3SLarry Finger }
268f1d2b4d3SLarry Finger 
269f1d2b4d3SLarry Finger void rtl_ips_nic_off(struct ieee80211_hw *hw)
270f1d2b4d3SLarry Finger {
271f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
272f1d2b4d3SLarry Finger 
273f1d2b4d3SLarry Finger 	/* because when link with ap, mac80211 will ask us
274f1d2b4d3SLarry Finger 	 * to disable nic quickly after scan before linking,
275f1d2b4d3SLarry Finger 	 * this will cause link failed, so we delay 100ms here
276f1d2b4d3SLarry Finger 	 */
277f1d2b4d3SLarry Finger 	queue_delayed_work(rtlpriv->works.rtl_wq,
278f1d2b4d3SLarry Finger 			   &rtlpriv->works.ips_nic_off_wq, MSECS(100));
279f1d2b4d3SLarry Finger }
280f1d2b4d3SLarry Finger 
281f1d2b4d3SLarry Finger /* NOTICE: any opmode should exc nic_on, or disable without
282f1d2b4d3SLarry Finger  * nic_on may something wrong, like adhoc TP
283f1d2b4d3SLarry Finger  */
284f1d2b4d3SLarry Finger void rtl_ips_nic_on(struct ieee80211_hw *hw)
285f1d2b4d3SLarry Finger {
286f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
287f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
288f1d2b4d3SLarry Finger 	enum rf_pwrstate rtstate;
289f1d2b4d3SLarry Finger 
290f1d2b4d3SLarry Finger 	cancel_delayed_work(&rtlpriv->works.ips_nic_off_wq);
291f1d2b4d3SLarry Finger 
292f1d2b4d3SLarry Finger 	spin_lock(&rtlpriv->locks.ips_lock);
293f1d2b4d3SLarry Finger 	if (ppsc->inactiveps) {
294f1d2b4d3SLarry Finger 		rtstate = ppsc->rfpwr_state;
295f1d2b4d3SLarry Finger 
296f1d2b4d3SLarry Finger 		if (rtstate != ERFON &&
297f1d2b4d3SLarry Finger 		    !ppsc->swrf_processing &&
298f1d2b4d3SLarry Finger 		    ppsc->rfoff_reason <= RF_CHANGE_BY_IPS) {
299f1d2b4d3SLarry Finger 
300f1d2b4d3SLarry Finger 			ppsc->inactive_pwrstate = ERFON;
301f1d2b4d3SLarry Finger 			ppsc->in_powersavemode = false;
302f1d2b4d3SLarry Finger 			_rtl_ps_inactive_ps(hw);
303f1d2b4d3SLarry Finger 			/* call after RF on */
304f1d2b4d3SLarry Finger 			if (rtlpriv->cfg->ops->get_btc_status())
305f1d2b4d3SLarry Finger 				rtlpriv->btcoexist.btc_ops->btc_ips_notify(rtlpriv,
306f1d2b4d3SLarry Finger 									ppsc->inactive_pwrstate);
307f1d2b4d3SLarry Finger 		}
308f1d2b4d3SLarry Finger 	}
309f1d2b4d3SLarry Finger 	spin_unlock(&rtlpriv->locks.ips_lock);
310f1d2b4d3SLarry Finger }
311f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_ips_nic_on);
312f1d2b4d3SLarry Finger 
313f1d2b4d3SLarry Finger /*for FW LPS*/
314f1d2b4d3SLarry Finger 
315f1d2b4d3SLarry Finger /*
316f1d2b4d3SLarry Finger  *Determine if we can set Fw into PS mode
317f1d2b4d3SLarry Finger  *in current condition.Return TRUE if it
318f1d2b4d3SLarry Finger  *can enter PS mode.
319f1d2b4d3SLarry Finger  */
320f1d2b4d3SLarry Finger static bool rtl_get_fwlps_doze(struct ieee80211_hw *hw)
321f1d2b4d3SLarry Finger {
322f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
323f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
324f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
325f1d2b4d3SLarry Finger 	u32 ps_timediff;
326f1d2b4d3SLarry Finger 
327f1d2b4d3SLarry Finger 	ps_timediff = jiffies_to_msecs(jiffies -
328f1d2b4d3SLarry Finger 				       ppsc->last_delaylps_stamp_jiffies);
329f1d2b4d3SLarry Finger 
330f1d2b4d3SLarry Finger 	if (ps_timediff < 2000) {
331f1d2b4d3SLarry Finger 		RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
332f1d2b4d3SLarry Finger 			 "Delay enter Fw LPS for DHCP, ARP, or EAPOL exchanging state\n");
333f1d2b4d3SLarry Finger 		return false;
334f1d2b4d3SLarry Finger 	}
335f1d2b4d3SLarry Finger 
336f1d2b4d3SLarry Finger 	if (mac->link_state != MAC80211_LINKED)
337f1d2b4d3SLarry Finger 		return false;
338f1d2b4d3SLarry Finger 
339f1d2b4d3SLarry Finger 	if (mac->opmode == NL80211_IFTYPE_ADHOC)
340f1d2b4d3SLarry Finger 		return false;
341f1d2b4d3SLarry Finger 
342f1d2b4d3SLarry Finger 	return true;
343f1d2b4d3SLarry Finger }
344f1d2b4d3SLarry Finger 
345f1d2b4d3SLarry Finger /* Change current and default preamble mode.*/
346f1d2b4d3SLarry Finger void rtl_lps_set_psmode(struct ieee80211_hw *hw, u8 rt_psmode)
347f1d2b4d3SLarry Finger {
348f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
349f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
350f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
351f1d2b4d3SLarry Finger 	bool enter_fwlps;
352f1d2b4d3SLarry Finger 
353f1d2b4d3SLarry Finger 	if (mac->opmode == NL80211_IFTYPE_ADHOC)
354f1d2b4d3SLarry Finger 		return;
355f1d2b4d3SLarry Finger 
356f1d2b4d3SLarry Finger 	if (mac->link_state != MAC80211_LINKED)
357f1d2b4d3SLarry Finger 		return;
358f1d2b4d3SLarry Finger 
359135f4fbdSPing-Ke Shih 	if (ppsc->dot11_psmode == rt_psmode && rt_psmode == EACTIVE)
360f1d2b4d3SLarry Finger 		return;
361f1d2b4d3SLarry Finger 
362f1d2b4d3SLarry Finger 	/* Update power save mode configured. */
363f1d2b4d3SLarry Finger 	ppsc->dot11_psmode = rt_psmode;
364f1d2b4d3SLarry Finger 
365f1d2b4d3SLarry Finger 	/*
366f1d2b4d3SLarry Finger 	 *<FW control LPS>
367f1d2b4d3SLarry Finger 	 *1. Enter PS mode
368f1d2b4d3SLarry Finger 	 *   Set RPWM to Fw to turn RF off and send H2C fw_pwrmode
369f1d2b4d3SLarry Finger 	 *   cmd to set Fw into PS mode.
370f1d2b4d3SLarry Finger 	 *2. Leave PS mode
371f1d2b4d3SLarry Finger 	 *   Send H2C fw_pwrmode cmd to Fw to set Fw into Active
372f1d2b4d3SLarry Finger 	 *   mode and set RPWM to turn RF on.
373f1d2b4d3SLarry Finger 	 */
374f1d2b4d3SLarry Finger 
375f1d2b4d3SLarry Finger 	if ((ppsc->fwctrl_lps) && ppsc->report_linked) {
376f1d2b4d3SLarry Finger 		if (ppsc->dot11_psmode == EACTIVE) {
377f1d2b4d3SLarry Finger 			RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
378f1d2b4d3SLarry Finger 				 "FW LPS leave ps_mode:%x\n",
379f1d2b4d3SLarry Finger 				  FW_PS_ACTIVE_MODE);
380f1d2b4d3SLarry Finger 			enter_fwlps = false;
381f1d2b4d3SLarry Finger 			ppsc->pwr_mode = FW_PS_ACTIVE_MODE;
382f1d2b4d3SLarry Finger 			ppsc->smart_ps = 0;
383f1d2b4d3SLarry Finger 			rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_FW_LPS_ACTION,
384f1d2b4d3SLarry Finger 						      (u8 *)(&enter_fwlps));
385f1d2b4d3SLarry Finger 			if (ppsc->p2p_ps_info.opp_ps)
386f1d2b4d3SLarry Finger 				rtl_p2p_ps_cmd(hw , P2P_PS_ENABLE);
387f1d2b4d3SLarry Finger 
388f1d2b4d3SLarry Finger 			if (rtlpriv->cfg->ops->get_btc_status())
389f1d2b4d3SLarry Finger 				rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
390f1d2b4d3SLarry Finger 		} else {
391f1d2b4d3SLarry Finger 			if (rtl_get_fwlps_doze(hw)) {
392f1d2b4d3SLarry Finger 				RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG,
393f1d2b4d3SLarry Finger 					 "FW LPS enter ps_mode:%x\n",
394f1d2b4d3SLarry Finger 					 ppsc->fwctrl_psmode);
395f1d2b4d3SLarry Finger 				if (rtlpriv->cfg->ops->get_btc_status())
396f1d2b4d3SLarry Finger 					rtlpriv->btcoexist.btc_ops->btc_lps_notify(rtlpriv, rt_psmode);
397f1d2b4d3SLarry Finger 				enter_fwlps = true;
398f1d2b4d3SLarry Finger 				ppsc->pwr_mode = ppsc->fwctrl_psmode;
399f1d2b4d3SLarry Finger 				ppsc->smart_ps = 2;
400f1d2b4d3SLarry Finger 				rtlpriv->cfg->ops->set_hw_reg(hw,
401f1d2b4d3SLarry Finger 							HW_VAR_FW_LPS_ACTION,
402f1d2b4d3SLarry Finger 							(u8 *)(&enter_fwlps));
403f1d2b4d3SLarry Finger 
404f1d2b4d3SLarry Finger 			} else {
405f1d2b4d3SLarry Finger 				/* Reset the power save related parameters. */
406f1d2b4d3SLarry Finger 				ppsc->dot11_psmode = EACTIVE;
407f1d2b4d3SLarry Finger 			}
408f1d2b4d3SLarry Finger 		}
409f1d2b4d3SLarry Finger 	}
410f1d2b4d3SLarry Finger }
411f1d2b4d3SLarry Finger 
412ba9f93f8SLarry Finger /* Interrupt safe routine to enter the leisure power save mode.*/
413ba9f93f8SLarry Finger static void rtl_lps_enter_core(struct ieee80211_hw *hw)
414f1d2b4d3SLarry Finger {
415f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
416f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
417f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
418f1d2b4d3SLarry Finger 	unsigned long flag;
419f1d2b4d3SLarry Finger 
420f1d2b4d3SLarry Finger 	if (!ppsc->fwctrl_lps)
421f1d2b4d3SLarry Finger 		return;
422f1d2b4d3SLarry Finger 
423f1d2b4d3SLarry Finger 	if (rtlpriv->sec.being_setkey)
424f1d2b4d3SLarry Finger 		return;
425f1d2b4d3SLarry Finger 
426f1d2b4d3SLarry Finger 	if (rtlpriv->link_info.busytraffic)
427f1d2b4d3SLarry Finger 		return;
428f1d2b4d3SLarry Finger 
429f1d2b4d3SLarry Finger 	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
430f1d2b4d3SLarry Finger 	if (mac->cnt_after_linked < 5)
431f1d2b4d3SLarry Finger 		return;
432f1d2b4d3SLarry Finger 
433f1d2b4d3SLarry Finger 	if (mac->opmode == NL80211_IFTYPE_ADHOC)
434f1d2b4d3SLarry Finger 		return;
435f1d2b4d3SLarry Finger 
436f1d2b4d3SLarry Finger 	if (mac->link_state != MAC80211_LINKED)
437f1d2b4d3SLarry Finger 		return;
438f1d2b4d3SLarry Finger 
439f1d2b4d3SLarry Finger 	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
440f1d2b4d3SLarry Finger 
441135f4fbdSPing-Ke Shih 	/* Don't need to check (ppsc->dot11_psmode == EACTIVE), because
442135f4fbdSPing-Ke Shih 	 * bt_ccoexist may ask to enter lps.
443135f4fbdSPing-Ke Shih 	 * In normal case, this constraint move to rtl_lps_set_psmode().
444135f4fbdSPing-Ke Shih 	 */
445f1d2b4d3SLarry Finger 	RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
446f1d2b4d3SLarry Finger 		 "Enter 802.11 power save mode...\n");
447f1d2b4d3SLarry Finger 	rtl_lps_set_psmode(hw, EAUTOPS);
448f1d2b4d3SLarry Finger 
449f1d2b4d3SLarry Finger 	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
450f1d2b4d3SLarry Finger }
451f1d2b4d3SLarry Finger 
452ba9f93f8SLarry Finger /* Interrupt safe routine to leave the leisure power save mode.*/
453ba9f93f8SLarry Finger static void rtl_lps_leave_core(struct ieee80211_hw *hw)
454f1d2b4d3SLarry Finger {
455f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
456f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
457f1d2b4d3SLarry Finger 	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
458f1d2b4d3SLarry Finger 	unsigned long flag;
459f1d2b4d3SLarry Finger 
460f1d2b4d3SLarry Finger 	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
461f1d2b4d3SLarry Finger 
462f1d2b4d3SLarry Finger 	if (ppsc->fwctrl_lps) {
463f1d2b4d3SLarry Finger 		if (ppsc->dot11_psmode != EACTIVE) {
464f1d2b4d3SLarry Finger 
465f1d2b4d3SLarry Finger 			/*FIX ME */
466f1d2b4d3SLarry Finger 			/*rtlpriv->cfg->ops->enable_interrupt(hw); */
467f1d2b4d3SLarry Finger 
468f1d2b4d3SLarry Finger 			if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
469f1d2b4d3SLarry Finger 			    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM) &&
470f1d2b4d3SLarry Finger 			    rtlhal->interface == INTF_PCI) {
471f1d2b4d3SLarry Finger 				rtlpriv->intf_ops->disable_aspm(hw);
472f1d2b4d3SLarry Finger 				RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
473f1d2b4d3SLarry Finger 			}
474f1d2b4d3SLarry Finger 
475f1d2b4d3SLarry Finger 			RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD,
476f1d2b4d3SLarry Finger 				 "Busy Traffic,Leave 802.11 power save..\n");
477f1d2b4d3SLarry Finger 
478f1d2b4d3SLarry Finger 			rtl_lps_set_psmode(hw, EACTIVE);
479f1d2b4d3SLarry Finger 		}
480f1d2b4d3SLarry Finger 	}
481f1d2b4d3SLarry Finger 	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
482f1d2b4d3SLarry Finger }
483f1d2b4d3SLarry Finger 
484f1d2b4d3SLarry Finger /* For sw LPS*/
485f1d2b4d3SLarry Finger void rtl_swlps_beacon(struct ieee80211_hw *hw, void *data, unsigned int len)
486f1d2b4d3SLarry Finger {
487f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
488f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
489f1d2b4d3SLarry Finger 	struct ieee80211_hdr *hdr = data;
490f1d2b4d3SLarry Finger 	struct ieee80211_tim_ie *tim_ie;
491f1d2b4d3SLarry Finger 	u8 *tim;
492f1d2b4d3SLarry Finger 	u8 tim_len;
493f1d2b4d3SLarry Finger 	bool u_buffed;
494f1d2b4d3SLarry Finger 	bool m_buffed;
495f1d2b4d3SLarry Finger 
496f1d2b4d3SLarry Finger 	if (mac->opmode != NL80211_IFTYPE_STATION)
497f1d2b4d3SLarry Finger 		return;
498f1d2b4d3SLarry Finger 
499f1d2b4d3SLarry Finger 	if (!rtlpriv->psc.swctrl_lps)
500f1d2b4d3SLarry Finger 		return;
501f1d2b4d3SLarry Finger 
502f1d2b4d3SLarry Finger 	if (rtlpriv->mac80211.link_state != MAC80211_LINKED)
503f1d2b4d3SLarry Finger 		return;
504f1d2b4d3SLarry Finger 
505f1d2b4d3SLarry Finger 	if (!rtlpriv->psc.sw_ps_enabled)
506f1d2b4d3SLarry Finger 		return;
507f1d2b4d3SLarry Finger 
508f1d2b4d3SLarry Finger 	if (rtlpriv->psc.fwctrl_lps)
509f1d2b4d3SLarry Finger 		return;
510f1d2b4d3SLarry Finger 
511f1d2b4d3SLarry Finger 	if (likely(!(hw->conf.flags & IEEE80211_CONF_PS)))
512f1d2b4d3SLarry Finger 		return;
513f1d2b4d3SLarry Finger 
514f1d2b4d3SLarry Finger 	/* check if this really is a beacon */
515f1d2b4d3SLarry Finger 	if (!ieee80211_is_beacon(hdr->frame_control))
516f1d2b4d3SLarry Finger 		return;
517f1d2b4d3SLarry Finger 
518f1d2b4d3SLarry Finger 	/* min. beacon length + FCS_LEN */
519f1d2b4d3SLarry Finger 	if (len <= 40 + FCS_LEN)
520f1d2b4d3SLarry Finger 		return;
521f1d2b4d3SLarry Finger 
522f1d2b4d3SLarry Finger 	/* and only beacons from the associated BSSID, please */
523f1d2b4d3SLarry Finger 	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
524f1d2b4d3SLarry Finger 		return;
525f1d2b4d3SLarry Finger 
526f1d2b4d3SLarry Finger 	rtlpriv->psc.last_beacon = jiffies;
527f1d2b4d3SLarry Finger 
528f1d2b4d3SLarry Finger 	tim = rtl_find_ie(data, len - FCS_LEN, WLAN_EID_TIM);
529f1d2b4d3SLarry Finger 	if (!tim)
530f1d2b4d3SLarry Finger 		return;
531f1d2b4d3SLarry Finger 
532f1d2b4d3SLarry Finger 	if (tim[1] < sizeof(*tim_ie))
533f1d2b4d3SLarry Finger 		return;
534f1d2b4d3SLarry Finger 
535f1d2b4d3SLarry Finger 	tim_len = tim[1];
536f1d2b4d3SLarry Finger 	tim_ie = (struct ieee80211_tim_ie *) &tim[2];
537f1d2b4d3SLarry Finger 
538f1d2b4d3SLarry Finger 	if (!WARN_ON_ONCE(!hw->conf.ps_dtim_period))
539f1d2b4d3SLarry Finger 		rtlpriv->psc.dtim_counter = tim_ie->dtim_count;
540f1d2b4d3SLarry Finger 
541f1d2b4d3SLarry Finger 	/* Check whenever the PHY can be turned off again. */
542f1d2b4d3SLarry Finger 
543f1d2b4d3SLarry Finger 	/* 1. What about buffered unicast traffic for our AID? */
544f1d2b4d3SLarry Finger 	u_buffed = ieee80211_check_tim(tim_ie, tim_len,
545f1d2b4d3SLarry Finger 				       rtlpriv->mac80211.assoc_id);
546f1d2b4d3SLarry Finger 
547f1d2b4d3SLarry Finger 	/* 2. Maybe the AP wants to send multicast/broadcast data? */
548f1d2b4d3SLarry Finger 	m_buffed = tim_ie->bitmap_ctrl & 0x01;
549f1d2b4d3SLarry Finger 	rtlpriv->psc.multi_buffered = m_buffed;
550f1d2b4d3SLarry Finger 
551f1d2b4d3SLarry Finger 	/* unicast will process by mac80211 through
552f1d2b4d3SLarry Finger 	 * set ~IEEE80211_CONF_PS, So we just check
553f1d2b4d3SLarry Finger 	 * multicast frames here */
554f1d2b4d3SLarry Finger 	if (!m_buffed) {
555f1d2b4d3SLarry Finger 		/* back to low-power land. and delay is
556f1d2b4d3SLarry Finger 		 * prevent null power save frame tx fail */
557f1d2b4d3SLarry Finger 		queue_delayed_work(rtlpriv->works.rtl_wq,
558f1d2b4d3SLarry Finger 				   &rtlpriv->works.ps_work, MSECS(5));
559f1d2b4d3SLarry Finger 	} else {
560f1d2b4d3SLarry Finger 		RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
561f1d2b4d3SLarry Finger 			 "u_bufferd: %x, m_buffered: %x\n", u_buffed, m_buffed);
562f1d2b4d3SLarry Finger 	}
563f1d2b4d3SLarry Finger }
564f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_swlps_beacon);
565f1d2b4d3SLarry Finger 
566f1d2b4d3SLarry Finger void rtl_swlps_rf_awake(struct ieee80211_hw *hw)
567f1d2b4d3SLarry Finger {
568f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
569f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
570f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
571f1d2b4d3SLarry Finger 	unsigned long flag;
572f1d2b4d3SLarry Finger 
573f1d2b4d3SLarry Finger 	if (!rtlpriv->psc.swctrl_lps)
574f1d2b4d3SLarry Finger 		return;
575f1d2b4d3SLarry Finger 	if (mac->link_state != MAC80211_LINKED)
576f1d2b4d3SLarry Finger 		return;
577f1d2b4d3SLarry Finger 
578f1d2b4d3SLarry Finger 	if (ppsc->reg_rfps_level & RT_RF_LPS_LEVEL_ASPM &&
579f1d2b4d3SLarry Finger 	    RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
580f1d2b4d3SLarry Finger 		rtlpriv->intf_ops->disable_aspm(hw);
581f1d2b4d3SLarry Finger 		RT_CLEAR_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
582f1d2b4d3SLarry Finger 	}
583f1d2b4d3SLarry Finger 
584f1d2b4d3SLarry Finger 	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
58530462b51SLarry Finger 	rtl_ps_set_rf_state(hw, ERFON, RF_CHANGE_BY_PS);
586f1d2b4d3SLarry Finger 	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
587f1d2b4d3SLarry Finger }
588f1d2b4d3SLarry Finger 
589f1d2b4d3SLarry Finger void rtl_swlps_rfon_wq_callback(void *data)
590f1d2b4d3SLarry Finger {
591f1d2b4d3SLarry Finger 	struct rtl_works *rtlworks =
592f1d2b4d3SLarry Finger 	    container_of_dwork_rtl(data, struct rtl_works, ps_rfon_wq);
593f1d2b4d3SLarry Finger 	struct ieee80211_hw *hw = rtlworks->hw;
594f1d2b4d3SLarry Finger 
595f1d2b4d3SLarry Finger 	rtl_swlps_rf_awake(hw);
596f1d2b4d3SLarry Finger }
597f1d2b4d3SLarry Finger 
598f1d2b4d3SLarry Finger void rtl_swlps_rf_sleep(struct ieee80211_hw *hw)
599f1d2b4d3SLarry Finger {
600f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
601f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
602f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
603f1d2b4d3SLarry Finger 	unsigned long flag;
604f1d2b4d3SLarry Finger 	u8 sleep_intv;
605f1d2b4d3SLarry Finger 
606f1d2b4d3SLarry Finger 	if (!rtlpriv->psc.sw_ps_enabled)
607f1d2b4d3SLarry Finger 		return;
608f1d2b4d3SLarry Finger 
609f1d2b4d3SLarry Finger 	if ((rtlpriv->sec.being_setkey) ||
610f1d2b4d3SLarry Finger 	    (mac->opmode == NL80211_IFTYPE_ADHOC))
611f1d2b4d3SLarry Finger 		return;
612f1d2b4d3SLarry Finger 
613f1d2b4d3SLarry Finger 	/*sleep after linked 10s, to let DHCP and 4-way handshake ok enough!! */
614f1d2b4d3SLarry Finger 	if ((mac->link_state != MAC80211_LINKED) || (mac->cnt_after_linked < 5))
615f1d2b4d3SLarry Finger 		return;
616f1d2b4d3SLarry Finger 
617f1d2b4d3SLarry Finger 	if (rtlpriv->link_info.busytraffic)
618f1d2b4d3SLarry Finger 		return;
619f1d2b4d3SLarry Finger 
620f1d2b4d3SLarry Finger 	spin_lock(&rtlpriv->locks.rf_ps_lock);
621f1d2b4d3SLarry Finger 	if (rtlpriv->psc.rfchange_inprogress) {
622f1d2b4d3SLarry Finger 		spin_unlock(&rtlpriv->locks.rf_ps_lock);
623f1d2b4d3SLarry Finger 		return;
624f1d2b4d3SLarry Finger 	}
625f1d2b4d3SLarry Finger 	spin_unlock(&rtlpriv->locks.rf_ps_lock);
626f1d2b4d3SLarry Finger 
627f1d2b4d3SLarry Finger 	spin_lock_irqsave(&rtlpriv->locks.lps_lock, flag);
62830462b51SLarry Finger 	rtl_ps_set_rf_state(hw, ERFSLEEP, RF_CHANGE_BY_PS);
629f1d2b4d3SLarry Finger 	spin_unlock_irqrestore(&rtlpriv->locks.lps_lock, flag);
630f1d2b4d3SLarry Finger 
631f1d2b4d3SLarry Finger 	if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_ASPM &&
632f1d2b4d3SLarry Finger 	    !RT_IN_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM)) {
633f1d2b4d3SLarry Finger 		rtlpriv->intf_ops->enable_aspm(hw);
634f1d2b4d3SLarry Finger 		RT_SET_PS_LEVEL(ppsc, RT_PS_LEVEL_ASPM);
635f1d2b4d3SLarry Finger 	}
636f1d2b4d3SLarry Finger 
637f1d2b4d3SLarry Finger 	/* here is power save alg, when this beacon is DTIM
638f1d2b4d3SLarry Finger 	 * we will set sleep time to dtim_period * n;
639f1d2b4d3SLarry Finger 	 * when this beacon is not DTIM, we will set sleep
640f1d2b4d3SLarry Finger 	 * time to sleep_intv = rtlpriv->psc.dtim_counter or
641f1d2b4d3SLarry Finger 	 * MAX_SW_LPS_SLEEP_INTV(default set to 5) */
642f1d2b4d3SLarry Finger 
643f1d2b4d3SLarry Finger 	if (rtlpriv->psc.dtim_counter == 0) {
644f1d2b4d3SLarry Finger 		if (hw->conf.ps_dtim_period == 1)
645f1d2b4d3SLarry Finger 			sleep_intv = hw->conf.ps_dtim_period * 2;
646f1d2b4d3SLarry Finger 		else
647f1d2b4d3SLarry Finger 			sleep_intv = hw->conf.ps_dtim_period;
648f1d2b4d3SLarry Finger 	} else {
649f1d2b4d3SLarry Finger 		sleep_intv = rtlpriv->psc.dtim_counter;
650f1d2b4d3SLarry Finger 	}
651f1d2b4d3SLarry Finger 
652f1d2b4d3SLarry Finger 	if (sleep_intv > MAX_SW_LPS_SLEEP_INTV)
653f1d2b4d3SLarry Finger 		sleep_intv = MAX_SW_LPS_SLEEP_INTV;
654f1d2b4d3SLarry Finger 
655f1d2b4d3SLarry Finger 	/* this print should always be dtim_conter = 0 &
656f1d2b4d3SLarry Finger 	 * sleep  = dtim_period, that meaons, we should
657f1d2b4d3SLarry Finger 	 * awake before every dtim */
658f1d2b4d3SLarry Finger 	RT_TRACE(rtlpriv, COMP_POWER, DBG_DMESG,
659f1d2b4d3SLarry Finger 		 "dtim_counter:%x will sleep :%d beacon_intv\n",
660f1d2b4d3SLarry Finger 		  rtlpriv->psc.dtim_counter, sleep_intv);
661f1d2b4d3SLarry Finger 
662f1d2b4d3SLarry Finger 	/* we tested that 40ms is enough for sw & hw sw delay */
663f1d2b4d3SLarry Finger 	queue_delayed_work(rtlpriv->works.rtl_wq, &rtlpriv->works.ps_rfon_wq,
664f1d2b4d3SLarry Finger 			MSECS(sleep_intv * mac->vif->bss_conf.beacon_int - 40));
665f1d2b4d3SLarry Finger }
666f1d2b4d3SLarry Finger 
667f1d2b4d3SLarry Finger void rtl_lps_change_work_callback(struct work_struct *work)
668f1d2b4d3SLarry Finger {
669f1d2b4d3SLarry Finger 	struct rtl_works *rtlworks =
670f1d2b4d3SLarry Finger 	    container_of(work, struct rtl_works, lps_change_work);
671f1d2b4d3SLarry Finger 	struct ieee80211_hw *hw = rtlworks->hw;
672f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
673f1d2b4d3SLarry Finger 
674f1d2b4d3SLarry Finger 	if (rtlpriv->enter_ps)
675ba9f93f8SLarry Finger 		rtl_lps_enter_core(hw);
676f1d2b4d3SLarry Finger 	else
677ba9f93f8SLarry Finger 		rtl_lps_leave_core(hw);
678f1d2b4d3SLarry Finger }
679f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_change_work_callback);
680f1d2b4d3SLarry Finger 
681ba9f93f8SLarry Finger void rtl_lps_enter(struct ieee80211_hw *hw)
682ba9f93f8SLarry Finger {
683ba9f93f8SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
684ba9f93f8SLarry Finger 
685ba9f93f8SLarry Finger 	if (!in_interrupt())
686ba9f93f8SLarry Finger 		return rtl_lps_enter_core(hw);
687ba9f93f8SLarry Finger 	rtlpriv->enter_ps = true;
688ba9f93f8SLarry Finger 	schedule_work(&rtlpriv->works.lps_change_work);
689ba9f93f8SLarry Finger }
690ba9f93f8SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_enter);
691ba9f93f8SLarry Finger 
692ba9f93f8SLarry Finger void rtl_lps_leave(struct ieee80211_hw *hw)
693ba9f93f8SLarry Finger {
694ba9f93f8SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
695ba9f93f8SLarry Finger 
696ba9f93f8SLarry Finger 	if (!in_interrupt())
697ba9f93f8SLarry Finger 		return rtl_lps_leave_core(hw);
698ba9f93f8SLarry Finger 	rtlpriv->enter_ps = false;
699ba9f93f8SLarry Finger 	schedule_work(&rtlpriv->works.lps_change_work);
700ba9f93f8SLarry Finger }
701ba9f93f8SLarry Finger EXPORT_SYMBOL_GPL(rtl_lps_leave);
702ba9f93f8SLarry Finger 
703f1d2b4d3SLarry Finger void rtl_swlps_wq_callback(void *data)
704f1d2b4d3SLarry Finger {
705f1d2b4d3SLarry Finger 	struct rtl_works *rtlworks = container_of_dwork_rtl(data,
706f1d2b4d3SLarry Finger 				     struct rtl_works,
707f1d2b4d3SLarry Finger 				     ps_work);
708f1d2b4d3SLarry Finger 	struct ieee80211_hw *hw = rtlworks->hw;
709f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
710f1d2b4d3SLarry Finger 	bool ps = false;
711f1d2b4d3SLarry Finger 
712f1d2b4d3SLarry Finger 	ps = (hw->conf.flags & IEEE80211_CONF_PS);
713f1d2b4d3SLarry Finger 
714f1d2b4d3SLarry Finger 	/* we can sleep after ps null send ok */
715f1d2b4d3SLarry Finger 	if (rtlpriv->psc.state_inap) {
716f1d2b4d3SLarry Finger 		rtl_swlps_rf_sleep(hw);
717f1d2b4d3SLarry Finger 
718f1d2b4d3SLarry Finger 		if (rtlpriv->psc.state && !ps) {
719f1d2b4d3SLarry Finger 			rtlpriv->psc.sleep_ms = jiffies_to_msecs(jiffies -
720f1d2b4d3SLarry Finger 						 rtlpriv->psc.last_action);
721f1d2b4d3SLarry Finger 		}
722f1d2b4d3SLarry Finger 
723f1d2b4d3SLarry Finger 		if (ps)
724f1d2b4d3SLarry Finger 			rtlpriv->psc.last_slept = jiffies;
725f1d2b4d3SLarry Finger 
726f1d2b4d3SLarry Finger 		rtlpriv->psc.last_action = jiffies;
727f1d2b4d3SLarry Finger 		rtlpriv->psc.state = ps;
728f1d2b4d3SLarry Finger 	}
729f1d2b4d3SLarry Finger }
730f1d2b4d3SLarry Finger 
731f1d2b4d3SLarry Finger static void rtl_p2p_noa_ie(struct ieee80211_hw *hw, void *data,
732f1d2b4d3SLarry Finger 			   unsigned int len)
733f1d2b4d3SLarry Finger {
734f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
735f1d2b4d3SLarry Finger 	struct ieee80211_mgmt *mgmt = data;
736f1d2b4d3SLarry Finger 	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
737f1d2b4d3SLarry Finger 	u8 *pos, *end, *ie;
738f1d2b4d3SLarry Finger 	u16 noa_len;
739f1d2b4d3SLarry Finger 	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
740f1d2b4d3SLarry Finger 	u8 noa_num, index , i, noa_index = 0;
741f1d2b4d3SLarry Finger 	bool find_p2p_ie = false , find_p2p_ps_ie = false;
742f1d2b4d3SLarry Finger 	pos = (u8 *)mgmt->u.beacon.variable;
743f1d2b4d3SLarry Finger 	end = data + len;
744f1d2b4d3SLarry Finger 	ie = NULL;
745f1d2b4d3SLarry Finger 
746f1d2b4d3SLarry Finger 	while (pos + 1 < end) {
747f1d2b4d3SLarry Finger 		if (pos + 2 + pos[1] > end)
748f1d2b4d3SLarry Finger 			return;
749f1d2b4d3SLarry Finger 
750f1d2b4d3SLarry Finger 		if (pos[0] == 221 && pos[1] > 4) {
751f1d2b4d3SLarry Finger 			if (memcmp(&pos[2], p2p_oui_ie_type, 4) == 0) {
752f1d2b4d3SLarry Finger 				ie = pos + 2+4;
753f1d2b4d3SLarry Finger 				break;
754f1d2b4d3SLarry Finger 			}
755f1d2b4d3SLarry Finger 		}
756f1d2b4d3SLarry Finger 		pos += 2 + pos[1];
757f1d2b4d3SLarry Finger 	}
758f1d2b4d3SLarry Finger 
759f1d2b4d3SLarry Finger 	if (ie == NULL)
760f1d2b4d3SLarry Finger 		return;
761f1d2b4d3SLarry Finger 	find_p2p_ie = true;
762f1d2b4d3SLarry Finger 	/*to find noa ie*/
763f1d2b4d3SLarry Finger 	while (ie + 1 < end) {
764f1d2b4d3SLarry Finger 		noa_len = READEF2BYTE((__le16 *)&ie[1]);
765f1d2b4d3SLarry Finger 		if (ie + 3 + ie[1] > end)
766f1d2b4d3SLarry Finger 			return;
767f1d2b4d3SLarry Finger 
768f1d2b4d3SLarry Finger 		if (ie[0] == 12) {
769f1d2b4d3SLarry Finger 			find_p2p_ps_ie = true;
770f1d2b4d3SLarry Finger 			if ((noa_len - 2) % 13 != 0) {
771f1d2b4d3SLarry Finger 				RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD,
772f1d2b4d3SLarry Finger 					 "P2P notice of absence: invalid length.%d\n",
773f1d2b4d3SLarry Finger 					 noa_len);
774f1d2b4d3SLarry Finger 				return;
775f1d2b4d3SLarry Finger 			} else {
776f1d2b4d3SLarry Finger 				noa_num = (noa_len - 2) / 13;
777f1d2b4d3SLarry Finger 			}
778f1d2b4d3SLarry Finger 			noa_index = ie[3];
779f1d2b4d3SLarry Finger 			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
780f1d2b4d3SLarry Finger 			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
781f1d2b4d3SLarry Finger 				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
782f1d2b4d3SLarry Finger 					 "update NOA ie.\n");
783f1d2b4d3SLarry Finger 				p2pinfo->noa_index = noa_index;
784f1d2b4d3SLarry Finger 				p2pinfo->opp_ps = (ie[4] >> 7);
785f1d2b4d3SLarry Finger 				p2pinfo->ctwindow = ie[4] & 0x7F;
786f1d2b4d3SLarry Finger 				p2pinfo->noa_num = noa_num;
787f1d2b4d3SLarry Finger 				index = 5;
788f1d2b4d3SLarry Finger 				for (i = 0; i < noa_num; i++) {
789f1d2b4d3SLarry Finger 					p2pinfo->noa_count_type[i] =
790f1d2b4d3SLarry Finger 							READEF1BYTE(ie+index);
791f1d2b4d3SLarry Finger 					index += 1;
792f1d2b4d3SLarry Finger 					p2pinfo->noa_duration[i] =
793f1d2b4d3SLarry Finger 						 READEF4BYTE((__le32 *)ie+index);
794f1d2b4d3SLarry Finger 					index += 4;
795f1d2b4d3SLarry Finger 					p2pinfo->noa_interval[i] =
796f1d2b4d3SLarry Finger 						 READEF4BYTE((__le32 *)ie+index);
797f1d2b4d3SLarry Finger 					index += 4;
798f1d2b4d3SLarry Finger 					p2pinfo->noa_start_time[i] =
799f1d2b4d3SLarry Finger 						 READEF4BYTE((__le32 *)ie+index);
800f1d2b4d3SLarry Finger 					index += 4;
801f1d2b4d3SLarry Finger 				}
802f1d2b4d3SLarry Finger 
803f1d2b4d3SLarry Finger 				if (p2pinfo->opp_ps == 1) {
804f1d2b4d3SLarry Finger 					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
805f1d2b4d3SLarry Finger 					/* Driver should wait LPS entering
806f1d2b4d3SLarry Finger 					 * CTWindow
807f1d2b4d3SLarry Finger 					 */
808f1d2b4d3SLarry Finger 					if (rtlpriv->psc.fw_current_inpsmode)
809f1d2b4d3SLarry Finger 						rtl_p2p_ps_cmd(hw,
810f1d2b4d3SLarry Finger 							       P2P_PS_ENABLE);
811f1d2b4d3SLarry Finger 				} else if (p2pinfo->noa_num > 0) {
812f1d2b4d3SLarry Finger 					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
813f1d2b4d3SLarry Finger 					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
814f1d2b4d3SLarry Finger 				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
815f1d2b4d3SLarry Finger 					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
816f1d2b4d3SLarry Finger 				}
817f1d2b4d3SLarry Finger 			}
818f1d2b4d3SLarry Finger 			break;
819f1d2b4d3SLarry Finger 		}
820f1d2b4d3SLarry Finger 		ie += 3 + noa_len;
821f1d2b4d3SLarry Finger 	}
822f1d2b4d3SLarry Finger 
823f1d2b4d3SLarry Finger 	if (find_p2p_ie == true) {
824f1d2b4d3SLarry Finger 		if ((p2pinfo->p2p_ps_mode > P2P_PS_NONE) &&
825f1d2b4d3SLarry Finger 		    (find_p2p_ps_ie == false))
826f1d2b4d3SLarry Finger 			rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
827f1d2b4d3SLarry Finger 	}
828f1d2b4d3SLarry Finger }
829f1d2b4d3SLarry Finger 
830f1d2b4d3SLarry Finger static void rtl_p2p_action_ie(struct ieee80211_hw *hw, void *data,
831f1d2b4d3SLarry Finger 			      unsigned int len)
832f1d2b4d3SLarry Finger {
833f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
834f1d2b4d3SLarry Finger 	struct ieee80211_mgmt *mgmt = data;
835f1d2b4d3SLarry Finger 	struct rtl_p2p_ps_info *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
836f1d2b4d3SLarry Finger 	u8 noa_num, index , i , noa_index = 0;
837f1d2b4d3SLarry Finger 	u8 *pos, *end, *ie;
838f1d2b4d3SLarry Finger 	u16 noa_len;
839f1d2b4d3SLarry Finger 	static u8 p2p_oui_ie_type[4] = {0x50, 0x6f, 0x9a, 0x09};
840f1d2b4d3SLarry Finger 
841f1d2b4d3SLarry Finger 	pos = (u8 *)&mgmt->u.action.category;
842f1d2b4d3SLarry Finger 	end = data + len;
843f1d2b4d3SLarry Finger 	ie = NULL;
844f1d2b4d3SLarry Finger 
845f1d2b4d3SLarry Finger 	if (pos[0] == 0x7f) {
846f1d2b4d3SLarry Finger 		if (memcmp(&pos[1], p2p_oui_ie_type, 4) == 0)
847f1d2b4d3SLarry Finger 			ie = pos + 3+4;
848f1d2b4d3SLarry Finger 	}
849f1d2b4d3SLarry Finger 
850f1d2b4d3SLarry Finger 	if (ie == NULL)
851f1d2b4d3SLarry Finger 		return;
852f1d2b4d3SLarry Finger 
853f1d2b4d3SLarry Finger 	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "action frame find P2P IE.\n");
854f1d2b4d3SLarry Finger 	/*to find noa ie*/
855f1d2b4d3SLarry Finger 	while (ie + 1 < end) {
856f1d2b4d3SLarry Finger 		noa_len = READEF2BYTE((__le16 *)&ie[1]);
857f1d2b4d3SLarry Finger 		if (ie + 3 + ie[1] > end)
858f1d2b4d3SLarry Finger 			return;
859f1d2b4d3SLarry Finger 
860f1d2b4d3SLarry Finger 		if (ie[0] == 12) {
861f1d2b4d3SLarry Finger 			RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "find NOA IE.\n");
862f1d2b4d3SLarry Finger 			RT_PRINT_DATA(rtlpriv, COMP_FW, DBG_LOUD, "noa ie ",
863f1d2b4d3SLarry Finger 				      ie, noa_len);
864f1d2b4d3SLarry Finger 			if ((noa_len - 2) % 13 != 0) {
865f1d2b4d3SLarry Finger 				RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
866f1d2b4d3SLarry Finger 					 "P2P notice of absence: invalid length.%d\n",
867f1d2b4d3SLarry Finger 					 noa_len);
868f1d2b4d3SLarry Finger 				return;
869f1d2b4d3SLarry Finger 			} else {
870f1d2b4d3SLarry Finger 				noa_num = (noa_len - 2) / 13;
871f1d2b4d3SLarry Finger 			}
872f1d2b4d3SLarry Finger 			noa_index = ie[3];
873f1d2b4d3SLarry Finger 			if (rtlpriv->psc.p2p_ps_info.p2p_ps_mode ==
874f1d2b4d3SLarry Finger 			    P2P_PS_NONE || noa_index != p2pinfo->noa_index) {
875f1d2b4d3SLarry Finger 				p2pinfo->noa_index = noa_index;
876f1d2b4d3SLarry Finger 				p2pinfo->opp_ps = (ie[4] >> 7);
877f1d2b4d3SLarry Finger 				p2pinfo->ctwindow = ie[4] & 0x7F;
878f1d2b4d3SLarry Finger 				p2pinfo->noa_num = noa_num;
879f1d2b4d3SLarry Finger 				index = 5;
880f1d2b4d3SLarry Finger 				for (i = 0; i < noa_num; i++) {
881f1d2b4d3SLarry Finger 					p2pinfo->noa_count_type[i] =
882f1d2b4d3SLarry Finger 							READEF1BYTE(ie+index);
883f1d2b4d3SLarry Finger 					index += 1;
884f1d2b4d3SLarry Finger 					p2pinfo->noa_duration[i] =
885f1d2b4d3SLarry Finger 							 READEF4BYTE((__le32 *)ie+index);
886f1d2b4d3SLarry Finger 					index += 4;
887f1d2b4d3SLarry Finger 					p2pinfo->noa_interval[i] =
888f1d2b4d3SLarry Finger 							 READEF4BYTE((__le32 *)ie+index);
889f1d2b4d3SLarry Finger 					index += 4;
890f1d2b4d3SLarry Finger 					p2pinfo->noa_start_time[i] =
891f1d2b4d3SLarry Finger 							 READEF4BYTE((__le32 *)ie+index);
892f1d2b4d3SLarry Finger 					index += 4;
893f1d2b4d3SLarry Finger 				}
894f1d2b4d3SLarry Finger 
895f1d2b4d3SLarry Finger 				if (p2pinfo->opp_ps == 1) {
896f1d2b4d3SLarry Finger 					p2pinfo->p2p_ps_mode = P2P_PS_CTWINDOW;
897f1d2b4d3SLarry Finger 					/* Driver should wait LPS entering
898f1d2b4d3SLarry Finger 					 * CTWindow
899f1d2b4d3SLarry Finger 					 */
900f1d2b4d3SLarry Finger 					if (rtlpriv->psc.fw_current_inpsmode)
901f1d2b4d3SLarry Finger 						rtl_p2p_ps_cmd(hw,
902f1d2b4d3SLarry Finger 							       P2P_PS_ENABLE);
903f1d2b4d3SLarry Finger 				} else if (p2pinfo->noa_num > 0) {
904f1d2b4d3SLarry Finger 					p2pinfo->p2p_ps_mode = P2P_PS_NOA;
905f1d2b4d3SLarry Finger 					rtl_p2p_ps_cmd(hw, P2P_PS_ENABLE);
906f1d2b4d3SLarry Finger 				} else if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
907f1d2b4d3SLarry Finger 					rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE);
908f1d2b4d3SLarry Finger 				}
909f1d2b4d3SLarry Finger 			}
910f1d2b4d3SLarry Finger 			break;
911f1d2b4d3SLarry Finger 		}
912f1d2b4d3SLarry Finger 		ie += 3 + noa_len;
913f1d2b4d3SLarry Finger 	}
914f1d2b4d3SLarry Finger }
915f1d2b4d3SLarry Finger 
916f1d2b4d3SLarry Finger void rtl_p2p_ps_cmd(struct ieee80211_hw *hw , u8 p2p_ps_state)
917f1d2b4d3SLarry Finger {
918f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
919f1d2b4d3SLarry Finger 	struct rtl_ps_ctl *rtlps = rtl_psc(rtl_priv(hw));
920f1d2b4d3SLarry Finger 	struct rtl_p2p_ps_info  *p2pinfo = &(rtlpriv->psc.p2p_ps_info);
921f1d2b4d3SLarry Finger 
922f1d2b4d3SLarry Finger 	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, " p2p state %x\n" , p2p_ps_state);
923f1d2b4d3SLarry Finger 	switch (p2p_ps_state) {
924f1d2b4d3SLarry Finger 	case P2P_PS_DISABLE:
925f1d2b4d3SLarry Finger 		p2pinfo->p2p_ps_state = p2p_ps_state;
926f1d2b4d3SLarry Finger 		rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
927f1d2b4d3SLarry Finger 					      &p2p_ps_state);
928f1d2b4d3SLarry Finger 		p2pinfo->noa_index = 0;
929f1d2b4d3SLarry Finger 		p2pinfo->ctwindow = 0;
930f1d2b4d3SLarry Finger 		p2pinfo->opp_ps = 0;
931f1d2b4d3SLarry Finger 		p2pinfo->noa_num = 0;
932f1d2b4d3SLarry Finger 		p2pinfo->p2p_ps_mode = P2P_PS_NONE;
933f1d2b4d3SLarry Finger 		if (rtlps->fw_current_inpsmode) {
934f1d2b4d3SLarry Finger 			if (rtlps->smart_ps == 0) {
935f1d2b4d3SLarry Finger 				rtlps->smart_ps = 2;
936f1d2b4d3SLarry Finger 				rtlpriv->cfg->ops->set_hw_reg(hw,
937f1d2b4d3SLarry Finger 					 HW_VAR_H2C_FW_PWRMODE,
938f1d2b4d3SLarry Finger 					 &rtlps->pwr_mode);
939f1d2b4d3SLarry Finger 			}
940f1d2b4d3SLarry Finger 
941f1d2b4d3SLarry Finger 		}
942f1d2b4d3SLarry Finger 		break;
943f1d2b4d3SLarry Finger 	case P2P_PS_ENABLE:
944f1d2b4d3SLarry Finger 		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
945f1d2b4d3SLarry Finger 			p2pinfo->p2p_ps_state = p2p_ps_state;
946f1d2b4d3SLarry Finger 
947f1d2b4d3SLarry Finger 			if (p2pinfo->ctwindow > 0) {
948f1d2b4d3SLarry Finger 				if (rtlps->smart_ps != 0) {
949f1d2b4d3SLarry Finger 					rtlps->smart_ps = 0;
950f1d2b4d3SLarry Finger 					rtlpriv->cfg->ops->set_hw_reg(hw,
951f1d2b4d3SLarry Finger 						 HW_VAR_H2C_FW_PWRMODE,
952f1d2b4d3SLarry Finger 						 &rtlps->pwr_mode);
953f1d2b4d3SLarry Finger 				}
954f1d2b4d3SLarry Finger 			}
955f1d2b4d3SLarry Finger 			rtlpriv->cfg->ops->set_hw_reg(hw,
956f1d2b4d3SLarry Finger 				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
957f1d2b4d3SLarry Finger 				 &p2p_ps_state);
958f1d2b4d3SLarry Finger 
959f1d2b4d3SLarry Finger 		}
960f1d2b4d3SLarry Finger 		break;
961f1d2b4d3SLarry Finger 	case P2P_PS_SCAN:
962f1d2b4d3SLarry Finger 	case P2P_PS_SCAN_DONE:
963f1d2b4d3SLarry Finger 	case P2P_PS_ALLSTASLEEP:
964f1d2b4d3SLarry Finger 		if (p2pinfo->p2p_ps_mode > P2P_PS_NONE) {
965f1d2b4d3SLarry Finger 			p2pinfo->p2p_ps_state = p2p_ps_state;
966f1d2b4d3SLarry Finger 			rtlpriv->cfg->ops->set_hw_reg(hw,
967f1d2b4d3SLarry Finger 				 HW_VAR_H2C_FW_P2P_PS_OFFLOAD,
968f1d2b4d3SLarry Finger 				 &p2p_ps_state);
969f1d2b4d3SLarry Finger 		}
970f1d2b4d3SLarry Finger 		break;
971f1d2b4d3SLarry Finger 	default:
972f1d2b4d3SLarry Finger 		break;
973f1d2b4d3SLarry Finger 	}
974f1d2b4d3SLarry Finger 	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
975f1d2b4d3SLarry Finger 		 "ctwindow %x oppps %x\n",
976f1d2b4d3SLarry Finger 		 p2pinfo->ctwindow , p2pinfo->opp_ps);
977f1d2b4d3SLarry Finger 	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD,
978f1d2b4d3SLarry Finger 		 "count %x duration %x index %x interval %x start time %x noa num %x\n",
979f1d2b4d3SLarry Finger 		 p2pinfo->noa_count_type[0],
980f1d2b4d3SLarry Finger 		 p2pinfo->noa_duration[0],
981f1d2b4d3SLarry Finger 		 p2pinfo->noa_index,
982f1d2b4d3SLarry Finger 		 p2pinfo->noa_interval[0],
983f1d2b4d3SLarry Finger 		 p2pinfo->noa_start_time[0],
984f1d2b4d3SLarry Finger 		 p2pinfo->noa_num);
985f1d2b4d3SLarry Finger 	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "end\n");
986f1d2b4d3SLarry Finger }
987f1d2b4d3SLarry Finger 
988f1d2b4d3SLarry Finger void rtl_p2p_info(struct ieee80211_hw *hw, void *data, unsigned int len)
989f1d2b4d3SLarry Finger {
990f1d2b4d3SLarry Finger 	struct rtl_priv *rtlpriv = rtl_priv(hw);
991f1d2b4d3SLarry Finger 	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
992f1d2b4d3SLarry Finger 	struct ieee80211_hdr *hdr = data;
993f1d2b4d3SLarry Finger 
994f1d2b4d3SLarry Finger 	if (!mac->p2p)
995f1d2b4d3SLarry Finger 		return;
996f1d2b4d3SLarry Finger 	if (mac->link_state != MAC80211_LINKED)
997f1d2b4d3SLarry Finger 		return;
998f1d2b4d3SLarry Finger 	/* min. beacon length + FCS_LEN */
999f1d2b4d3SLarry Finger 	if (len <= 40 + FCS_LEN)
1000f1d2b4d3SLarry Finger 		return;
1001f1d2b4d3SLarry Finger 
1002f1d2b4d3SLarry Finger 	/* and only beacons from the associated BSSID, please */
1003f1d2b4d3SLarry Finger 	if (!ether_addr_equal_64bits(hdr->addr3, rtlpriv->mac80211.bssid))
1004f1d2b4d3SLarry Finger 		return;
1005f1d2b4d3SLarry Finger 
1006f1d2b4d3SLarry Finger 	/* check if this really is a beacon */
1007f1d2b4d3SLarry Finger 	if (!(ieee80211_is_beacon(hdr->frame_control) ||
1008f1d2b4d3SLarry Finger 	      ieee80211_is_probe_resp(hdr->frame_control) ||
1009f1d2b4d3SLarry Finger 	      ieee80211_is_action(hdr->frame_control)))
1010f1d2b4d3SLarry Finger 		return;
1011f1d2b4d3SLarry Finger 
1012f1d2b4d3SLarry Finger 	if (ieee80211_is_action(hdr->frame_control))
1013f1d2b4d3SLarry Finger 		rtl_p2p_action_ie(hw , data , len - FCS_LEN);
1014f1d2b4d3SLarry Finger 	else
1015f1d2b4d3SLarry Finger 		rtl_p2p_noa_ie(hw , data , len - FCS_LEN);
1016f1d2b4d3SLarry Finger }
1017f1d2b4d3SLarry Finger EXPORT_SYMBOL_GPL(rtl_p2p_info);
1018