Skip to content

Commit

Permalink
ath9k: lock reset and PCU start/stopping
Browse files Browse the repository at this point in the history
Apart from locking the start and stop PCU we need
to ensure we also content starting and stopping the PCU
between hardware resets.

This is part of a series that will help resolve the bug:

https://bugzilla.kernel.org/show_bug.cgi?id=14624

For more details about this issue refer to:

http://marc.info/?l=linux-wireless&m=128629803703756&w=2

Cc: stable@kernel.org
Cc: Ben Greear <greearb@candelatech.com>
Cc: Kyungwan Nam <kyungwan.nam@atheros.com>
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Tested-by: Ben Greear <greearb@candelatech.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Luis R. Rodriguez authored and John W. Linville committed Oct 27, 2010
1 parent b79b33c commit 5e848f7
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 2 deletions.
27 changes: 27 additions & 0 deletions drivers/net/wireless/ath/ath9k/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,9 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
*/
ath9k_hw_set_interrupts(ah, 0);
ath_drain_all_txq(sc, false);

spin_lock_bh(&sc->rx.pcu_lock);

stopped = ath_stoprecv(sc);

/* XXX: do not flush receive queue here. We don't want
Expand Down Expand Up @@ -268,6 +271,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
"reset status %d\n",
channel->center_freq, r);
spin_unlock_bh(&sc->sc_resetlock);
spin_unlock_bh(&sc->rx.pcu_lock);
goto ps_restore;
}
spin_unlock_bh(&sc->sc_resetlock);
Expand All @@ -276,9 +280,12 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
ath_print(common, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
r = -EIO;
spin_unlock_bh(&sc->rx.pcu_lock);
goto ps_restore;
}

spin_unlock_bh(&sc->rx.pcu_lock);

ath_update_txpow(sc);
ath9k_hw_set_interrupts(ah, ah->imask);

Expand Down Expand Up @@ -876,6 +883,7 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
if (!ah->curchan)
ah->curchan = ath_get_curchannel(sc, sc->hw);

spin_lock_bh(&sc->rx.pcu_lock);
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
if (r) {
Expand All @@ -890,8 +898,10 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw)
if (ath_startrecv(sc) != 0) {
ath_print(common, ATH_DBG_FATAL,
"Unable to restart recv logic\n");
spin_unlock_bh(&sc->rx.pcu_lock);
return;
}
spin_unlock_bh(&sc->rx.pcu_lock);

if (sc->sc_flags & SC_OP_BEACONS)
ath_beacon_config(sc, NULL); /* restart beacons */
Expand Down Expand Up @@ -930,6 +940,9 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
ath9k_hw_set_interrupts(ah, 0);

ath_drain_all_txq(sc, false); /* clear pending tx frames */

spin_lock_bh(&sc->rx.pcu_lock);

ath_stoprecv(sc); /* turn off frame recv */
ath_flushrecv(sc); /* flush recv queue */

Expand All @@ -947,6 +960,9 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw)
spin_unlock_bh(&sc->sc_resetlock);

ath9k_hw_phy_disable(ah);

spin_unlock_bh(&sc->rx.pcu_lock);

ath9k_hw_configpcipowersave(ah, 1, 1);
ath9k_ps_restore(sc);
ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP);
Expand All @@ -966,6 +982,9 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)

ath9k_hw_set_interrupts(ah, 0);
ath_drain_all_txq(sc, retry_tx);

spin_lock_bh(&sc->rx.pcu_lock);

ath_stoprecv(sc);
ath_flushrecv(sc);

Expand All @@ -980,6 +999,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx)
ath_print(common, ATH_DBG_FATAL,
"Unable to start recv logic\n");

spin_unlock_bh(&sc->rx.pcu_lock);

/*
* We may be doing a reset in response to a request
* that changes the channel so update any state that
Expand Down Expand Up @@ -1142,6 +1163,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
* be followed by initialization of the appropriate bits
* and then setup of the interrupt mask.
*/
spin_lock_bh(&sc->rx.pcu_lock);
spin_lock_bh(&sc->sc_resetlock);
r = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (r) {
Expand All @@ -1150,6 +1172,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
"(freq %u MHz)\n", r,
curchan->center_freq);
spin_unlock_bh(&sc->sc_resetlock);
spin_unlock_bh(&sc->rx.pcu_lock);
goto mutex_unlock;
}
spin_unlock_bh(&sc->sc_resetlock);
Expand All @@ -1171,8 +1194,10 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath_print(common, ATH_DBG_FATAL,
"Unable to start recv logic\n");
r = -EIO;
spin_unlock_bh(&sc->rx.pcu_lock);
goto mutex_unlock;
}
spin_unlock_bh(&sc->rx.pcu_lock);

/* Setup our intr mask. */
ah->imask = ATH9K_INT_TX | ATH9K_INT_RXEOL |
Expand Down Expand Up @@ -1371,12 +1396,14 @@ static void ath9k_stop(struct ieee80211_hw *hw)
* before setting the invalid flag. */
ath9k_hw_set_interrupts(ah, 0);

spin_lock_bh(&sc->rx.pcu_lock);
if (!(sc->sc_flags & SC_OP_INVALID)) {
ath_drain_all_txq(sc, false);
ath_stoprecv(sc);
ath9k_hw_phy_disable(ah);
} else
sc->rx.rxlink = NULL;
spin_unlock_bh(&sc->rx.pcu_lock);

/* disable HAL and put h/w to sleep */
ath9k_hw_disable(ah);
Expand Down
2 changes: 0 additions & 2 deletions drivers/net/wireless/ath/ath9k/recv.c
Original file line number Diff line number Diff line change
Expand Up @@ -533,13 +533,11 @@ bool ath_stoprecv(struct ath_softc *sc)

void ath_flushrecv(struct ath_softc *sc)
{
spin_lock_bh(&sc->rx.pcu_lock);
sc->sc_flags |= SC_OP_RXFLUSH;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
ath_rx_tasklet(sc, 1, true);
ath_rx_tasklet(sc, 1, false);
sc->sc_flags &= ~SC_OP_RXFLUSH;
spin_unlock_bh(&sc->rx.pcu_lock);
}

static bool ath_beacon_dtim_pending_cab(struct sk_buff *skb)
Expand Down

0 comments on commit 5e848f7

Please sign in to comment.