| From lrodriguez@atheros.com Tue Apr 28 21:42:36 2009 |
| From: "Luis R. Rodriguez" <lrodriguez@atheros.com> |
| Date: Mon, 23 Mar 2009 19:03:26 -0400 |
| Subject: ath9k: implement IO serialization |
| To: stable@kernel.org |
| Cc: "Luis R. Rodriguez" <lrodriguez@atheros.com>, ath9k-devel@venema.h4ckr.net, linux-wireless@vger.kernel.org |
| Message-ID: <1237849407-17273-1-git-send-email-lrodriguez@atheros.com> |
| |
| From: Luis R. Rodriguez <lrodriguez@atheros.com> |
| |
| 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> |
| --- |
| drivers/net/wireless/ath9k/ath9k.h | 4 ++-- |
| drivers/net/wireless/ath9k/core.c | 1 + |
| drivers/net/wireless/ath9k/core.h | 33 +++++++++++++++++++++++++++++++++ |
| drivers/net/wireless/ath9k/hw.c | 19 +++++++++++++++++++ |
| 4 files changed, 55 insertions(+), 2 deletions(-) |
| |
| --- 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) |
| --- 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_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) { |
| --- 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_s |
| 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 */ |
| --- 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 |
| } |
| |
| 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, |