ath9k: improve reliability of the noise floor calibration
authorFelix Fietkau <nbd@openwrt.org>
Sun, 11 Jul 2010 11:56:18 +0000 (11:56 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 11 Jul 2010 11:56:18 +0000 (11:56 +0000)
SVN-Revision: 22126

package/mac80211/patches/526-ath9k_improve_nf_cal.patch [new file with mode: 0644]

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 (file)
index 0000000..2bc11a1
--- /dev/null
@@ -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;