From b28b2b95906a1f0cbc246fcbbdf9a5808a1b79d8 Mon Sep 17 00:00:00 2001
From: Felix Fietkau <nbd@openwrt.org>
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