From b28b2b95906a1f0cbc246fcbbdf9a5808a1b79d8 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 11 Jul 2010 11:56:18 +0000 Subject: [PATCH] ath9k: improve reliability of the noise floor calibration SVN-Revision: 22126 --- .../patches/526-ath9k_improve_nf_cal.patch | 399 ++++++++++++++++++ 1 file changed, 399 insertions(+) create mode 100644 package/mac80211/patches/526-ath9k_improve_nf_cal.patch 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; -- 2.30.2