summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElias Oltmanns <eo@nebensachen.de>2008-11-12 11:28:39 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2008-11-20 14:54:42 -0800
commitcfce2256fc569c37640eb2ea3204c9b1f02aef4f (patch)
tree98d5e5c373d3c598b7a594a622aebb2e15c0f9e3
parentc72a32e1819c184c16008ab772d414fd2107575b (diff)
ath5k: fix suspend-related oops on rmmod
Cumulative patch backporting the following two commits from upstream: commit 8bdd5b9c6bd53add260756b6673a0545fbdbba21 upstream Author: Bob Copeland <me@bobcopeland.com> Based on a patch by Elias Oltmanns, we call ath5k_init in resume even if we didn't previously open the device. Besides starting up the device unnecessarily, this also causes an oops on rmmod because mac80211 will not invoke ath5k_stop and softirqs are left running after the module has been unloaded. Add a new state bit, ATH_STAT_STARTED, to indicate that we have been started up. commit bc1b32d6bdd2d6f3fbee9a7c01c9b099f11c579c upstream Author: Elias Oltmanns <eo@nebensachen.de> After a s2ram / resume cycle, resetting the key cache does not work unless it is deferred until after the hardware has been reinitialised by a call to ath5k_hw_reset(). This fixes a regression introduced by "ath5k: fix suspend-related oops on rmmod". Reported-by: Toralf Förster <toralf.foerster@gmx.de> Signed-off-by: Elias Oltmanns <eo@nebensachen.de> Signed-off-by: Bob Copeland <me@bobcopeland.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/net/wireless/ath5k/base.c67
-rw-r--r--drivers/net/wireless/ath5k/base.h3
2 files changed, 36 insertions, 34 deletions
diff --git a/drivers/net/wireless/ath5k/base.c b/drivers/net/wireless/ath5k/base.c
index 0676c6d84383..a2258273fd2e 100644
--- a/drivers/net/wireless/ath5k/base.c
+++ b/drivers/net/wireless/ath5k/base.c
@@ -294,9 +294,9 @@ static inline u64 ath5k_extend_tsf(struct ath5k_hw *ah, u32 rstamp)
}
/* Interrupt handling */
-static int ath5k_init(struct ath5k_softc *sc);
+static int ath5k_init(struct ath5k_softc *sc, bool is_resume);
static int ath5k_stop_locked(struct ath5k_softc *sc);
-static int ath5k_stop_hw(struct ath5k_softc *sc);
+static int ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend);
static irqreturn_t ath5k_intr(int irq, void *dev_id);
static void ath5k_tasklet_reset(unsigned long data);
@@ -584,7 +584,7 @@ ath5k_pci_suspend(struct pci_dev *pdev, pm_message_t state)
ath5k_led_off(sc);
- ath5k_stop_hw(sc);
+ ath5k_stop_hw(sc, true);
free_irq(pdev->irq, sc);
pci_save_state(pdev);
@@ -599,8 +599,7 @@ ath5k_pci_resume(struct pci_dev *pdev)
{
struct ieee80211_hw *hw = pci_get_drvdata(pdev);
struct ath5k_softc *sc = hw->priv;
- struct ath5k_hw *ah = sc->ah;
- int i, err;
+ int err;
pci_restore_state(pdev);
@@ -621,21 +620,11 @@ ath5k_pci_resume(struct pci_dev *pdev)
goto err_no_irq;
}
- err = ath5k_init(sc);
+ err = ath5k_init(sc, true);
if (err)
goto err_irq;
ath5k_led_enable(sc);
- /*
- * Reset the key cache since some parts do not
- * reset the contents on initial power up or resume.
- *
- * FIXME: This may need to be revisited when mac80211 becomes
- * aware of suspend/resume.
- */
- for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
- ath5k_hw_reset_key(ah, i);
-
return 0;
err_irq:
free_irq(pdev->irq, sc);
@@ -657,7 +646,6 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
struct ath5k_softc *sc = hw->priv;
struct ath5k_hw *ah = sc->ah;
u8 mac[ETH_ALEN];
- unsigned int i;
int ret;
ATH5K_DBG(sc, ATH5K_DEBUG_ANY, "devid 0x%x\n", pdev->device);
@@ -676,13 +664,6 @@ ath5k_attach(struct pci_dev *pdev, struct ieee80211_hw *hw)
__set_bit(ATH_STAT_MRRETRY, sc->status);
/*
- * Reset the key cache since some parts do not
- * reset the contents on initial power up.
- */
- for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
- ath5k_hw_reset_key(ah, i);
-
- /*
* Collect the channel list. The 802.11 layer
* is resposible for filtering this list based
* on settings like the phy mode and regulatory
@@ -2197,12 +2178,18 @@ ath5k_beacon_config(struct ath5k_softc *sc)
\********************/
static int
-ath5k_init(struct ath5k_softc *sc)
+ath5k_init(struct ath5k_softc *sc, bool is_resume)
{
- int ret;
+ struct ath5k_hw *ah = sc->ah;
+ int ret, i;
mutex_lock(&sc->lock);
+ if (is_resume && !test_bit(ATH_STAT_STARTED, sc->status))
+ goto out_ok;
+
+ __clear_bit(ATH_STAT_STARTED, sc->status);
+
ATH5K_DBG(sc, ATH5K_DEBUG_RESET, "mode %d\n", sc->opmode);
/*
@@ -2220,7 +2207,7 @@ ath5k_init(struct ath5k_softc *sc)
*/
sc->curchan = sc->hw->conf.channel;
sc->curband = &sc->sbands[sc->curchan->band];
- ret = ath5k_hw_reset(sc->ah, sc->opmode, sc->curchan, false);
+ ret = ath5k_hw_reset(ah, sc->opmode, sc->curchan, false);
if (ret) {
ATH5K_ERR(sc, "unable to reset hardware: %d\n", ret);
goto done;
@@ -2229,7 +2216,14 @@ ath5k_init(struct ath5k_softc *sc)
* This is needed only to setup initial state
* but it's best done after a reset.
*/
- ath5k_hw_set_txpower_limit(sc->ah, 0);
+ ath5k_hw_set_txpower_limit(ah, 0);
+
+ /*
+ * Reset the key cache since some parts do not reset the
+ * contents on initial power up or resume from suspend.
+ */
+ for (i = 0; i < AR5K_KEYTABLE_SIZE; i++)
+ ath5k_hw_reset_key(ah, i);
/*
* Setup the hardware after reset: the key cache
@@ -2249,13 +2243,17 @@ ath5k_init(struct ath5k_softc *sc)
AR5K_INT_RXORN | AR5K_INT_FATAL | AR5K_INT_GLOBAL |
AR5K_INT_MIB;
- ath5k_hw_set_intr(sc->ah, sc->imask);
+ ath5k_hw_set_intr(ah, sc->imask);
+
+ __set_bit(ATH_STAT_STARTED, sc->status);
+
/* Set ack to be sent at low bit-rates */
- ath5k_hw_set_ack_bitrate_high(sc->ah, false);
+ ath5k_hw_set_ack_bitrate_high(ah, false);
mod_timer(&sc->calib_tim, round_jiffies(jiffies +
msecs_to_jiffies(ath5k_calinterval * 1000)));
+out_ok:
ret = 0;
done:
mmiowb();
@@ -2310,7 +2308,7 @@ ath5k_stop_locked(struct ath5k_softc *sc)
* stop is preempted).
*/
static int
-ath5k_stop_hw(struct ath5k_softc *sc)
+ath5k_stop_hw(struct ath5k_softc *sc, bool is_suspend)
{
int ret;
@@ -2341,6 +2339,9 @@ ath5k_stop_hw(struct ath5k_softc *sc)
}
}
ath5k_txbuf_free(sc, sc->bbuf);
+ if (!is_suspend)
+ __clear_bit(ATH_STAT_STARTED, sc->status);
+
mmiowb();
mutex_unlock(&sc->lock);
@@ -2719,12 +2720,12 @@ err:
static int ath5k_start(struct ieee80211_hw *hw)
{
- return ath5k_init(hw->priv);
+ return ath5k_init(hw->priv, false);
}
static void ath5k_stop(struct ieee80211_hw *hw)
{
- ath5k_stop_hw(hw->priv);
+ ath5k_stop_hw(hw->priv, false);
}
static int ath5k_add_interface(struct ieee80211_hw *hw,
diff --git a/drivers/net/wireless/ath5k/base.h b/drivers/net/wireless/ath5k/base.h
index 7ec2f377d5c7..214a565dd58d 100644
--- a/drivers/net/wireless/ath5k/base.h
+++ b/drivers/net/wireless/ath5k/base.h
@@ -132,11 +132,12 @@ struct ath5k_softc {
size_t desc_len; /* size of TX/RX descriptors */
u16 cachelsz; /* cache line size */
- DECLARE_BITMAP(status, 4);
+ DECLARE_BITMAP(status, 5);
#define ATH_STAT_INVALID 0 /* disable hardware accesses */
#define ATH_STAT_MRRETRY 1 /* multi-rate retry support */
#define ATH_STAT_PROMISC 2
#define ATH_STAT_LEDSOFT 3 /* enable LED gpio status */
+#define ATH_STAT_STARTED 4 /* opened & irqs enabled */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
unsigned int curmode; /* current phy mode */