ath9k: implement IO serialization
authorLuis R. Rodriguez <lrodriguez@atheros.com>
Thu, 12 Mar 2009 22:18:49 +0000 (18:18 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 16 Mar 2009 22:01:58 +0000 (18:01 -0400)
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

A port is probably required to older kernels and I can work on
that.

Cc: stable@kernel.org
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath9k/ath9k.h
drivers/net/wireless/ath9k/core.h
drivers/net/wireless/ath9k/hw.c
drivers/net/wireless/ath9k/main.c

index d278135..6650f60 100644 (file)
@@ -587,8 +587,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)
index 4ca2aed..139566c 100644 (file)
@@ -701,6 +701,7 @@ struct ath_softc {
        struct ath_hal *sc_ah;
        void __iomem *mem;
        spinlock_t sc_resetlock;
+       spinlock_t sc_serial_rw;
        struct mutex mutex;
 
        u8 sc_curbssid[ETH_ALEN];
@@ -751,4 +752,36 @@ int ath_get_hal_qnum(u16 queue, struct ath_softc *sc);
 int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc);
 int ath_cabq_update(struct ath_softc *);
 
+/*
+ * 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 */
index 34474ed..5c87043 100644 (file)
@@ -437,6 +437,25 @@ static void ath9k_hw_set_defaults(struct ath_hal *ah)
        }
 
        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->ah_config.serialize_regmode = SER_REG_MODE_AUTO;
 }
 
 static struct ath_hal_5416 *ath9k_hw_newstate(u16 devid,
index 0e80990..3c04044 100644 (file)
@@ -1336,6 +1336,7 @@ static int ath_init(u16 devid, struct ath_softc *sc)
                printk(KERN_ERR "Unable to create debugfs files\n");
 
        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, ath9k_beacon_tasklet,