aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2010-07-11 11:56:18 +0000
committerFelix Fietkau <nbd@openwrt.org>2010-07-11 11:56:18 +0000
commitb28b2b95906a1f0cbc246fcbbdf9a5808a1b79d8 (patch)
treef966cdbb07a9957c807fe007513c970f42e9fd1e
parent581dfd912c86050b73d69241fcd618cf1b85339f (diff)
downloadupstream-b28b2b95906a1f0cbc246fcbbdf9a5808a1b79d8.tar.gz
upstream-b28b2b95906a1f0cbc246fcbbdf9a5808a1b79d8.tar.bz2
upstream-b28b2b95906a1f0cbc246fcbbdf9a5808a1b79d8.zip
ath9k: improve reliability of the noise floor calibration
SVN-Revision: 22126
-rw-r--r--package/mac80211/patches/526-ath9k_improve_nf_cal.patch399
1 files changed, 399 insertions, 0 deletions
diff --git a/package/mac80211/patches/526-ath9k_improve_nf_cal.patch b/package/mac80211/patches/526-ath9k_improve_nf_cal.patch
new file mode 100644
index 0000000000..2bc11a1d20
--- /dev/null
+++ b/package/mac80211/patches/526-ath9k_improve_nf_cal.patch
@@ -0,0 +1,399 @@
+--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+@@ -1518,77 +1518,6 @@ static void ar5008_hw_do_getnf(struct at
+ nfarray[5] = sign_extend(nf, 9);
+ }
+
+-static void ar5008_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
+-{
+- struct ath9k_nfcal_hist *h;
+- int i, j;
+- int32_t val;
+- const u32 ar5416_cca_regs[6] = {
+- AR_PHY_CCA,
+- AR_PHY_CH1_CCA,
+- AR_PHY_CH2_CCA,
+- AR_PHY_EXT_CCA,
+- AR_PHY_CH1_EXT_CCA,
+- AR_PHY_CH2_EXT_CCA
+- };
+- u8 chainmask, rx_chain_status;
+-
+- rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK);
+- if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
+- chainmask = 0x9;
+- else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
+- if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4))
+- chainmask = 0x1B;
+- else
+- chainmask = 0x09;
+- } else {
+- if (rx_chain_status & 0x4)
+- chainmask = 0x3F;
+- else if (rx_chain_status & 0x2)
+- chainmask = 0x1B;
+- else
+- chainmask = 0x09;
+- }
+-
+- h = ah->nfCalHist;
+-
+- for (i = 0; i < NUM_NF_READINGS; i++) {
+- if (chainmask & (1 << i)) {
+- val = REG_READ(ah, ar5416_cca_regs[i]);
+- val &= 0xFFFFFE00;
+- val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
+- REG_WRITE(ah, ar5416_cca_regs[i], val);
+- }
+- }
+-
+- REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+- AR_PHY_AGC_CONTROL_ENABLE_NF);
+- REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+- AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+- REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+-
+- for (j = 0; j < 5; j++) {
+- if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
+- AR_PHY_AGC_CONTROL_NF) == 0)
+- break;
+- udelay(50);
+- }
+-
+- ENABLE_REGWRITE_BUFFER(ah);
+-
+- for (i = 0; i < NUM_NF_READINGS; i++) {
+- if (chainmask & (1 << i)) {
+- val = REG_READ(ah, ar5416_cca_regs[i]);
+- val &= 0xFFFFFE00;
+- val |= (((u32) (-50) << 1) & 0x1ff);
+- REG_WRITE(ah, ar5416_cca_regs[i], val);
+- }
+- }
+-
+- REGWRITE_BUFFER_FLUSH(ah);
+- DISABLE_REGWRITE_BUFFER(ah);
+-}
+-
+ /*
+ * Initialize the ANI register values with default (ini) values.
+ * This routine is called during a (full) hardware reset after
+@@ -1666,6 +1595,14 @@ static void ar5008_hw_set_nf_limits(stru
+ void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
+ {
+ struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
++ const u32 ar5416_cca_regs[6] = {
++ AR_PHY_CCA,
++ AR_PHY_CH1_CCA,
++ AR_PHY_CH2_CCA,
++ AR_PHY_EXT_CCA,
++ AR_PHY_CH1_EXT_CCA,
++ AR_PHY_CH2_EXT_CCA
++ };
+
+ priv_ops->rf_set_freq = ar5008_hw_set_channel;
+ priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate;
+@@ -1685,7 +1622,6 @@ void ar5008_hw_attach_phy_ops(struct ath
+ priv_ops->restore_chainmask = ar5008_restore_chainmask;
+ priv_ops->set_diversity = ar5008_set_diversity;
+ priv_ops->do_getnf = ar5008_hw_do_getnf;
+- priv_ops->loadnf = ar5008_hw_loadnf;
+
+ if (modparam_force_new_ani) {
+ priv_ops->ani_control = ar5008_hw_ani_control_new;
+@@ -1701,4 +1637,5 @@ void ar5008_hw_attach_phy_ops(struct ath
+ priv_ops->compute_pll_control = ar5008_hw_compute_pll_control;
+
+ ar5008_hw_set_nf_limits(ah);
++ memcpy(ah->nf_regs, ar5416_cca_regs, sizeof(ah->nf_regs));
+ }
+--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+@@ -1050,106 +1050,6 @@ static void ar9003_hw_set_nf_limits(stru
+ }
+
+ /*
+- * Find out which of the RX chains are enabled
+- */
+-static u32 ar9003_hw_get_rx_chainmask(struct ath_hw *ah)
+-{
+- u32 chain = REG_READ(ah, AR_PHY_RX_CHAINMASK);
+- /*
+- * The bits [2:0] indicate the rx chain mask and are to be
+- * interpreted as follows:
+- * 00x => Only chain 0 is enabled
+- * 01x => Chain 1 and 0 enabled
+- * 1xx => Chain 2,1 and 0 enabled
+- */
+- return chain & 0x7;
+-}
+-
+-static void ar9003_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
+-{
+- struct ath9k_nfcal_hist *h;
+- unsigned i, j;
+- int32_t val;
+- const u32 ar9300_cca_regs[6] = {
+- AR_PHY_CCA_0,
+- AR_PHY_CCA_1,
+- AR_PHY_CCA_2,
+- AR_PHY_EXT_CCA,
+- AR_PHY_EXT_CCA_1,
+- AR_PHY_EXT_CCA_2,
+- };
+- u8 chainmask, rx_chain_status;
+- struct ath_common *common = ath9k_hw_common(ah);
+-
+- rx_chain_status = ar9003_hw_get_rx_chainmask(ah);
+-
+- chainmask = 0x3F;
+- h = ah->nfCalHist;
+-
+- for (i = 0; i < NUM_NF_READINGS; i++) {
+- if (chainmask & (1 << i)) {
+- val = REG_READ(ah, ar9300_cca_regs[i]);
+- val &= 0xFFFFFE00;
+- val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
+- REG_WRITE(ah, ar9300_cca_regs[i], val);
+- }
+- }
+-
+- /*
+- * Load software filtered NF value into baseband internal minCCApwr
+- * variable.
+- */
+- REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+- AR_PHY_AGC_CONTROL_ENABLE_NF);
+- REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
+- AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
+- REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+-
+- /*
+- * Wait for load to complete, should be fast, a few 10s of us.
+- * The max delay was changed from an original 250us to 10000us
+- * since 250us often results in NF load timeout and causes deaf
+- * condition during stress testing 12/12/2009
+- */
+- for (j = 0; j < 1000; j++) {
+- if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
+- AR_PHY_AGC_CONTROL_NF) == 0)
+- break;
+- udelay(10);
+- }
+-
+- /*
+- * We timed out waiting for the noisefloor to load, probably due to an
+- * in-progress rx. Simply return here and allow the load plenty of time
+- * to complete before the next calibration interval. We need to avoid
+- * trying to load -50 (which happens below) while the previous load is
+- * still in progress as this can cause rx deafness. Instead by returning
+- * here, the baseband nf cal will just be capped by our present
+- * noisefloor until the next calibration timer.
+- */
+- if (j == 1000) {
+- ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf "
+- "to load: AR_PHY_AGC_CONTROL=0x%x\n",
+- REG_READ(ah, AR_PHY_AGC_CONTROL));
+- return;
+- }
+-
+- /*
+- * Restore maxCCAPower register parameter again so that we're not capped
+- * by the median we just loaded. This will be initial (and max) value
+- * of next noise floor calibration the baseband does.
+- */
+- for (i = 0; i < NUM_NF_READINGS; i++) {
+- if (chainmask & (1 << i)) {
+- val = REG_READ(ah, ar9300_cca_regs[i]);
+- val &= 0xFFFFFE00;
+- val |= (((u32) (-50) << 1) & 0x1ff);
+- REG_WRITE(ah, ar9300_cca_regs[i], val);
+- }
+- }
+-}
+-
+-/*
+ * Initialize the ANI register values with default (ini) values.
+ * This routine is called during a (full) hardware reset after
+ * all the registers are initialised from the INI.
+@@ -1216,6 +1116,14 @@ static void ar9003_hw_ani_cache_ini_regs
+ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
+ {
+ struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
++ const u32 ar9300_cca_regs[6] = {
++ AR_PHY_CCA_0,
++ AR_PHY_CCA_1,
++ AR_PHY_CCA_2,
++ AR_PHY_EXT_CCA,
++ AR_PHY_EXT_CCA_1,
++ AR_PHY_EXT_CCA_2,
++ };
+
+ priv_ops->rf_set_freq = ar9003_hw_set_channel;
+ priv_ops->spur_mitigate_freq = ar9003_hw_spur_mitigate;
+@@ -1232,10 +1140,10 @@ void ar9003_hw_attach_phy_ops(struct ath
+ priv_ops->set_diversity = ar9003_hw_set_diversity;
+ priv_ops->ani_control = ar9003_hw_ani_control;
+ priv_ops->do_getnf = ar9003_hw_do_getnf;
+- priv_ops->loadnf = ar9003_hw_loadnf;
+ priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs;
+
+ ar9003_hw_set_nf_limits(ah);
++ memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs));
+ }
+
+ void ar9003_hw_bb_watchdog_config(struct ath_hw *ah)
+--- a/drivers/net/wireless/ath/ath9k/calib.c
++++ b/drivers/net/wireless/ath/ath9k/calib.c
+@@ -167,6 +167,100 @@ void ath9k_hw_start_nfcal(struct ath_hw
+ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
+ }
+
++void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
++{
++ struct ath9k_nfcal_hist *h;
++ unsigned i, j;
++ int32_t val;
++ u8 chainmask;
++ struct ath_common *common = ath9k_hw_common(ah);
++
++ if (AR_SREV_9300_20_OR_LATER(ah))
++ chainmask = 0x3F;
++ else if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
++ chainmask = 0x9;
++ else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
++ if ((ah->rxchainmask & 0x2) || (ah->rxchainmask & 0x4))
++ chainmask = 0x1B;
++ else
++ chainmask = 0x09;
++ } else {
++ if (ah->rxchainmask & 0x4)
++ chainmask = 0x3F;
++ else if (ah->rxchainmask & 0x2)
++ chainmask = 0x1B;
++ else
++ chainmask = 0x09;
++ }
++ h = ah->nfCalHist;
++
++ for (i = 0; i < NUM_NF_READINGS; i++) {
++ if (chainmask & (1 << i)) {
++ val = REG_READ(ah, ah->nf_regs[i]);
++ val &= 0xFFFFFE00;
++ val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
++ REG_WRITE(ah, ah->nf_regs[i], val);
++ }
++ }
++
++ /*
++ * Load software filtered NF value into baseband internal minCCApwr
++ * variable.
++ */
++ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
++ AR_PHY_AGC_CONTROL_ENABLE_NF);
++ REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
++ AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
++ REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
++
++ /*
++ * Wait for load to complete, should be fast, a few 10s of us.
++ * The max delay was changed from an original 250us to 10000us
++ * since 250us often results in NF load timeout and causes deaf
++ * condition during stress testing 12/12/2009
++ */
++ for (j = 0; j < 1000; j++) {
++ if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
++ AR_PHY_AGC_CONTROL_NF) == 0)
++ break;
++ udelay(10);
++ }
++
++ /*
++ * We timed out waiting for the noisefloor to load, probably due to an
++ * in-progress rx. Simply return here and allow the load plenty of time
++ * to complete before the next calibration interval. We need to avoid
++ * trying to load -50 (which happens below) while the previous load is
++ * still in progress as this can cause rx deafness. Instead by returning
++ * here, the baseband nf cal will just be capped by our present
++ * noisefloor until the next calibration timer.
++ */
++ if (j == 1000) {
++ ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf "
++ "to load: AR_PHY_AGC_CONTROL=0x%x\n",
++ REG_READ(ah, AR_PHY_AGC_CONTROL));
++ return;
++ }
++
++ /*
++ * Restore maxCCAPower register parameter again so that we're not capped
++ * by the median we just loaded. This will be initial (and max) value
++ * of next noise floor calibration the baseband does.
++ */
++ ENABLE_REGWRITE_BUFFER(ah);
++ for (i = 0; i < NUM_NF_READINGS; i++) {
++ if (chainmask & (1 << i)) {
++ val = REG_READ(ah, ah->nf_regs[i]);
++ val &= 0xFFFFFE00;
++ val |= (((u32) (-50) << 1) & 0x1ff);
++ REG_WRITE(ah, ah->nf_regs[i], val);
++ }
++ }
++ REGWRITE_BUFFER_FLUSH(ah);
++ DISABLE_REGWRITE_BUFFER(ah);
++}
++
++
+ static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
+ {
+ struct ath_common *common = ath9k_hw_common(ah);
+--- a/drivers/net/wireless/ath/ath9k/calib.h
++++ b/drivers/net/wireless/ath/ath9k/calib.h
+@@ -109,6 +109,7 @@ struct ath9k_pacal_info{
+
+ bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
+ void ath9k_hw_start_nfcal(struct ath_hw *ah);
++void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
+ int16_t ath9k_hw_getnf(struct ath_hw *ah,
+ struct ath9k_channel *chan);
+ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah);
+--- a/drivers/net/wireless/ath/ath9k/hw-ops.h
++++ b/drivers/net/wireless/ath/ath9k/hw-ops.h
+@@ -264,12 +264,6 @@ static inline void ath9k_hw_do_getnf(str
+ ath9k_hw_private_ops(ah)->do_getnf(ah, nfarray);
+ }
+
+-static inline void ath9k_hw_loadnf(struct ath_hw *ah,
+- struct ath9k_channel *chan)
+-{
+- ath9k_hw_private_ops(ah)->loadnf(ah, chan);
+-}
+-
+ static inline bool ath9k_hw_init_cal(struct ath_hw *ah,
+ struct ath9k_channel *chan)
+ {
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -510,7 +510,6 @@ struct ath_gen_timer_table {
+ * AR_RTC_PLL_CONTROL for a given channel
+ * @setup_calibration: set up calibration
+ * @iscal_supported: used to query if a type of calibration is supported
+- * @loadnf: load noise floor read from each chain on the CCA registers
+ *
+ * @ani_reset: reset ANI parameters to default values
+ * @ani_lower_immunity: lower the noise immunity level. The level controls
+@@ -564,7 +563,6 @@ struct ath_hw_private_ops {
+ bool (*ani_control)(struct ath_hw *ah, enum ath9k_ani_cmd cmd,
+ int param);
+ void (*do_getnf)(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]);
+- void (*loadnf)(struct ath_hw *ah, struct ath9k_channel *chan);
+
+ /* ANI */
+ void (*ani_reset)(struct ath_hw *ah, bool is_scanning);
+@@ -658,6 +656,7 @@ struct ath_hw {
+ bool need_an_top2_fixup;
+ u16 tx_trig_level;
+
++ u32 nf_regs[6];
+ struct ath_nf_limits nf_2g;
+ struct ath_nf_limits nf_5g;
+ u16 rfsilent;