1/*
2 * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3 * Copyright (c) 2002-2008 Atheros Communications, Inc.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 *
17 * $FreeBSD$
18 */
19#include "opt_ah.h"
20
21#include "ah.h"
22#include "ah_internal.h"
23
24#include "ar5416/ar5416.h"
25#include "ar5416/ar5416reg.h"
26
27/*
28 * Notify Power Mgt is enabled in self-generated frames.
29 * If requested, force chip awake.
30 *
31 * Returns A_OK if chip is awake or successfully forced awake.
32 *
33 * WARNING WARNING WARNING
34 * There is a problem with the chip where sometimes it will not wake up.
35 */
36static HAL_BOOL
37ar5416SetPowerModeAwake(struct ath_hal *ah, int setChip)
38{
39#define	POWER_UP_TIME	200000
40	uint32_t val;
41	int i = 0;
42
43	if (setChip) {
44		/*
45		 * Do a Power-On-Reset if OWL is shutdown
46		 * the NetBSD driver  power-cycles the Cardbus slot
47		 * as part of the reset procedure.
48		 */
49		if ((OS_REG_READ(ah, AR_RTC_STATUS)
50			& AR_RTC_PM_STATUS_M) == AR_RTC_STATUS_SHUTDOWN) {
51			if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON))
52				goto bad;
53			AH5416(ah)->ah_initPLL(ah, AH_NULL);
54		}
55
56		if (AR_SREV_HOWL(ah))
57			OS_REG_SET_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
58
59		OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
60		if (AR_SREV_HOWL(ah))
61			OS_DELAY(10000);
62		else
63			OS_DELAY(50);   /* Give chip the chance to awake */
64
65		for (i = POWER_UP_TIME / 50; i != 0; i--) {
66			val = OS_REG_READ(ah, AR_RTC_STATUS) & AR_RTC_STATUS_M;
67			if (val == AR_RTC_STATUS_ON)
68				break;
69			OS_DELAY(50);
70			OS_REG_SET_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
71		}
72	bad:
73		if (i == 0) {
74#ifdef AH_DEBUG
75			ath_hal_printf(ah, "%s: Failed to wakeup in %ums\n",
76				__func__, POWER_UP_TIME/1000);
77#endif
78			return AH_FALSE;
79		}
80	}
81
82	OS_REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
83	return AH_TRUE;
84#undef POWER_UP_TIME
85}
86
87/*
88 * Notify Power Mgt is disabled in self-generated frames.
89 * If requested, force chip to sleep.
90 */
91static void
92ar5416SetPowerModeSleep(struct ath_hal *ah, int setChip)
93{
94	OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
95	if (setChip) {
96		/* Clear the RTC force wake bit to allow the mac to sleep */
97		OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
98		if (! AR_SREV_HOWL(ah))
99			OS_REG_WRITE(ah, AR_RC, AR_RC_AHB|AR_RC_HOSTIF);
100		/* Shutdown chip. Active low */
101		if (! AR_SREV_OWL(ah))
102			OS_REG_CLR_BIT(ah, AR_RTC_RESET, AR_RTC_RESET_EN);
103	}
104}
105
106/*
107 * Notify Power Management is enabled in self-generating
108 * fames.  If request, set power mode of chip to
109 * auto/normal.  Duration in units of 128us (1/8 TU).
110 */
111static void
112ar5416SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip)
113{
114	OS_REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
115
116	if (setChip)
117		OS_REG_CLR_BIT(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN);
118}
119
120/*
121 * Set power mgt to the requested mode, and conditionally set
122 * the chip as well
123 */
124HAL_BOOL
125ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip)
126{
127#ifdef AH_DEBUG
128	static const char* modes[] = {
129		"AWAKE",
130		"FULL-SLEEP",
131		"NETWORK SLEEP",
132		"UNDEFINED"
133	};
134#endif
135	int status = AH_TRUE;
136
137#if 0
138	if (!setChip)
139		return AH_TRUE;
140#endif
141
142	HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__,
143	    modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : "");
144	switch (mode) {
145	case HAL_PM_AWAKE:
146		if (setChip)
147			ah->ah_powerMode = mode;
148		status = ar5416SetPowerModeAwake(ah, setChip);
149		break;
150	case HAL_PM_FULL_SLEEP:
151		ar5416SetPowerModeSleep(ah, setChip);
152		if (setChip)
153			ah->ah_powerMode = mode;
154		break;
155	case HAL_PM_NETWORK_SLEEP:
156		ar5416SetPowerModeNetworkSleep(ah, setChip);
157		if (setChip)
158			ah->ah_powerMode = mode;
159		break;
160	default:
161		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode 0x%x\n",
162		    __func__, mode);
163		return AH_FALSE;
164	}
165	return status;
166}
167
168/*
169 * Return the current sleep mode of the chip
170 */
171HAL_POWER_MODE
172ar5416GetPowerMode(struct ath_hal *ah)
173{
174	int mode = OS_REG_READ(ah, AR_RTC_STATUS);
175	switch (mode & AR_RTC_PM_STATUS_M) {
176	case AR_RTC_STATUS_ON:
177	case AR_RTC_STATUS_WAKEUP:
178		return HAL_PM_AWAKE;
179	case AR_RTC_STATUS_SLEEP:
180		return HAL_PM_NETWORK_SLEEP;
181	case AR_RTC_STATUS_SHUTDOWN:
182		return HAL_PM_FULL_SLEEP;
183	default:
184		HALDEBUG(ah, HAL_DEBUG_ANY,
185		    "%s: unknown power mode, RTC_STATUS 0x%x\n",
186		    __func__, mode);
187		return HAL_PM_UNDEFINED;
188	}
189}
190