xref: /openbmc/linux/drivers/staging/vt6656/power.c (revision 2bac6f98)
192b96797SForest Bond /*
292b96797SForest Bond  * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
392b96797SForest Bond  * All rights reserved.
492b96797SForest Bond  *
592b96797SForest Bond  * This program is free software; you can redistribute it and/or modify
692b96797SForest Bond  * it under the terms of the GNU General Public License as published by
792b96797SForest Bond  * the Free Software Foundation; either version 2 of the License, or
892b96797SForest Bond  * (at your option) any later version.
992b96797SForest Bond  *
1092b96797SForest Bond  * This program is distributed in the hope that it will be useful,
1192b96797SForest Bond  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1292b96797SForest Bond  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1392b96797SForest Bond  * GNU General Public License for more details.
1492b96797SForest Bond  *
1592b96797SForest Bond  * You should have received a copy of the GNU General Public License along
1692b96797SForest Bond  * with this program; if not, write to the Free Software Foundation, Inc.,
1792b96797SForest Bond  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1892b96797SForest Bond  *
1992b96797SForest Bond  *
2092b96797SForest Bond  * File: power.c
2192b96797SForest Bond  *
220d743951SUwe Kleine-König  * Purpose: Handles 802.11 power management functions
2392b96797SForest Bond  *
2492b96797SForest Bond  * Author: Lyndon Chen
2592b96797SForest Bond  *
2692b96797SForest Bond  * Date: July 17, 2002
2792b96797SForest Bond  *
2892b96797SForest Bond  * Functions:
2992b96797SForest Bond  *      PSvEnablePowerSaving - Enable Power Saving Mode
3092b96797SForest Bond  *      PSvDiasblePowerSaving - Disable Power Saving Mode
3192b96797SForest Bond  *      PSbConsiderPowerDown - Decide if we can Power Down
3292b96797SForest Bond  *      PSvSendPSPOLL - Send PS-POLL packet
3392b96797SForest Bond  *      PSbSendNullPacket - Send Null packet
3492b96797SForest Bond  *      PSbIsNextTBTTWakeUp - Decide if we need to wake up at next Beacon
3592b96797SForest Bond  *
3692b96797SForest Bond  * Revision History:
3792b96797SForest Bond  *
3892b96797SForest Bond  */
3992b96797SForest Bond 
4092b96797SForest Bond #include "mac.h"
4192b96797SForest Bond #include "device.h"
4292b96797SForest Bond #include "wmgr.h"
4392b96797SForest Bond #include "power.h"
4492b96797SForest Bond #include "wcmd.h"
4592b96797SForest Bond #include "rxtx.h"
4692b96797SForest Bond #include "card.h"
4762c8526dSMalcolm Priestley #include "usbpipe.h"
4892b96797SForest Bond 
4992b96797SForest Bond static int msglevel = MSG_LEVEL_INFO;
5092b96797SForest Bond 
517404eab2SPhilip Worrall /*
5292b96797SForest Bond  *
5392b96797SForest Bond  * Routine Description:
5492b96797SForest Bond  * Enable hw power saving functions
5592b96797SForest Bond  *
5692b96797SForest Bond  * Return Value:
5792b96797SForest Bond  *    None.
5892b96797SForest Bond  *
597404eab2SPhilip Worrall  */
6092b96797SForest Bond 
612bac6f98SMalcolm Priestley void PSvEnablePowerSaving(struct vnt_private *priv, u16 listen_interval)
6292b96797SForest Bond {
632bac6f98SMalcolm Priestley 	struct vnt_manager *mgmt = &priv->vnt_mgmt;
642bac6f98SMalcolm Priestley 	u16 aid = mgmt->wCurrAID | BIT14 | BIT15;
6592b96797SForest Bond 
664846cbc1SAlejandro Emanuel Paredes 	/* set period of power up before TBTT */
672bac6f98SMalcolm Priestley 	vnt_mac_write_word(priv, MAC_REG_PWBT, C_PWBT);
6892b96797SForest Bond 
692bac6f98SMalcolm Priestley 	if (priv->op_mode != NL80211_IFTYPE_ADHOC) {
704846cbc1SAlejandro Emanuel Paredes 		/* set AID */
712bac6f98SMalcolm Priestley 		vnt_mac_write_word(priv, MAC_REG_AIDATIM, aid);
7292b96797SForest Bond 	}
7392b96797SForest Bond 
7464052b78SAybuke Ozdemir 	/* Warren:06-18-2004,the sequence must follow
7564052b78SAybuke Ozdemir 	 * PSEN->AUTOSLEEP->GO2DOZE
7664052b78SAybuke Ozdemir 	 */
777404eab2SPhilip Worrall 	/* enable power saving hw function */
782bac6f98SMalcolm Priestley 	vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_PSEN);
79f9cfbe94SPhilip Worrall 
807404eab2SPhilip Worrall 	/* Set AutoSleep */
812bac6f98SMalcolm Priestley 	vnt_mac_reg_bits_on(priv, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
8292b96797SForest Bond 
8364052b78SAybuke Ozdemir 	/* Warren:MUST turn on this once before turn on AUTOSLEEP ,or the
8464052b78SAybuke Ozdemir 	 * AUTOSLEEP doesn't work
8564052b78SAybuke Ozdemir 	 */
862bac6f98SMalcolm Priestley 	vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_GO2DOZE);
8792b96797SForest Bond 
882bac6f98SMalcolm Priestley 	if (listen_interval >= 2) {
8992b96797SForest Bond 
907404eab2SPhilip Worrall 		/* clear always listen beacon */
912bac6f98SMalcolm Priestley 		vnt_mac_reg_bits_off(priv, MAC_REG_PSCTL, PSCTL_ALBCN);
92f9cfbe94SPhilip Worrall 
937404eab2SPhilip Worrall 		/* first time set listen next beacon */
942bac6f98SMalcolm Priestley 		vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_LNBCN);
9592b96797SForest Bond 
962bac6f98SMalcolm Priestley 		mgmt->wCountToWakeUp = listen_interval;
9792b96797SForest Bond 
98f9cfbe94SPhilip Worrall 	} else {
9992b96797SForest Bond 
1007404eab2SPhilip Worrall 		/* always listen beacon */
1012bac6f98SMalcolm Priestley 		vnt_mac_reg_bits_on(priv, MAC_REG_PSCTL, PSCTL_ALBCN);
10292b96797SForest Bond 
1032bac6f98SMalcolm Priestley 		mgmt->wCountToWakeUp = 0;
10492b96797SForest Bond 	}
10592b96797SForest Bond 
1062bac6f98SMalcolm Priestley 	priv->bEnablePSMode = true;
10792b96797SForest Bond 
10864052b78SAybuke Ozdemir 	/* We don't send null pkt in ad hoc mode
10964052b78SAybuke Ozdemir 	 * since beacon will handle this.
11064052b78SAybuke Ozdemir 	 */
1112bac6f98SMalcolm Priestley 	if (priv->op_mode == NL80211_IFTYPE_STATION)
1122bac6f98SMalcolm Priestley 		PSbSendNullPacket(priv);
113f9cfbe94SPhilip Worrall 
1142bac6f98SMalcolm Priestley 	priv->bPWBitOn = true;
11592b96797SForest Bond 	DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "PS:Power Saving Mode Enable...\n");
11692b96797SForest Bond }
11792b96797SForest Bond 
1187404eab2SPhilip Worrall /*
11992b96797SForest Bond  *
12092b96797SForest Bond  * Routine Description:
12192b96797SForest Bond  * Disable hw power saving functions
12292b96797SForest Bond  *
12392b96797SForest Bond  * Return Value:
12492b96797SForest Bond  *    None.
12592b96797SForest Bond  *
1267404eab2SPhilip Worrall  */
12792b96797SForest Bond 
12845c73bb1SMalcolm Priestley void PSvDisablePowerSaving(struct vnt_private *pDevice)
12992b96797SForest Bond {
13092b96797SForest Bond 
1317404eab2SPhilip Worrall 	/* disable power saving hw function */
1321390b02aSMalcolm Priestley 	vnt_control_out(pDevice, MESSAGE_TYPE_DISABLE_PS, 0,
133f9cfbe94SPhilip Worrall 						0, 0, NULL);
13492b96797SForest Bond 
1357404eab2SPhilip Worrall 	/* clear AutoSleep */
136a9bed1dfSMalcolm Priestley 	vnt_mac_reg_bits_off(pDevice, MAC_REG_PSCFG, PSCFG_AUTOSLEEP);
13792b96797SForest Bond 
1387404eab2SPhilip Worrall 	/* set always listen beacon */
13936957537SMalcolm Priestley 	vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_ALBCN);
140e269fc2dSAndres More 	pDevice->bEnablePSMode = false;
14192b96797SForest Bond 
142a0ad2776SMalcolm Priestley 	if (pDevice->op_mode == NL80211_IFTYPE_STATION)
14392b96797SForest Bond 		PSbSendNullPacket(pDevice);
144f9cfbe94SPhilip Worrall 
145e269fc2dSAndres More 	pDevice->bPWBitOn = false;
14692b96797SForest Bond }
14792b96797SForest Bond 
1487404eab2SPhilip Worrall /*
14992b96797SForest Bond  *
15092b96797SForest Bond  * Routine Description:
15192b96797SForest Bond  * Consider to power down when no more packets to tx or rx.
15292b96797SForest Bond  *
15392b96797SForest Bond  * Return Value:
1544e9b5e2bSAndres More  *    true, if power down success
155e269fc2dSAndres More  *    false, if fail
1567404eab2SPhilip Worrall  */
15792b96797SForest Bond 
15845c73bb1SMalcolm Priestley int PSbConsiderPowerDown(struct vnt_private *pDevice, int bCheckRxDMA,
15945c73bb1SMalcolm Priestley 	int bCheckCountToWakeUp)
16092b96797SForest Bond {
16145c73bb1SMalcolm Priestley 	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
16245c73bb1SMalcolm Priestley 	u8 byData;
16392b96797SForest Bond 
1647404eab2SPhilip Worrall 	/* check if already in Doze mode */
16553742906SMalcolm Priestley 	vnt_control_in_u8(pDevice, MESSAGE_REQUEST_MACREG,
166f9cfbe94SPhilip Worrall 					MAC_REG_PSCTL, &byData);
167f9cfbe94SPhilip Worrall 
16892b96797SForest Bond 	if ((byData & PSCTL_PS) != 0)
1694e9b5e2bSAndres More 		return true;
17092b96797SForest Bond 
17192b96797SForest Bond 	if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) {
1727404eab2SPhilip Worrall 		/* check if in TIM wake period */
17392b96797SForest Bond 		if (pMgmt->bInTIMWake)
174e269fc2dSAndres More 			return false;
17592b96797SForest Bond 	}
17692b96797SForest Bond 
1777404eab2SPhilip Worrall 	/* check scan state */
17892b96797SForest Bond 	if (pDevice->bCmdRunning)
179e269fc2dSAndres More 		return false;
18092b96797SForest Bond 
1817404eab2SPhilip Worrall 	/* Tx Burst */
18292b96797SForest Bond 	if (pDevice->bPSModeTxBurst)
183e269fc2dSAndres More 		return false;
18492b96797SForest Bond 
1857404eab2SPhilip Worrall 	/* Froce PSEN on */
18636957537SMalcolm Priestley 	vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_PSEN);
18792b96797SForest Bond 
18892b96797SForest Bond 	if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA) {
189f9cfbe94SPhilip Worrall 		if (bCheckCountToWakeUp && (pMgmt->wCountToWakeUp == 0
190f9cfbe94SPhilip Worrall 			|| pMgmt->wCountToWakeUp == 1)) {
191e269fc2dSAndres More 				return false;
19292b96797SForest Bond 		}
19392b96797SForest Bond 	}
19492b96797SForest Bond 
1954e9b5e2bSAndres More 	pDevice->bPSRxBeacon = true;
196f9cfbe94SPhilip Worrall 
1977404eab2SPhilip Worrall 	/* no Tx, no Rx isr, now go to Doze */
19836957537SMalcolm Priestley 	vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_GO2DOZE);
19992b96797SForest Bond 	DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Go to Doze ZZZZZZZZZZZZZZZ\n");
2004e9b5e2bSAndres More 	return true;
20192b96797SForest Bond }
20292b96797SForest Bond 
2037404eab2SPhilip Worrall /*
20492b96797SForest Bond  *
20592b96797SForest Bond  * Routine Description:
20692b96797SForest Bond  * Send PS-POLL packet
20792b96797SForest Bond  *
20892b96797SForest Bond  * Return Value:
20992b96797SForest Bond  *    None.
21092b96797SForest Bond  *
2117404eab2SPhilip Worrall  */
21292b96797SForest Bond 
21345c73bb1SMalcolm Priestley void PSvSendPSPOLL(struct vnt_private *pDevice)
21492b96797SForest Bond {
21545c73bb1SMalcolm Priestley 	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
21645c73bb1SMalcolm Priestley 	struct vnt_tx_mgmt *pTxPacket = NULL;
21792b96797SForest Bond 
21845c73bb1SMalcolm Priestley 	memset(pMgmt->pbyPSPacketPool, 0, sizeof(struct vnt_tx_mgmt)
21945c73bb1SMalcolm Priestley 		+ WLAN_HDR_ADDR2_LEN);
22045c73bb1SMalcolm Priestley 	pTxPacket = (struct vnt_tx_mgmt *)pMgmt->pbyPSPacketPool;
22145c73bb1SMalcolm Priestley 	pTxPacket->p80211Header = (PUWLAN_80211HDR)((u8 *)pTxPacket
22245c73bb1SMalcolm Priestley 		+ sizeof(struct vnt_tx_mgmt));
22345c73bb1SMalcolm Priestley 
22492b96797SForest Bond 	pTxPacket->p80211Header->sA2.wFrameCtl = cpu_to_le16(
22592b96797SForest Bond 		(
22692b96797SForest Bond 			WLAN_SET_FC_FTYPE(WLAN_TYPE_CTL) |
22792b96797SForest Bond 			WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_PSPOLL) |
22892b96797SForest Bond 			WLAN_SET_FC_PWRMGT(0)
22992b96797SForest Bond 		));
230f9cfbe94SPhilip Worrall 
23164052b78SAybuke Ozdemir 	pTxPacket->p80211Header->sA2.wDurationID =
23264052b78SAybuke Ozdemir 		pMgmt->wCurrAID | BIT14 | BIT15;
23364052b78SAybuke Ozdemir 	memcpy(pTxPacket->p80211Header->sA2.abyAddr1, pMgmt->abyCurrBSSID,
23464052b78SAybuke Ozdemir 		WLAN_ADDR_LEN);
23564052b78SAybuke Ozdemir 	memcpy(pTxPacket->p80211Header->sA2.abyAddr2, pMgmt->abyMACAddr,
23664052b78SAybuke Ozdemir 		WLAN_ADDR_LEN);
23792b96797SForest Bond 	pTxPacket->cbMPDULen = WLAN_HDR_ADDR2_LEN;
23892b96797SForest Bond 	pTxPacket->cbPayloadLen = 0;
239f9cfbe94SPhilip Worrall 
240e25b75ecSPhilip Worrall 	/* log failure if sending failed */
2410f783c9dSJohannes Löthberg 	if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING)
24264052b78SAybuke Ozdemir 		DBG_PRT(MSG_LEVEL_DEBUG,
24364052b78SAybuke Ozdemir 			KERN_INFO "Send PS-Poll packet failed..\n");
244e25b75ecSPhilip Worrall }
24592b96797SForest Bond 
2467404eab2SPhilip Worrall /*
24792b96797SForest Bond  *
24892b96797SForest Bond  * Routine Description:
24992b96797SForest Bond  * Send NULL packet to AP for notification power state of STA
25092b96797SForest Bond  *
25192b96797SForest Bond  * Return Value:
25292b96797SForest Bond  *    None.
25392b96797SForest Bond  *
2547404eab2SPhilip Worrall  */
2550cbd8d98SAndres More 
25645c73bb1SMalcolm Priestley int PSbSendNullPacket(struct vnt_private *pDevice)
25792b96797SForest Bond {
25845c73bb1SMalcolm Priestley 	struct vnt_tx_mgmt *pTxPacket = NULL;
25945c73bb1SMalcolm Priestley 	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
260b898cf21SPhilip Worrall 	u16 flags = 0;
26192b96797SForest Bond 
262e269fc2dSAndres More 	if (pDevice->bLinkPass == false)
263e269fc2dSAndres More 		return false;
26492b96797SForest Bond 
26560cc2747SMalcolm Priestley 	if (pDevice->bEnablePSMode == false && pDevice->tx_trigger == false)
266e269fc2dSAndres More 		return false;
267feaf03d3SAndres More 
26845c73bb1SMalcolm Priestley 	memset(pMgmt->pbyPSPacketPool, 0, sizeof(struct vnt_tx_mgmt)
26945c73bb1SMalcolm Priestley 		+ WLAN_NULLDATA_FR_MAXLEN);
27045c73bb1SMalcolm Priestley 	pTxPacket = (struct vnt_tx_mgmt *)pMgmt->pbyPSPacketPool;
27145c73bb1SMalcolm Priestley 	pTxPacket->p80211Header = (PUWLAN_80211HDR)((u8 *)pTxPacket
27245c73bb1SMalcolm Priestley 		+ sizeof(struct vnt_tx_mgmt));
27392b96797SForest Bond 
274b898cf21SPhilip Worrall 	flags = WLAN_SET_FC_FTYPE(WLAN_TYPE_DATA) |
275b898cf21SPhilip Worrall 			WLAN_SET_FC_FSTYPE(WLAN_FSTYPE_NULL);
276b898cf21SPhilip Worrall 
277b898cf21SPhilip Worrall 	if (pDevice->bEnablePSMode)
278b898cf21SPhilip Worrall 		flags |= WLAN_SET_FC_PWRMGT(1);
279b898cf21SPhilip Worrall 	else
280b898cf21SPhilip Worrall 		flags |= WLAN_SET_FC_PWRMGT(0);
281b898cf21SPhilip Worrall 
282b898cf21SPhilip Worrall 	pTxPacket->p80211Header->sA3.wFrameCtl = cpu_to_le16(flags);
28392b96797SForest Bond 
284036dba14SPhilip Worrall 	if (pMgmt->eCurrMode != WMAC_MODE_IBSS_STA)
28564052b78SAybuke Ozdemir 		pTxPacket->p80211Header->sA3.wFrameCtl |=
28664052b78SAybuke Ozdemir 			cpu_to_le16((u16)WLAN_SET_FC_TODS(1));
28792b96797SForest Bond 
28864052b78SAybuke Ozdemir 	memcpy(pTxPacket->p80211Header->sA3.abyAddr1, pMgmt->abyCurrBSSID,
28964052b78SAybuke Ozdemir 		WLAN_ADDR_LEN);
29064052b78SAybuke Ozdemir 	memcpy(pTxPacket->p80211Header->sA3.abyAddr2, pMgmt->abyMACAddr,
29164052b78SAybuke Ozdemir 		WLAN_ADDR_LEN);
29264052b78SAybuke Ozdemir 	memcpy(pTxPacket->p80211Header->sA3.abyAddr3, pMgmt->abyCurrBSSID,
29364052b78SAybuke Ozdemir 		WLAN_BSSID_LEN);
29492b96797SForest Bond 	pTxPacket->cbMPDULen = WLAN_HDR_ADDR3_LEN;
29592b96797SForest Bond 	pTxPacket->cbPayloadLen = 0;
296e25b75ecSPhilip Worrall 	/* log error if sending failed */
29792b96797SForest Bond 	if (csMgmt_xmit(pDevice, pTxPacket) != CMD_STATUS_PENDING) {
29864052b78SAybuke Ozdemir 		DBG_PRT(MSG_LEVEL_DEBUG,
29964052b78SAybuke Ozdemir 			KERN_INFO "Send Null Packet failed !\n");
300e269fc2dSAndres More 		return false;
30192b96797SForest Bond 	}
3024e9b5e2bSAndres More 	return true;
30392b96797SForest Bond }
30492b96797SForest Bond 
3057404eab2SPhilip Worrall /*
30692b96797SForest Bond  *
30792b96797SForest Bond  * Routine Description:
30892b96797SForest Bond  * Check if Next TBTT must wake up
30992b96797SForest Bond  *
31092b96797SForest Bond  * Return Value:
31192b96797SForest Bond  *    None.
31292b96797SForest Bond  *
3137404eab2SPhilip Worrall  */
31492b96797SForest Bond 
31545c73bb1SMalcolm Priestley int PSbIsNextTBTTWakeUp(struct vnt_private *pDevice)
31692b96797SForest Bond {
31745c73bb1SMalcolm Priestley 	struct vnt_manager *pMgmt = &pDevice->vnt_mgmt;
318e269fc2dSAndres More 	int bWakeUp = false;
31992b96797SForest Bond 
32092b96797SForest Bond 	if (pMgmt->wListenInterval >= 2) {
321036dba14SPhilip Worrall 		if (pMgmt->wCountToWakeUp == 0)
32292b96797SForest Bond 			pMgmt->wCountToWakeUp = pMgmt->wListenInterval;
32392b96797SForest Bond 
32492b96797SForest Bond 		pMgmt->wCountToWakeUp--;
32592b96797SForest Bond 
32692b96797SForest Bond 		if (pMgmt->wCountToWakeUp == 1) {
3277404eab2SPhilip Worrall 			/* Turn on wake up to listen next beacon */
32836957537SMalcolm Priestley 			vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_LNBCN);
329e269fc2dSAndres More 			pDevice->bPSRxBeacon = false;
3304e9b5e2bSAndres More 			bWakeUp = true;
33192b96797SForest Bond 		} else if (!pDevice->bPSRxBeacon) {
3327404eab2SPhilip Worrall 			/* Listen until RxBeacon */
33336957537SMalcolm Priestley 			vnt_mac_reg_bits_on(pDevice, MAC_REG_PSCTL, PSCTL_LNBCN);
33492b96797SForest Bond 		}
33592b96797SForest Bond 	}
33692b96797SForest Bond 	return bWakeUp;
33792b96797SForest Bond }
33892b96797SForest Bond 
339