diff options
Diffstat (limited to 'drivers/net/wireless/ath9k')
-rw-r--r-- | drivers/net/wireless/ath9k/ath9k.h | 33 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/hw.c | 22 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/hw.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 1 |
4 files changed, 57 insertions, 3 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index f0b105a11ae..b64be8e9a69 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -579,6 +579,7 @@ struct ath_softc { void __iomem *mem; int irq; spinlock_t sc_resetlock; + spinlock_t sc_serial_rw; struct mutex mutex; u8 curbssid[ETH_ALEN]; @@ -724,4 +725,36 @@ void ath9k_wiphy_pause_all_forced(struct ath_softc *sc, bool ath9k_wiphy_scanning(struct ath_softc *sc); void ath9k_wiphy_work(struct work_struct *work); +/* + * 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_hw *ah, u32 reg_offset, u32 val) +{ + if (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_hw *ah, u32 reg_offset) +{ + u32 val; + if (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 /* ATH9K_H */ diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index eb750a50399..60e55d8c510 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -391,6 +391,25 @@ static void ath9k_hw_set_defaults(struct ath_hw *ah) } ah->config.intr_mitigation = 1; + + /* + * 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->config.serialize_regmode = SER_REG_MODE_AUTO; } static struct ath_hw *ath9k_hw_newstate(u16 devid, struct ath_softc *sc, @@ -610,7 +629,8 @@ static struct ath_hw *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, } if (ah->config.serialize_regmode == SER_REG_MODE_AUTO) { - if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) { + if (ah->hw_version.macVersion == AR_SREV_VERSION_5416_PCI || + (AR_SREV_9280(ah) && !ah->is_pciexpress)) { ah->config.serialize_regmode = SER_REG_MODE_ON; } else { diff --git a/drivers/net/wireless/ath9k/hw.h b/drivers/net/wireless/ath9k/hw.h index 89936a038da..dc681f011fd 100644 --- a/drivers/net/wireless/ath9k/hw.h +++ b/drivers/net/wireless/ath9k/hw.h @@ -42,8 +42,8 @@ #define AR5416_MAGIC 0x19641014 /* Register read/write primitives */ -#define REG_WRITE(_ah, _reg, _val) iowrite32(_val, _ah->ah_sc->mem + _reg) -#define REG_READ(_ah, _reg) ioread32(_ah->ah_sc->mem + _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/main.c b/drivers/net/wireless/ath9k/main.c index f473fee72a2..a9715f5b0af 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1370,6 +1370,7 @@ static int ath_init(u16 devid, struct ath_softc *sc) spin_lock_init(&sc->wiphy_lock); spin_lock_init(&sc->sc_resetlock); + spin_lock_init(&sc->sc_serial_rw); mutex_init(&sc->mutex); tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, |