1185377Ssam/*
2185377Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3185377Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc.
4185377Ssam *
5185377Ssam * Permission to use, copy, modify, and/or distribute this software for any
6185377Ssam * purpose with or without fee is hereby granted, provided that the above
7185377Ssam * copyright notice and this permission notice appear in all copies.
8185377Ssam *
9185377Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10185377Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11185377Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12185377Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13185377Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14185377Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15185377Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16185377Ssam *
17203159Srpaulo * $FreeBSD$
18185377Ssam */
19185377Ssam#include "opt_ah.h"
20185377Ssam
21185377Ssam#include "ah.h"
22185377Ssam#include "ah_internal.h"
23185377Ssam
24185377Ssam#include "ar5212/ar5212.h"
25185377Ssam#include "ar5212/ar5212reg.h"
26185377Ssam#include "ar5212/ar5212desc.h"
27185377Ssam
28185377Ssam/*
29185377Ssam * Notify Power Mgt is enabled in self-generated frames.
30185377Ssam * If requested, force chip awake.
31185377Ssam *
32185377Ssam * Returns A_OK if chip is awake or successfully forced awake.
33185377Ssam *
34185377Ssam * WARNING WARNING WARNING
35185377Ssam * There is a problem with the chip where sometimes it will not wake up.
36185377Ssam */
37185377Ssamstatic HAL_BOOL
38185377Ssamar5212SetPowerModeAwake(struct ath_hal *ah, int setChip)
39185377Ssam{
40185377Ssam#define	AR_SCR_MASK \
41203159Srpaulo    (AR_SCR_SLDUR|AR_SCR_SLE|AR_SCR_SLDTP|AR_SCR_SLDWP|\
42203159Srpaulo     AR_SCR_SLEPOL|AR_SCR_MIBIE|AR_SCR_UNKNOWN)
43185377Ssam#define	POWER_UP_TIME	2000
44185377Ssam	uint32_t scr, val;
45185377Ssam	int i;
46185377Ssam
47185377Ssam	if (setChip) {
48185377Ssam		/*
49185377Ssam		 * Be careful setting the AWAKE mode.  When we are called
50185377Ssam		 * with the chip powered down the read returns 0xffffffff
51185377Ssam		 * which when blindly written back with OS_REG_RMW_FIELD
52185377Ssam		 * enables the MIB interrupt for the sleep performance
53185377Ssam		 * counters.  This can result in an interrupt storm when
54185377Ssam		 * ANI is in operation as noone knows to turn off the MIB
55185377Ssam		 * interrupt cause.
56185377Ssam		 */
57185377Ssam		scr = OS_REG_READ(ah, AR_SCR);
58185377Ssam		if (scr & ~AR_SCR_MASK) {
59185377Ssam			HALDEBUG(ah, HAL_DEBUG_ANY,
60185377Ssam			    "%s: bogus SCR 0x%x, PCICFG 0x%x\n",
61185377Ssam			    __func__, scr, OS_REG_READ(ah, AR_PCICFG));
62185377Ssam			scr = 0;
63185377Ssam		}
64185377Ssam		scr = (scr &~ AR_SCR_SLE) | AR_SCR_SLE_WAKE;
65185377Ssam		OS_REG_WRITE(ah, AR_SCR, scr);
66185377Ssam		OS_DELAY(10);	/* Give chip the chance to awake */
67185377Ssam
68185377Ssam		for (i = POWER_UP_TIME / 50; i != 0; i--) {
69185377Ssam			val = OS_REG_READ(ah, AR_PCICFG);
70185377Ssam			if ((val & AR_PCICFG_SPWR_DN) == 0)
71185377Ssam				break;
72185377Ssam			OS_DELAY(50);
73185377Ssam			OS_REG_WRITE(ah, AR_SCR, scr);
74185377Ssam		}
75185377Ssam		if (i == 0) {
76185377Ssam#ifdef AH_DEBUG
77185377Ssam			ath_hal_printf(ah, "%s: Failed to wakeup in %ums\n",
78185377Ssam				__func__, POWER_UP_TIME/50);
79185377Ssam#endif
80185377Ssam			return AH_FALSE;
81185377Ssam		}
82185377Ssam	}
83185377Ssam
84185377Ssam	OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
85185377Ssam	return AH_TRUE;
86185377Ssam#undef POWER_UP_TIME
87185377Ssam#undef AR_SCR_MASK
88185377Ssam}
89185377Ssam
90185377Ssam/*
91185377Ssam * Notify Power Mgt is disabled in self-generated frames.
92185377Ssam * If requested, force chip to sleep.
93185377Ssam */
94185377Ssamstatic void
95185377Ssamar5212SetPowerModeSleep(struct ath_hal *ah, int setChip)
96185377Ssam{
97185377Ssam	OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
98185377Ssam	if (setChip)
99185377Ssam		OS_REG_RMW_FIELD(ah, AR_SCR, AR_SCR_SLE, AR_SCR_SLE_SLP);
100185377Ssam}
101185377Ssam
102185377Ssam/*
103185377Ssam * Notify Power Management is enabled in self-generating
104185377Ssam * fames.  If request, set power mode of chip to
105185377Ssam * auto/normal.  Duration in units of 128us (1/8 TU).
106185377Ssam */
107185377Ssamstatic void
108185377Ssamar5212SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip)
109185377Ssam{
110185377Ssam	OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
111185377Ssam	if (setChip)
112185377Ssam		OS_REG_RMW_FIELD(ah, AR_SCR, AR_SCR_SLE, AR_SCR_SLE_NORM);
113185377Ssam}
114185377Ssam
115185377Ssam/*
116185377Ssam * Set power mgt to the requested mode, and conditionally set
117185377Ssam * the chip as well
118185377Ssam */
119185377SsamHAL_BOOL
120185377Ssamar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip)
121185377Ssam{
122185377Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
123185377Ssam#ifdef AH_DEBUG
124185377Ssam	static const char* modes[] = {
125185377Ssam		"AWAKE",
126185377Ssam		"FULL-SLEEP",
127185377Ssam		"NETWORK SLEEP",
128185377Ssam		"UNDEFINED"
129185377Ssam	};
130185377Ssam#endif
131185377Ssam	int status = AH_TRUE;
132185377Ssam
133185377Ssam	HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__,
134185377Ssam		modes[ahp->ah_powerMode], modes[mode],
135185377Ssam		setChip ? "set chip " : "");
136185377Ssam	switch (mode) {
137185377Ssam	case HAL_PM_AWAKE:
138185377Ssam		status = ar5212SetPowerModeAwake(ah, setChip);
139185377Ssam		break;
140185377Ssam	case HAL_PM_FULL_SLEEP:
141185377Ssam		ar5212SetPowerModeSleep(ah, setChip);
142185377Ssam		break;
143185377Ssam	case HAL_PM_NETWORK_SLEEP:
144185377Ssam		ar5212SetPowerModeNetworkSleep(ah, setChip);
145185377Ssam		break;
146185377Ssam	default:
147185377Ssam		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n",
148185377Ssam		    __func__, mode);
149185377Ssam		return AH_FALSE;
150185377Ssam	}
151185377Ssam	ahp->ah_powerMode = mode;
152185377Ssam	return status;
153185377Ssam}
154185377Ssam
155185377Ssam/*
156185377Ssam * Return the current sleep mode of the chip
157185377Ssam */
158185377SsamHAL_POWER_MODE
159185377Ssamar5212GetPowerMode(struct ath_hal *ah)
160185377Ssam{
161185377Ssam	/* Just so happens the h/w maps directly to the abstracted value */
162185377Ssam	return MS(OS_REG_READ(ah, AR_SCR), AR_SCR_SLE);
163185377Ssam}
164185377Ssam
165185377Ssam#if 0
166185377Ssam/*
167185377Ssam * Return the current sleep state of the chip
168185377Ssam * TRUE = sleeping
169185377Ssam */
170185377SsamHAL_BOOL
171185377Ssamar5212GetPowerStatus(struct ath_hal *ah)
172185377Ssam{
173185377Ssam	return (OS_REG_READ(ah, AR_PCICFG) & AR_PCICFG_SPWR_DN) != 0;
174185377Ssam}
175185377Ssam#endif
176