Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93b1b37f authored by Vivek Natarajan's avatar Vivek Natarajan Committed by John W. Linville
Browse files

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: default avatarVivek Natarajan <vnatarajan@atheros.com>
Signed-off-by: default avatarSujith <Sujith.Manoharan@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 6170cd5c
Loading
Loading
Loading
Loading
+96 −66
Original line number Diff line number Diff line
@@ -965,7 +965,7 @@ int ath9k_hw_init(struct ath_hw *ah)
	ath9k_hw_init_mode_regs(ah);

	if (ah->is_pciexpress)
		ath9k_hw_configpcipowersave(ah, 0);
		ath9k_hw_configpcipowersave(ah, 0, 0);
	else
		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
 * 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;
	u32 val;

	if (ah->is_pciexpress != true)
		return;
@@ -3017,9 +3018,7 @@ void ath9k_hw_configpcipowersave(struct ath_hw *ah, int restore)
		return;

	/* Nothing to do on restore for 11N */
	if (restore)
		return;

	if (!restore) {
		if (AR_SREV_9280_20_OR_LATER(ah)) {
			/*
			 * 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 */
		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 {
		if (AR_SREV_9285(ah) || AR_SREV_9271(ah) || AR_SREV_9287(ah))
			REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT);
			if (AR_SREV_9285(ah) || AR_SREV_9271(ah) ||
			    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
		 * otherwise card may disappear.
				 * On AR9280 chips bit 22 of 0x4004 needs to be
				 * set otherwise card may disappear.
				 */
		else if (AR_SREV_9280(ah))
			REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT);
		else
			REG_WRITE(ah, AR_WA, AR_WA_DEFAULT);
				val = AR9280_WA_DEFAULT;
				if (!power_off)
					val &= (~AR_WA_D3_L1_DISABLE);
			} 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);
			}
		}
	}
}

+1 −1
Original line number Diff line number Diff line
@@ -650,7 +650,7 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
				    const struct ath9k_beacon_state *bs);
bool ath9k_hw_setpower(struct ath_hw *ah,
		       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 */
bool ath9k_hw_intrpend(struct ath_hw *ah);
+4 −4
Original line number Diff line number Diff line
@@ -1131,7 +1131,7 @@ void ath_radio_enable(struct ath_softc *sc)
	int r;

	ath9k_ps_wakeup(sc);
	ath9k_hw_configpcipowersave(ah, 0);
	ath9k_hw_configpcipowersave(ah, 0, 0);

	if (!ah->curchan)
		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);

	ath9k_hw_phy_disable(ah);
	ath9k_hw_configpcipowersave(ah, 1);
	ath9k_hw_configpcipowersave(ah, 1, 1);
	ath9k_ps_restore(sc);
	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);

	/* 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
@@ -2170,7 +2170,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)

	/* disable HAL and put h/w to sleep */
	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);

	sc->sc_flags |= SC_OP_INVALID;
+2 −1
Original line number Diff line number Diff line
@@ -676,8 +676,9 @@
#define AR_RC_HOSTIF         0x00000100

#define AR_WA                		0x4004
#define AR_WA_D3_L1_DISABLE		(1 << 14)
#define AR9285_WA_DEFAULT 		0x004a05cb
#define AR9280_WA_DEFAULT           	0x0040073f
#define AR9280_WA_DEFAULT           	0x0040073b
#define AR_WA_DEFAULT               	0x0000073f