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