diff options
author | Luis R. Rodriguez <lrodriguez@atheros.com> | 2010-10-26 18:27:25 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-11-15 13:24:47 -0500 |
commit | 6a6733f256f18cbcf4875e13f59eedb593b755a8 (patch) | |
tree | ca1aa9ea86d946a4922d2a71d33837ec22454be1 /drivers | |
parent | 4bdd1e978ede034c1211957eb17eaf50de00d234 (diff) |
ath9k: content DMA start / stop through the PCU lock
This helps align resets / RX enable & disable / TX stop / start.
Locking around the PCU is important to ensure the hardware doesn't
get stale data when working with DMA'able data.
This is part of a series of patches which fix stopping
TX DMA completley when requested on the driver.
For more details about this issue refer to this thread:
http://marc.info/?l=linux-wireless&m=128629803703756&w=2
Tested-by: Ben Greear <greearb@candelatech.com>
Cc: Kyungwan Nam <kyungwan.nam@atheros.com>
Cc: stable@kernel.org
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 52 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 2 |
2 files changed, 26 insertions, 28 deletions
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 463ac1229f53..d522112cf736 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -230,6 +230,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, | |||
230 | 230 | ||
231 | ath9k_ps_wakeup(sc); | 231 | ath9k_ps_wakeup(sc); |
232 | 232 | ||
233 | spin_lock_bh(&sc->sc_pcu_lock); | ||
234 | |||
233 | /* | 235 | /* |
234 | * This is only performed if the channel settings have | 236 | * This is only performed if the channel settings have |
235 | * actually changed. | 237 | * actually changed. |
@@ -242,8 +244,6 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, | |||
242 | ath9k_hw_disable_interrupts(ah); | 244 | ath9k_hw_disable_interrupts(ah); |
243 | ath_drain_all_txq(sc, false); | 245 | ath_drain_all_txq(sc, false); |
244 | 246 | ||
245 | spin_lock_bh(&sc->sc_pcu_lock); | ||
246 | |||
247 | stopped = ath_stoprecv(sc); | 247 | stopped = ath_stoprecv(sc); |
248 | 248 | ||
249 | /* XXX: do not flush receive queue here. We don't want | 249 | /* XXX: do not flush receive queue here. We don't want |
@@ -268,7 +268,6 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, | |||
268 | "Unable to reset channel (%u MHz), " | 268 | "Unable to reset channel (%u MHz), " |
269 | "reset status %d\n", | 269 | "reset status %d\n", |
270 | channel->center_freq, r); | 270 | channel->center_freq, r); |
271 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
272 | goto ps_restore; | 271 | goto ps_restore; |
273 | } | 272 | } |
274 | 273 | ||
@@ -276,12 +275,9 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, | |||
276 | ath_print(common, ATH_DBG_FATAL, | 275 | ath_print(common, ATH_DBG_FATAL, |
277 | "Unable to restart recv logic\n"); | 276 | "Unable to restart recv logic\n"); |
278 | r = -EIO; | 277 | r = -EIO; |
279 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
280 | goto ps_restore; | 278 | goto ps_restore; |
281 | } | 279 | } |
282 | 280 | ||
283 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
284 | |||
285 | ath_update_txpow(sc); | 281 | ath_update_txpow(sc); |
286 | ath9k_hw_set_interrupts(ah, ah->imask); | 282 | ath9k_hw_set_interrupts(ah, ah->imask); |
287 | 283 | ||
@@ -292,6 +288,8 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, | |||
292 | } | 288 | } |
293 | 289 | ||
294 | ps_restore: | 290 | ps_restore: |
291 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
292 | |||
295 | ath9k_ps_restore(sc); | 293 | ath9k_ps_restore(sc); |
296 | return r; | 294 | return r; |
297 | } | 295 | } |
@@ -605,6 +603,8 @@ void ath9k_tasklet(unsigned long data) | |||
605 | return; | 603 | return; |
606 | } | 604 | } |
607 | 605 | ||
606 | spin_lock_bh(&sc->sc_pcu_lock); | ||
607 | |||
608 | if (!ath9k_hw_check_alive(ah)) | 608 | if (!ath9k_hw_check_alive(ah)) |
609 | ieee80211_queue_work(sc->hw, &sc->hw_check_work); | 609 | ieee80211_queue_work(sc->hw, &sc->hw_check_work); |
610 | 610 | ||
@@ -615,15 +615,12 @@ void ath9k_tasklet(unsigned long data) | |||
615 | rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); | 615 | rxmask = (ATH9K_INT_RX | ATH9K_INT_RXEOL | ATH9K_INT_RXORN); |
616 | 616 | ||
617 | if (status & rxmask) { | 617 | if (status & rxmask) { |
618 | spin_lock_bh(&sc->sc_pcu_lock); | ||
619 | |||
620 | /* Check for high priority Rx first */ | 618 | /* Check for high priority Rx first */ |
621 | if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && | 619 | if ((ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) && |
622 | (status & ATH9K_INT_RXHP)) | 620 | (status & ATH9K_INT_RXHP)) |
623 | ath_rx_tasklet(sc, 0, true); | 621 | ath_rx_tasklet(sc, 0, true); |
624 | 622 | ||
625 | ath_rx_tasklet(sc, 0, false); | 623 | ath_rx_tasklet(sc, 0, false); |
626 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
627 | } | 624 | } |
628 | 625 | ||
629 | if (status & ATH9K_INT_TX) { | 626 | if (status & ATH9K_INT_TX) { |
@@ -649,6 +646,8 @@ void ath9k_tasklet(unsigned long data) | |||
649 | 646 | ||
650 | /* re-enable hardware interrupt */ | 647 | /* re-enable hardware interrupt */ |
651 | ath9k_hw_enable_interrupts(ah); | 648 | ath9k_hw_enable_interrupts(ah); |
649 | |||
650 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
652 | ath9k_ps_restore(sc); | 651 | ath9k_ps_restore(sc); |
653 | } | 652 | } |
654 | 653 | ||
@@ -876,12 +875,13 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
876 | int r; | 875 | int r; |
877 | 876 | ||
878 | ath9k_ps_wakeup(sc); | 877 | ath9k_ps_wakeup(sc); |
878 | spin_lock_bh(&sc->sc_pcu_lock); | ||
879 | |||
879 | ath9k_hw_configpcipowersave(ah, 0, 0); | 880 | ath9k_hw_configpcipowersave(ah, 0, 0); |
880 | 881 | ||
881 | if (!ah->curchan) | 882 | if (!ah->curchan) |
882 | ah->curchan = ath_get_curchannel(sc, sc->hw); | 883 | ah->curchan = ath_get_curchannel(sc, sc->hw); |
883 | 884 | ||
884 | spin_lock_bh(&sc->sc_pcu_lock); | ||
885 | r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); | 885 | r = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); |
886 | if (r) { | 886 | if (r) { |
887 | ath_print(common, ATH_DBG_FATAL, | 887 | ath_print(common, ATH_DBG_FATAL, |
@@ -897,8 +897,6 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
897 | spin_unlock_bh(&sc->sc_pcu_lock); | 897 | spin_unlock_bh(&sc->sc_pcu_lock); |
898 | return; | 898 | return; |
899 | } | 899 | } |
900 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
901 | |||
902 | if (sc->sc_flags & SC_OP_BEACONS) | 900 | if (sc->sc_flags & SC_OP_BEACONS) |
903 | ath_beacon_config(sc, NULL); /* restart beacons */ | 901 | ath_beacon_config(sc, NULL); /* restart beacons */ |
904 | 902 | ||
@@ -911,6 +909,8 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
911 | ath9k_hw_set_gpio(ah, ah->led_pin, 0); | 909 | ath9k_hw_set_gpio(ah, ah->led_pin, 0); |
912 | 910 | ||
913 | ieee80211_wake_queues(hw); | 911 | ieee80211_wake_queues(hw); |
912 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
913 | |||
914 | ath9k_ps_restore(sc); | 914 | ath9k_ps_restore(sc); |
915 | } | 915 | } |
916 | 916 | ||
@@ -921,6 +921,8 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
921 | int r; | 921 | int r; |
922 | 922 | ||
923 | ath9k_ps_wakeup(sc); | 923 | ath9k_ps_wakeup(sc); |
924 | spin_lock_bh(&sc->sc_pcu_lock); | ||
925 | |||
924 | ieee80211_stop_queues(hw); | 926 | ieee80211_stop_queues(hw); |
925 | 927 | ||
926 | /* | 928 | /* |
@@ -937,8 +939,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
937 | 939 | ||
938 | ath_drain_all_txq(sc, false); /* clear pending tx frames */ | 940 | ath_drain_all_txq(sc, false); /* clear pending tx frames */ |
939 | 941 | ||
940 | spin_lock_bh(&sc->sc_pcu_lock); | ||
941 | |||
942 | ath_stoprecv(sc); /* turn off frame recv */ | 942 | ath_stoprecv(sc); /* turn off frame recv */ |
943 | ath_flushrecv(sc); /* flush recv queue */ | 943 | ath_flushrecv(sc); /* flush recv queue */ |
944 | 944 | ||
@@ -955,10 +955,11 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) | |||
955 | 955 | ||
956 | ath9k_hw_phy_disable(ah); | 956 | ath9k_hw_phy_disable(ah); |
957 | 957 | ||
958 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
959 | |||
960 | ath9k_hw_configpcipowersave(ah, 1, 1); | 958 | ath9k_hw_configpcipowersave(ah, 1, 1); |
959 | |||
960 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
961 | ath9k_ps_restore(sc); | 961 | ath9k_ps_restore(sc); |
962 | |||
962 | ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP); | 963 | ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP); |
963 | } | 964 | } |
964 | 965 | ||
@@ -972,13 +973,13 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) | |||
972 | /* Stop ANI */ | 973 | /* Stop ANI */ |
973 | del_timer_sync(&common->ani.timer); | 974 | del_timer_sync(&common->ani.timer); |
974 | 975 | ||
976 | spin_lock_bh(&sc->sc_pcu_lock); | ||
977 | |||
975 | ieee80211_stop_queues(hw); | 978 | ieee80211_stop_queues(hw); |
976 | 979 | ||
977 | ath9k_hw_disable_interrupts(ah); | 980 | ath9k_hw_disable_interrupts(ah); |
978 | ath_drain_all_txq(sc, retry_tx); | 981 | ath_drain_all_txq(sc, retry_tx); |
979 | 982 | ||
980 | spin_lock_bh(&sc->sc_pcu_lock); | ||
981 | |||
982 | ath_stoprecv(sc); | 983 | ath_stoprecv(sc); |
983 | ath_flushrecv(sc); | 984 | ath_flushrecv(sc); |
984 | 985 | ||
@@ -991,8 +992,6 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) | |||
991 | ath_print(common, ATH_DBG_FATAL, | 992 | ath_print(common, ATH_DBG_FATAL, |
992 | "Unable to start recv logic\n"); | 993 | "Unable to start recv logic\n"); |
993 | 994 | ||
994 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
995 | |||
996 | /* | 995 | /* |
997 | * We may be doing a reset in response to a request | 996 | * We may be doing a reset in response to a request |
998 | * that changes the channel so update any state that | 997 | * that changes the channel so update any state that |
@@ -1017,6 +1016,7 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) | |||
1017 | } | 1016 | } |
1018 | 1017 | ||
1019 | ieee80211_wake_queues(hw); | 1018 | ieee80211_wake_queues(hw); |
1019 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
1020 | 1020 | ||
1021 | /* Start ANI */ | 1021 | /* Start ANI */ |
1022 | ath_start_ani(common); | 1022 | ath_start_ani(common); |
@@ -1381,25 +1381,25 @@ static void ath9k_stop(struct ieee80211_hw *hw) | |||
1381 | ath9k_btcoex_timer_pause(sc); | 1381 | ath9k_btcoex_timer_pause(sc); |
1382 | } | 1382 | } |
1383 | 1383 | ||
1384 | spin_lock_bh(&sc->sc_pcu_lock); | ||
1385 | |||
1384 | /* make sure h/w will not generate any interrupt | 1386 | /* make sure h/w will not generate any interrupt |
1385 | * before setting the invalid flag. */ | 1387 | * before setting the invalid flag. */ |
1386 | ath9k_hw_disable_interrupts(ah); | 1388 | ath9k_hw_disable_interrupts(ah); |
1387 | 1389 | ||
1388 | if (!(sc->sc_flags & SC_OP_INVALID)) { | 1390 | if (!(sc->sc_flags & SC_OP_INVALID)) { |
1389 | ath_drain_all_txq(sc, false); | 1391 | ath_drain_all_txq(sc, false); |
1390 | spin_lock_bh(&sc->sc_pcu_lock); | ||
1391 | ath_stoprecv(sc); | 1392 | ath_stoprecv(sc); |
1392 | ath9k_hw_phy_disable(ah); | 1393 | ath9k_hw_phy_disable(ah); |
1393 | spin_unlock_bh(&sc->sc_pcu_lock); | 1394 | } else |
1394 | } else { | ||
1395 | spin_lock_bh(&sc->sc_pcu_lock); | ||
1396 | sc->rx.rxlink = NULL; | 1395 | sc->rx.rxlink = NULL; |
1397 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
1398 | } | ||
1399 | 1396 | ||
1400 | /* disable HAL and put h/w to sleep */ | 1397 | /* disable HAL and put h/w to sleep */ |
1401 | ath9k_hw_disable(ah); | 1398 | ath9k_hw_disable(ah); |
1402 | ath9k_hw_configpcipowersave(ah, 1, 1); | 1399 | ath9k_hw_configpcipowersave(ah, 1, 1); |
1400 | |||
1401 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
1402 | |||
1403 | ath9k_ps_restore(sc); | 1403 | ath9k_ps_restore(sc); |
1404 | 1404 | ||
1405 | /* Finally, put the chip in FULL SLEEP mode */ | 1405 | /* Finally, put the chip in FULL SLEEP mode */ |
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index d97b7a336b5b..2bc422eb80c7 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c | |||
@@ -1148,13 +1148,11 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) | |||
1148 | ath_print(common, ATH_DBG_FATAL, | 1148 | ath_print(common, ATH_DBG_FATAL, |
1149 | "Failed to stop TX DMA. Resetting hardware!\n"); | 1149 | "Failed to stop TX DMA. Resetting hardware!\n"); |
1150 | 1150 | ||
1151 | spin_lock_bh(&sc->sc_pcu_lock); | ||
1152 | r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false); | 1151 | r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false); |
1153 | if (r) | 1152 | if (r) |
1154 | ath_print(common, ATH_DBG_FATAL, | 1153 | ath_print(common, ATH_DBG_FATAL, |
1155 | "Unable to reset hardware; reset status %d\n", | 1154 | "Unable to reset hardware; reset status %d\n", |
1156 | r); | 1155 | r); |
1157 | spin_unlock_bh(&sc->sc_pcu_lock); | ||
1158 | } | 1156 | } |
1159 | 1157 | ||
1160 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { | 1158 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { |