ath9k: Revamp PCIE workarounds

* Disable L1 state ONLY when device is in D3 mode.
* Clear bit 22 of register 0x4004.
* Handle power on/off properly

Not setting the workarounds properly resulted in the
disappearance of the card in certain cases.

Signed-off-by: Vivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: Sujith <Sujith.Manoharan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Vivek Natarajan 2009-09-17 09:24:58 +05:30 committed by John W. Linville
parent 6170cd5c72
commit 93b1b37f6a
4 changed files with 107 additions and 76 deletions

View File

@ -965,7 +965,7 @@ int ath9k_hw_init(struct ath_hw *ah)
ath9k_hw_init_mode_regs(ah); ath9k_hw_init_mode_regs(ah);
if (ah->is_pciexpress) if (ah->is_pciexpress)
ath9k_hw_configpcipowersave(ah, 0); ath9k_hw_configpcipowersave(ah, 0, 0);
else else
ath9k_hw_disablepcie(ah); ath9k_hw_disablepcie(ah);
@ -3005,9 +3005,10 @@ void ath9k_ps_restore(struct ath_softc *sc)
* Programming the SerDes must go through the same 288 bit serial shift * Programming the SerDes must go through the same 288 bit serial shift
* register as the other analog registers. Hence the 9 writes. * register as the other analog registers. Hence the 9 writes.
*/ */
void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore) void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off)
{ {
u8 i; u8 i;
u32 val;
if (ah->is_pciexpress != true) if (ah->is_pciexpress != true)
return; return;
@ -3017,9 +3018,7 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore)
return; return;
/* Nothing to do on restore for 11N */ /* Nothing to do on restore for 11N */
if (restore) if (!restore) {
return;
if (AR_SREV_9280_20_OR_LATER(ah)) { if (AR_SREV_9280_20_OR_LATER(ah)) {
/* /*
* AR9280 2.0 or later chips use SerDes values from the * AR9280 2.0 or later chips use SerDes values from the
@ -3083,18 +3082,49 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore)
/* Several PCIe massages to ensure proper behaviour */ /* Several PCIe massages to ensure proper behaviour */
if (ah->config.pcie_waen) { if (ah->config.pcie_waen) {
REG_WRITE(ah, AR_WA, ah->config.pcie_waen); val = ah->config.pcie_waen;
if (!power_off)
val &= (~AR_WA_D3_L1_DISABLE);
} else { } else {
if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah)) if (AR_SREV_9285(ah) || AR_SREV_9271(ah) ||
REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); AR_SREV_9287(ah)) {
val = AR9285_WA_DEFAULT;
if (!power_off)
val &= (~AR_WA_D3_L1_DISABLE);
} else if (AR_SREV_9280(ah)) {
/* /*
* On AR9280 chips bit 22 of 0x4004 needs to be set to * On AR9280 chips bit 22 of 0x4004 needs to be
* otherwise card may disappear. * set otherwise card may disappear.
*/ */
else if (AR_SREV_9280(ah)) val = AR9280_WA_DEFAULT;
REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT); if (!power_off)
else val &= (~AR_WA_D3_L1_DISABLE);
REG_WRITE(ah, AR_WA, AR_WA_DEFAULT); } else
val = AR_WA_DEFAULT;
}
REG_WRITE(ah, AR_WA, val);
}
if (power_off) {
/*
* Set PCIe workaround bits
* bit 14 in WA register (disable L1) should only
* be set when device enters D3 and be cleared
* when device comes back to D0.
*/
if (ah->config.pcie_waen) {
if (ah->config.pcie_waen & AR_WA_D3_L1_DISABLE)
REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE);
} else {
if (((AR_SREV_9285(ah) || AR_SREV_9271(ah) ||
AR_SREV_9287(ah)) &&
(AR9285_WA_DEFAULT & AR_WA_D3_L1_DISABLE)) ||
(AR_SREV_9280(ah) &&
(AR9280_WA_DEFAULT & AR_WA_D3_L1_DISABLE))) {
REG_SET_BIT(ah, AR_WA, AR_WA_D3_L1_DISABLE);
}
}
} }
} }

View File

@ -650,7 +650,7 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
const struct ath9k_beacon_state *bs); const struct ath9k_beacon_state *bs);
bool ath9k_hw_setpower(struct ath_hw *ah, bool ath9k_hw_setpower(struct ath_hw *ah,
enum ath9k_power_mode mode); enum ath9k_power_mode mode);
void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore); void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore, int power_off);
/* Interrupt Handling */ /* Interrupt Handling */
bool ath9k_hw_intrpend(struct ath_hw *ah); bool ath9k_hw_intrpend(struct ath_hw *ah);

View File

@ -1131,7 +1131,7 @@ void ath_radio_enable(struct ath_softc *sc)
int r; int r;
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
ath9k_hw_configpcipowersave(ah, 0); ath9k_hw_configpcipowersave(ah, 0, 0);
if (!ah->curchan) if (!ah->curchan)
ah->curchan = ath_get_curchannel(sc, sc->hw); ah->curchan = ath_get_curchannel(sc, sc->hw);
@ -1202,7 +1202,7 @@ void ath_radio_disable(struct ath_softc *sc)
spin_unlock_bh(&sc->sc_resetlock); spin_unlock_bh(&sc->sc_resetlock);
ath9k_hw_phy_disable(ah); ath9k_hw_phy_disable(ah);
ath9k_hw_configpcipowersave(ah, 1); ath9k_hw_configpcipowersave(ah, 1, 1);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP);
} }
@ -1942,7 +1942,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
init_channel = ath_get_curchannel(sc, hw); init_channel = ath_get_curchannel(sc, hw);
/* Reset SERDES registers */ /* Reset SERDES registers */
ath9k_hw_configpcipowersave(sc->sc_ah, 0); ath9k_hw_configpcipowersave(sc->sc_ah, 0, 0);
/* /*
* The basic interface to setting the hardware in a good * The basic interface to setting the hardware in a good
@ -2170,7 +2170,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
/* disable HAL and put h/w to sleep */ /* disable HAL and put h/w to sleep */
ath9k_hw_disable(sc->sc_ah); ath9k_hw_disable(sc->sc_ah);
ath9k_hw_configpcipowersave(sc->sc_ah, 1); ath9k_hw_configpcipowersave(sc->sc_ah, 1, 1);
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
sc->sc_flags |= SC_OP_INVALID; sc->sc_flags |= SC_OP_INVALID;

View File

@ -676,8 +676,9 @@
#define AR_RC_HOSTIF 0x00000100 #define AR_RC_HOSTIF 0x00000100
#define AR_WA 0x4004 #define AR_WA 0x4004
#define AR_WA_D3_L1_DISABLE (1 << 14)
#define AR9285_WA_DEFAULT 0x004a05cb #define AR9285_WA_DEFAULT 0x004a05cb
#define AR9280_WA_DEFAULT 0x0040073f #define AR9280_WA_DEFAULT 0x0040073b
#define AR_WA_DEFAULT 0x0000073f #define AR_WA_DEFAULT 0x0000073f