From 701c1d15ab8e606bb28ad54ddfb157df50ac68a9 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Tue, 3 Feb 2009 14:12:49 -0800 Subject: [PATCH 1/3] ath9k: add comments for ath9k_hw_configpcipowersave() using for ASPM To: johannes@sipsolutions.net, linville@tuxdriver.com Cc: linux-wireless@vger.kernel.org Signed-off-by: Luis R. Rodriguez --- drivers/net/wireless/ath9k/hw.c | 35 +++++++++++++++++++++++++++++++++++ 1 files changed, 35 insertions(+), 0 deletions(-) diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 7728234..a7bca87 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -2669,6 +2669,15 @@ bool ath9k_hw_setpower(struct ath_hal *ah, return status; } +/* + * Helper for ASPM support. + * + * Disable PLL when in L0s as well as receiver clock when in L1. + * This power saving option must be enabled through the SerDes. + * + * Programming the SerDes must go through the same 288 bit serial shift + * register as the other analog registers. Hence the 9 writes. + */ void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) { struct ath_hal_5416 *ahp = AH5416(ah); @@ -2677,13 +2686,20 @@ void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) if (ah->ah_isPciExpress != true) return; + /* Do not touch SERDES registers */ if (ah->ah_config.pcie_powersave_enable == 2) return; + /* Nothing to do on restore for 11N */ if (restore) return; if (AR_SREV_9280_20_OR_LATER(ah)) { + /* + * AR9280 2.0 or later chips use SerDes values from the + * initvals.h initialized depending on chipset during + * ath9k_hw_do_attach() + */ for (i = 0; i < ahp->ah_iniPcieSerdes.ia_rows; i++) { REG_WRITE(ah, INI_RA(&ahp->ah_iniPcieSerdes, i, 0), INI_RA(&ahp->ah_iniPcieSerdes, i, 1)); @@ -2694,10 +2710,12 @@ void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fd00); REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + /* RX shut off when elecidle is asserted */ REG_WRITE(ah, AR_PCIE_SERDES, 0xa8000019); REG_WRITE(ah, AR_PCIE_SERDES, 0x13160820); REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980560); + /* Shut off CLKREQ active in L1 */ if (ah->ah_config.pcie_clock_req) REG_WRITE(ah, AR_PCIE_SERDES, 0x401deffc); else @@ -2707,29 +2725,46 @@ void ath9k_hw_configpcipowersave(struct ath_hal *ah, int restore) REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); REG_WRITE(ah, AR_PCIE_SERDES, 0x00043007); + /* Load the new settings */ REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); udelay(1000); } else { REG_WRITE(ah, AR_PCIE_SERDES, 0x9248fc00); REG_WRITE(ah, AR_PCIE_SERDES, 0x24924924); + + /* RX shut off when elecidle is asserted */ REG_WRITE(ah, AR_PCIE_SERDES, 0x28000039); REG_WRITE(ah, AR_PCIE_SERDES, 0x53160824); REG_WRITE(ah, AR_PCIE_SERDES, 0xe5980579); + + /* + * Ignore ah->ah_config.pcie_clock_req setting for + * pre-AR9280 11n + */ REG_WRITE(ah, AR_PCIE_SERDES, 0x001defff); + REG_WRITE(ah, AR_PCIE_SERDES, 0x1aaabe40); REG_WRITE(ah, AR_PCIE_SERDES, 0xbe105554); REG_WRITE(ah, AR_PCIE_SERDES, 0x000e3007); + + /* Load the new settings */ REG_WRITE(ah, AR_PCIE_SERDES2, 0x00000000); } + /* set bit 19 to allow forcing of pcie core into L1 state */ REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + /* Several PCIe massages to ensure proper behaviour */ if (ah->ah_config.pcie_waen) { REG_WRITE(ah, AR_WA, ah->ah_config.pcie_waen); } else { if (AR_SREV_9285(ah)) REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); + /* + * On AR9280 chips bit 22 of 0x4004 needs to be set to + * otherwise card may disappear. + */ else if (AR_SREV_9280(ah)) REG_WRITE(ah, AR_WA, AR9280_WA_DEFAULT); else -- 1.6.1.2.253.ga34a From 7da7588343da16b5049df2e13757619afc7bc95f Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Tue, 3 Feb 2009 16:20:50 -0800 Subject: [PATCH 2/3] ath9k: enable serialization of register writes on AR9280 PCI To: johannes@sipsolutions.net, linville@tuxdriver.com Cc: linux-wireless@vger.kernel.org AR9280 PCI devices (non-PCIe) require serialization of register writes as well. Signed-off-by: Luis R. Rodriguez --- drivers/net/wireless/ath9k/hw.c | 15 ++++++++++++++- 1 files changed, 14 insertions(+), 1 deletions(-) diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index a7bca87..01c79b6 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c @@ -617,8 +617,21 @@ static struct ath_hal *ath9k_hw_do_attach(u16 devid, struct ath_softc *sc, goto bad; } + /* + * Serialization of Register Accesses: + * + * AR5416 and AR9280 PCI based-based cards can have issues with + * platforms that do not serialize PCI register accesses correctly. + * + * Enable serialization of register accesses when the following + * are true: + * + * - device requires serialization (e.g. AR5416 2.2 and PCI/miniPCI) + * - Multiprocessor/HT system + */ if (ah->ah_config.serialize_regmode == SER_REG_MODE_AUTO) { - if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) { + if (ah->ah_macVersion == AR_SREV_VERSION_5416_PCI || + (AR_SREV_9280(ah) && !ah->ah_isPciExpress)) { ah->ah_config.serialize_regmode = SER_REG_MODE_ON; } else { -- 1.6.1.2.253.ga34a From 9ee9c8eae57f589a5f1e471673e0b504780fda54 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Tue, 3 Feb 2009 16:59:47 -0800 Subject: [PATCH 3/3] ath9k: implement serialized writes To: johannes@sipsolutions.net, linville@tuxdriver.com Cc: linux-wireless@vger.kernel.org This implements serialization of writes on devices which require it. Signed-off-by: Luis R. Rodriguez --- drivers/net/wireless/ath9k/ath9k.h | 14 +++++++++++++- drivers/net/wireless/ath9k/core.h | 1 + drivers/net/wireless/ath9k/main.c | 1 + 3 files changed, 15 insertions(+), 1 deletions(-) diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 5289d28..6a39c3f 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h @@ -578,7 +578,19 @@ struct ath9k_country_entry { u8 iso[3]; }; -#define REG_WRITE(_ah, _reg, _val) iowrite32(_val, _ah->ah_sh + _reg) +#define REG_WRITE(_ah, _reg, _val) do { \ + if (ah->ah_config.serialize_regmode == SER_REG_MODE_ON) { \ + unsigned long serialize_flags; \ + spin_lock_irqsave(&_ah->ah_sc->sc_serial_write, \ + serialize_flags); \ + iowrite32(_val, _ah->ah_sh + _reg); \ + spin_unlock_irqrestore(&_ah->ah_sc->sc_serial_write, \ + serialize_flags); \ + } \ + else \ + iowrite32(_val, _ah->ah_sh + _reg); \ + } while (0) + #define REG_READ(_ah, _reg) ioread32(_ah->ah_sh + _reg) #define SM(_v, _f) (((_v) << _f##_S) & _f) diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index 29251f8..0f37e30 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h @@ -693,6 +693,7 @@ struct ath_softc { void __iomem *mem; int irq; spinlock_t sc_resetlock; + spinlock_t sc_serial_write; struct mutex mutex; u8 sc_curbssid[ETH_ALEN]; diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index d8e8266..f762216 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c @@ -1320,6 +1320,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_write); mutex_init(&sc->mutex); tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet, -- 1.6.1.2.253.ga34a