diff options
author | Luis R. Rodriguez <lrodriguez@atheros.com> | 2009-03-23 19:03:29 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-05-02 10:24:49 -0700 |
commit | 7a1ed3d6dccfd8a5c485c8396397f307ca26bb8f (patch) | |
tree | 982450cedc5f8da944f54f1718178dbb299a7c46 | |
parent | 606624763556fd32a8d68796ce9ef03ae57df5e1 (diff) | |
download | lwn-7a1ed3d6dccfd8a5c485c8396397f307ca26bb8f.tar.gz lwn-7a1ed3d6dccfd8a5c485c8396397f307ca26bb8f.zip |
ath9k: implement IO serialization
This is a port of:
commit SHA1 6158425be398936af1fd04451f78ffad01529cb0
for 2.6.27
All 802.11n PCI devices (Cardbus, PCI, mini-PCI) require
serialization of IO when on non-uniprocessor systems. PCI
express devices not not require this.
This should fix our only last standing open ath9k kernel.org
bugzilla bug report:
http://bugzilla.kernel.org/show_bug.cgi?id=12110
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/net/wireless/ath9k/ath9k.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/core.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/core.h | 33 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/hw.c | 19 |
4 files changed, 55 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index d1b0fbae5a32..8ccf3742423f 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -591,8 +591,8 @@ struct ath9k_country_entry { u8 iso[3]; }; -#define REG_WRITE(_ah, _reg, _val) iowrite32(_val, _ah->ah_sh + _reg) -#define REG_READ(_ah, _reg) ioread32(_ah->ah_sh + _reg) +#define REG_WRITE(_ah, _reg, _val) ath9k_iowrite32((_ah), (_reg), (_val)) +#define REG_READ(_ah, _reg) ath9k_ioread32((_ah), (_reg)) #define SM(_v, _f) (((_v) << _f##_S) & _f) #define MS(_v, _f) (((_v) & _f) >> _f##_S) diff --git a/drivers/net/wireless/ath9k/core.c b/drivers/net/wireless/ath9k/core.c index 87e37bc39145..e50ba6d90fc5 100644 --- a/drivers/net/wireless/ath9k/core.c +++ b/drivers/net/wireless/ath9k/core.c @@ -1120,6 +1120,7 @@ int ath_init(u16 devid, struct ath_softc *sc) sc->sc_cachelsz = csz << 2; /* convert to bytes */ spin_lock_init(&sc->sc_resetlock); + spin_lock_init(&sc->sc_serial_rw); ah = ath9k_hw_attach(devid, sc, sc->mem, &status); if (ah == NULL) { diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index 88f4cc3cb826..51ef315af552 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h @@ -1022,6 +1022,7 @@ struct ath_softc { spinlock_t sc_rxbuflock; spinlock_t sc_txbuflock; spinlock_t sc_resetlock; + spinlock_t sc_serial_rw; spinlock_t node_lock; }; @@ -1069,4 +1070,36 @@ void ath_skb_unmap_single(struct ath_softc *sc, void ath_mcast_merge(struct ath_softc *sc, u32 mfilt[2]); enum ath9k_ht_macmode ath_cwm_macmode(struct ath_softc *sc); +/* + * Read and write, they both share the same lock. We do this to serialize + * reads and writes on Atheros 802.11n PCI devices only. This is required + * as the FIFO on these devices can only accept sanely 2 requests. After + * that the device goes bananas. Serializing the reads/writes prevents this + * from happening. + */ + +static inline void ath9k_iowrite32(struct ath_hal *ah, u32 reg_offset, u32 val) +{ + if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); + iowrite32(val, ah->ah_sc->mem + reg_offset); + spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); + } else + iowrite32(val, ah->ah_sc->mem + reg_offset); +} + +static inline unsigned int ath9k_ioread32(struct ath_hal *ah, u32 reg_offset) +{ + u32 val; + if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&ah->ah_sc->sc_serial_rw, flags); + val = ioread32(ah->ah_sc->mem + reg_offset); + spin_unlock_irqrestore(&ah->ah_sc->sc_serial_rw, flags); + } else + val = ioread32(ah->ah_sc->mem + reg_offset); + return val; +} + #endif /* CORE_H */ diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 69120b534f49..4a35266fad73 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -369,6 +369,25 @@ static void ath9k_hw_set_defaults(struct ath_hal *ah) } ah->ah_config.intr_mitigation = 0; + + /* + * We need this for PCI devices only (Cardbus, PCI, miniPCI) + * _and_ if on non-uniprocessor systems (Multiprocessor/HT). + * This means we use it for all AR5416 devices, and the few + * minor PCI AR9280 devices out there. + * + * Serialization is required because these devices do not handle + * well the case of two concurrent reads/writes due to the latency + * involved. During one read/write another read/write can be issued + * on another CPU while the previous read/write may still be working + * on our hardware, if we hit this case the hardware poops in a loop. + * We prevent this by serializing reads and writes. + * + * This issue is not present on PCI-Express devices or pre-AR5416 + * devices (legacy, 802.11abg). + */ + if (num_possible_cpus() > 1) + ah->ah_config.serialize_regmode = SER_REG_MODE_AUTO; } static inline void ath9k_hw_override_ini(struct ath_hal *ah, |