diff options
author | Sujith Manoharan <c_manoha@qca.qualcomm.com> | 2014-10-16 22:10:12 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-10-27 14:16:14 -0400 |
commit | ae2ff23930d78ff619852c47a4b89aef5a26ef2b (patch) | |
tree | 79962d5c0efa616634d6967dc10fee8f95c8b30f /drivers/net/wireless/ath/ath9k/main.c | |
parent | 5555c955249b0d942e8ae066e80a24237b16e7f8 (diff) |
ath9k: Set ATH_OP_HW_RESET before HW reset
When a HW reset is done, the interrupt tasklet is
disabled before ISRs are disabled in the HW. This
allows a small window where the HW can still generate
interrupts. Since the tasklet is disabled and not killed,
it is not scheduled but deferred for execution at a later
time.
This happens because ATH_OP_HW_RESET is not set when ath_reset()
is called. When the hw_reset_work workqueue is used, this
problem doesn't arise because ATH_OP_HW_RESET is set
and the ISR bails out.
Set ATH_OP_HW_RESET properly in ath_reset() to avoid
this race - all the ath_reset_internal() callers have
been converted to use ath_reset() in the previous patch.
Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/main.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/main.c | 12 |
1 files changed, 12 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index eb6ba9f8d9ff..dda09ba321d7 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c | |||
@@ -598,10 +598,17 @@ chip_reset: | |||
598 | #undef SCHED_INTR | 598 | #undef SCHED_INTR |
599 | } | 599 | } |
600 | 600 | ||
601 | /* | ||
602 | * This function is called when a HW reset cannot be deferred | ||
603 | * and has to be immediate. | ||
604 | */ | ||
601 | int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan) | 605 | int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan) |
602 | { | 606 | { |
607 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | ||
603 | int r; | 608 | int r; |
604 | 609 | ||
610 | set_bit(ATH_OP_HW_RESET, &common->op_flags); | ||
611 | |||
605 | ath9k_ps_wakeup(sc); | 612 | ath9k_ps_wakeup(sc); |
606 | r = ath_reset_internal(sc, hchan); | 613 | r = ath_reset_internal(sc, hchan); |
607 | ath9k_ps_restore(sc); | 614 | ath9k_ps_restore(sc); |
@@ -609,6 +616,11 @@ int ath_reset(struct ath_softc *sc, struct ath9k_channel *hchan) | |||
609 | return r; | 616 | return r; |
610 | } | 617 | } |
611 | 618 | ||
619 | /* | ||
620 | * When a HW reset can be deferred, it is added to the | ||
621 | * hw_reset_work workqueue, but we set ATH_OP_HW_RESET before | ||
622 | * queueing. | ||
623 | */ | ||
612 | void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type) | 624 | void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type) |
613 | { | 625 | { |
614 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); | 626 | struct ath_common *common = ath9k_hw_common(sc->sc_ah); |