summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuis R. Rodriguez <lrodriguez@atheros.com>2009-03-23 19:03:26 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2009-05-02 10:57:15 -0700
commit10c5743dc4ee84416a5af7741dfce6ce7c2db0af (patch)
tree1d55227053c299c271bfead183291b0f3f06e1d9
parent7b6d4c8c3d17f59572d254784a770e058dc9d31e (diff)
ath9k: implement IO serialization
This is a port of: commit SHA1 6158425be398936af1fd04451f78ffad01529cb0 for 2.6.28. 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.h4
-rw-r--r--drivers/net/wireless/ath9k/core.c1
-rw-r--r--drivers/net/wireless/ath9k/core.h33
-rw-r--r--drivers/net/wireless/ath9k/hw.c19
4 files changed, 55 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h
index accace5f7efb..86025f6a17ba 100644
--- a/drivers/net/wireless/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath9k/ath9k.h
@@ -590,8 +590,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 c5033f6f42ac..cbe32aa0218a 100644
--- a/drivers/net/wireless/ath9k/core.c
+++ b/drivers/net/wireless/ath9k/core.c
@@ -1089,6 +1089,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 cb3e61e57c4d..9fe718316349 100644
--- a/drivers/net/wireless/ath9k/core.h
+++ b/drivers/net/wireless/ath9k/core.h
@@ -1040,6 +1040,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;
/* LEDs */
@@ -1081,4 +1082,36 @@ void ath_get_currentCountry(struct ath_softc *sc,
struct ath9k_country_entry *ctry);
u64 ath_extend_tsf(struct ath_softc *sc, u32 rstamp);
+/*
+ * 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 98bc25c9b3cf..82ead1b713ec 100644
--- a/drivers/net/wireless/ath9k/hw.c
+++ b/drivers/net/wireless/ath9k/hw.c
@@ -346,6 +346,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 void ath9k_hw_override_ini(struct ath_hal *ah,