Hi, REX ABERT wrote:
> Still up and running 3.5 days without a Noise Floor Calibration Timeout. Kernel team: please consider the following patches for squeeze: 9d332c82b4cf ath5k: initialize default noise floor e5e2647fd6ce ath5k: use noise calibration from madwifi hal The former is for the sake of the latter. Rex tested the pair and found it to fix this bug: | The only way to reconnect wireless is to shut down and power off---a | reboot is not sufficient. Unfortunately this is a little too big a change for the upstream stable tree (at least by stable_kernel_rules.txt standards) but it's well tested in ath5k and in madwifi before that. Patch against the kernel packaging repo attached for convenience. Hope that helps, Jonathan
Index: debian/changelog =================================================================== --- debian/changelog (revision 19148) +++ debian/changelog (working copy) @@ -7,6 +7,10 @@ * linux-image: Relax version dependency on linux-base, to simplify testing of bug fixes + [ Jonathan Nieder ] + * ath5k: initialize default noise floor + * ath5k: use noise calibration from madwifi hal (Closes: #611107) + -- Bastian Blank <wa...@debian.org> Mon, 07 May 2012 19:18:05 +0200 linux-2.6 (2.6.32-45) stable; urgency=high Index: debian/patches/bugfix/all/ath5k-initialize-default-noise-floor.patch =================================================================== --- debian/patches/bugfix/all/ath5k-initialize-default-noise-floor.patch (revision 0) +++ debian/patches/bugfix/all/ath5k-initialize-default-noise-floor.patch (working copy) @@ -0,0 +1,32 @@ +From: Bruno Randolf <b...@einfach.org> +Date: Thu, 25 Mar 2010 14:49:31 +0900 +Subject: ath5k: initialize default noise floor + +commit 9d332c82b4cf2e4538450e4af40f073cc5e599ec upstream. + +Initialize noise floor variable with a default of -95. This was used +uninitialized in the signal strength (RSSI -> dBm) conversion until the first +noise floor calibration was completed. + +Signed-off-by: Bruno Randolf <b...@einfach.org> +Signed-off-by: John W. Linville <linvi...@tuxdriver.com> +Signed-off-by: Jonathan Nieder <jrnie...@gmail.com> +--- + drivers/net/wireless/ath/ath5k/attach.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c +index 88663dfe3b3d..b5ea906cd7c8 100644 +--- a/drivers/net/wireless/ath/ath5k/attach.c ++++ b/drivers/net/wireless/ath/ath5k/attach.c +@@ -134,6 +134,7 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc) + ah->ah_limit_tx_retries = AR5K_INIT_TX_RETRY; + ah->ah_software_retry = false; + ah->ah_current_channel = &sc->channels[0]; ++ ah->ah_noise_floor = -95; /* until first NF calibration is run */ + + /* + * Find the mac version +-- +1.7.10 + Index: debian/patches/bugfix/all/ath5k-use-noise-calibration-from-madwifi-hal.patch =================================================================== --- debian/patches/bugfix/all/ath5k-use-noise-calibration-from-madwifi-hal.patch (revision 0) +++ debian/patches/bugfix/all/ath5k-use-noise-calibration-from-madwifi-hal.patch (working copy) @@ -0,0 +1,383 @@ +From: Bob Copeland <m...@bobcopeland.com> +Date: Wed, 14 Oct 2009 14:16:30 -0400 +Subject: ath5k: use noise calibration from madwifi hal + +commit e5e2647fd6ceef2cdc479954b84517535eb7febd upstream. + +This updates ath5k to calibrate the noise floor similar to the +way it is done in the madwifi hal and ath9k. Of note: + +- we start NF measurement at the same time as AGC calibration, + but do not actually read the value until the periodic (long) + calibration +- we keep a history of the last few values read and write the + median back to the hardware for CCA +- we do not complain if NF calibration isn't complete, instead + we keep the last read value. + +Signed-off-by: Bob Copeland <m...@bobcopeland.com> +Acked-by: Nick Kossifidis <mickfl...@gmail.com> +Signed-off-by: John W. Linville <linvi...@tuxdriver.com> +Signed-off-by: Jonathan Nieder <jrnie...@gmail.com> +--- + drivers/net/wireless/ath/ath5k/ath5k.h | 13 +++ + drivers/net/wireless/ath/ath5k/attach.c | 2 + + drivers/net/wireless/ath/ath5k/phy.c | 193 +++++++++++++++++++++---------- + drivers/net/wireless/ath/ath5k/reg.h | 11 +- + drivers/net/wireless/ath/ath5k/reset.c | 17 +-- + 5 files changed, 152 insertions(+), 84 deletions(-) + +diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h +index 2c79c78ef2d7..0601236e27f2 100644 +--- a/drivers/net/wireless/ath/ath5k/ath5k.h ++++ b/drivers/net/wireless/ath/ath5k/ath5k.h +@@ -204,6 +204,7 @@ + #define AR5K_TUNE_CWMAX_11B 1023 + #define AR5K_TUNE_CWMAX_XR 7 + #define AR5K_TUNE_NOISE_FLOOR -72 ++#define AR5K_TUNE_CCA_MAX_GOOD_VALUE -95 + #define AR5K_TUNE_MAX_TXPOWER 63 + #define AR5K_TUNE_DEFAULT_TXPOWER 25 + #define AR5K_TUNE_TPC_TXPOWER false +@@ -1011,6 +1012,14 @@ struct ath5k_capabilities { + } cap_queues; + }; + ++/* size of noise floor history (keep it a power of two) */ ++#define ATH5K_NF_CAL_HIST_MAX 8 ++struct ath5k_nfcal_hist ++{ ++ s16 index; /* current index into nfval */ ++ s16 nfval[ATH5K_NF_CAL_HIST_MAX]; /* last few noise floors */ ++}; ++ + + /***************************************\ + HARDWARE ABSTRACTION LAYER STRUCTURE +@@ -1124,6 +1133,8 @@ struct ath5k_hw { + struct ieee80211_channel r_last_channel; + } ah_radar; + ++ struct ath5k_nfcal_hist ah_nfcal_hist; ++ + /* noise floor from last periodic calibration */ + s32 ah_noise_floor; + +@@ -1287,8 +1298,10 @@ extern int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah); + extern bool ath5k_channel_ok(struct ath5k_hw *ah, u16 freq, unsigned int flags); + extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel); + /* PHY calibration */ ++void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah); + extern int ath5k_hw_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel); + extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); ++extern s16 ath5k_hw_get_noise_floor(struct ath5k_hw *ah); + extern void ath5k_hw_calibration_poll(struct ath5k_hw *ah); + /* Spur mitigation */ + bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, +diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c +index b5ea906cd7c8..591f54a54a46 100644 +--- a/drivers/net/wireless/ath/ath5k/attach.c ++++ b/drivers/net/wireless/ath/ath5k/attach.c +@@ -344,6 +344,8 @@ struct ath5k_hw *ath5k_hw_attach(struct ath5k_softc *sc) + + ath5k_hw_rfgain_opt_init(ah); + ++ ath5k_hw_init_nfcal_hist(ah); ++ + /* turn on HW LEDs */ + ath5k_hw_set_ledstate(ah, AR5K_LED_INIT); + +diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c +index 9d676474839d..721ec5ee381d 100644 +--- a/drivers/net/wireless/ath/ath5k/phy.c ++++ b/drivers/net/wireless/ath/ath5k/phy.c +@@ -1124,77 +1124,148 @@ ath5k_hw_calibration_poll(struct ath5k_hw *ah) + ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION; + AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); + } ++} + ++static int sign_extend(int val, const int nbits) ++{ ++ int order = BIT(nbits-1); ++ return (val ^ order) - order; + } + +-/** +- * ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration +- * +- * @ah: struct ath5k_hw pointer we are operating on +- * @freq: the channel frequency, just used for error logging +- * +- * This function performs a noise floor calibration of the PHY and waits for +- * it to complete. Then the noise floor value is compared to some maximum +- * noise floor we consider valid. +- * +- * Note that this is different from what the madwifi HAL does: it reads the +- * noise floor and afterwards initiates the calibration. Since the noise floor +- * calibration can take some time to finish, depending on the current channel +- * use, that avoids the occasional timeout warnings we are seeing now. +- * +- * See the following link for an Atheros patent on noise floor calibration: +- * http://patft.uspto.gov/netacgi/nph-Parser?Sect1=PTO1&Sect2=HITOFF&d=PALL \ +- * &p=1&u=%2Fnetahtml%2FPTO%2Fsrchnum.htm&r=1&f=G&l=50&s1=7245893.PN.&OS=PN/7 ++static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah) ++{ ++ s32 val; ++ ++ val = ath5k_hw_reg_read(ah, AR5K_PHY_NF); ++ return sign_extend(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 9); ++} ++ ++void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah) ++{ ++ int i; ++ ++ ah->ah_nfcal_hist.index = 0; ++ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) ++ ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE; ++} ++ ++static void ath5k_hw_update_nfcal_hist(struct ath5k_hw *ah, s16 noise_floor) ++{ ++ struct ath5k_nfcal_hist *hist = &ah->ah_nfcal_hist; ++ hist->index = (hist->index + 1) & (ATH5K_NF_CAL_HIST_MAX-1); ++ hist->nfval[hist->index] = noise_floor; ++} ++ ++static s16 ath5k_hw_get_median_noise_floor(struct ath5k_hw *ah) ++{ ++ s16 sort[ATH5K_NF_CAL_HIST_MAX]; ++ s16 tmp; ++ int i, j; ++ ++ memcpy(sort, ah->ah_nfcal_hist.nfval, sizeof(sort)); ++ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX - 1; i++) { ++ for (j = 1; j < ATH5K_NF_CAL_HIST_MAX - i; j++) { ++ if (sort[j] > sort[j-1]) { ++ tmp = sort[j]; ++ sort[j] = sort[j-1]; ++ sort[j-1] = tmp; ++ } ++ } ++ } ++ for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++) { ++ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, ++ "cal %d:%d\n", i, sort[i]); ++ } ++ return sort[(ATH5K_NF_CAL_HIST_MAX-1) / 2]; ++} ++ ++/* ++ * When we tell the hardware to perform a noise floor calibration ++ * by setting the AR5K_PHY_AGCCTL_NF bit, it will periodically ++ * sample-and-hold the minimum noise level seen at the antennas. ++ * This value is then stored in a ring buffer of recently measured ++ * noise floor values so we have a moving window of the last few ++ * samples. + * +- * XXX: Since during noise floor calibration antennas are detached according to +- * the patent, we should stop tx queues here. ++ * The median of the values in the history is then loaded into the ++ * hardware for its own use for RSSI and CCA measurements. + */ +-int +-ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq) ++void ath5k_hw_update_noise_floor(struct ath5k_hw *ah) + { +- int ret; +- unsigned int i; +- s32 noise_floor; ++ struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; ++ u32 val; ++ s16 nf, threshold; ++ u8 ee_mode; ++ ++ /* keep last value if calibration hasn't completed */ ++ if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) { ++ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, ++ "NF did not complete in calibration window\n"); ++ ++ return; ++ } ++ ++ switch (ah->ah_current_channel->hw_value & CHANNEL_MODES) { ++ case CHANNEL_A: ++ case CHANNEL_T: ++ case CHANNEL_XR: ++ ee_mode = AR5K_EEPROM_MODE_11A; ++ break; ++ case CHANNEL_G: ++ case CHANNEL_TG: ++ ee_mode = AR5K_EEPROM_MODE_11G; ++ break; ++ default: ++ case CHANNEL_B: ++ ee_mode = AR5K_EEPROM_MODE_11B; ++ break; ++ } ++ ++ ++ /* completed NF calibration, test threshold */ ++ nf = ath5k_hw_read_measured_noise_floor(ah); ++ threshold = ee->ee_noise_floor_thr[ee_mode]; ++ ++ if (nf > threshold) { ++ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, ++ "noise floor failure detected; " ++ "read %d, threshold %d\n", ++ nf, threshold); ++ ++ nf = AR5K_TUNE_CCA_MAX_GOOD_VALUE; ++ } ++ ++ ath5k_hw_update_nfcal_hist(ah, nf); ++ nf = ath5k_hw_get_median_noise_floor(ah); ++ ++ /* load noise floor (in .5 dBm) so the hardware will use it */ ++ val = ath5k_hw_reg_read(ah, AR5K_PHY_NF) & ~AR5K_PHY_NF_M; ++ val |= (nf * 2) & AR5K_PHY_NF_M; ++ ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); ++ ++ AR5K_REG_MASKED_BITS(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, ++ ~(AR5K_PHY_AGCCTL_NF_EN | AR5K_PHY_AGCCTL_NF_NOUPDATE)); ++ ++ ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, AR5K_PHY_AGCCTL_NF, ++ 0, false); + + /* +- * Enable noise floor calibration ++ * Load a high max CCA Power value (-50 dBm in .5 dBm units) ++ * so that we're not capped by the median we just loaded. ++ * This will be used as the initial value for the next noise ++ * floor calibration. + */ ++ val = (val & ~AR5K_PHY_NF_M) | ((-50 * 2) & AR5K_PHY_NF_M); ++ ath5k_hw_reg_write(ah, val, AR5K_PHY_NF); + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, +- AR5K_PHY_AGCCTL_NF); ++ AR5K_PHY_AGCCTL_NF_EN | ++ AR5K_PHY_AGCCTL_NF_NOUPDATE | ++ AR5K_PHY_AGCCTL_NF); + +- ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, +- AR5K_PHY_AGCCTL_NF, 0, false); +- if (ret) { +- ATH5K_ERR(ah->ah_sc, +- "noise floor calibration timeout (%uMHz)\n", freq); +- return -EAGAIN; +- } ++ ah->ah_noise_floor = nf; + +- /* Wait until the noise floor is calibrated and read the value */ +- for (i = 20; i > 0; i--) { +- mdelay(1); +- noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF); +- noise_floor = AR5K_PHY_NF_RVAL(noise_floor); +- if (noise_floor & AR5K_PHY_NF_ACTIVE) { +- noise_floor = AR5K_PHY_NF_AVAL(noise_floor); +- +- if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) +- break; +- } +- } +- +- ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, +- "noise floor %d\n", noise_floor); +- +- if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { +- ATH5K_ERR(ah->ah_sc, +- "noise floor calibration failed (%uMHz)\n", freq); +- return -EAGAIN; +- } +- +- ah->ah_noise_floor = noise_floor; +- +- return 0; ++ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE, ++ "noise floor calibrated: %d\n", nf); + } + + /* +@@ -1287,7 +1358,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah, + return ret; + } + +- ath5k_hw_noise_floor_calibration(ah, channel->center_freq); ++ ath5k_hw_update_noise_floor(ah); + + /* + * Re-enable RX/TX and beacons +@@ -1360,7 +1431,7 @@ done: + * since noise floor calibration interrupts rx path while I/Q + * calibration doesn't. We don't need to run noise floor calibration + * as often as I/Q calibration.*/ +- ath5k_hw_noise_floor_calibration(ah, channel->center_freq); ++ ath5k_hw_update_noise_floor(ah); + + /* Initiate a gain_F calibration */ + ath5k_hw_request_rfgain_probe(ah); +diff --git a/drivers/net/wireless/ath/ath5k/reg.h b/drivers/net/wireless/ath/ath5k/reg.h +index c63ea6afd96f..186469eb4132 100644 +--- a/drivers/net/wireless/ath/ath5k/reg.h ++++ b/drivers/net/wireless/ath/ath5k/reg.h +@@ -2039,17 +2039,14 @@ + #define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automaticaly */ + + /* +- * PHY noise floor status register ++ * PHY noise floor status register (CCA = Clear Channel Assessment) + */ + #define AR5K_PHY_NF 0x9864 /* Register address */ +-#define AR5K_PHY_NF_M 0x000001ff /* Noise floor mask */ +-#define AR5K_PHY_NF_ACTIVE 0x00000100 /* Noise floor calibration still active */ +-#define AR5K_PHY_NF_RVAL(_n) (((_n) >> 19) & AR5K_PHY_NF_M) +-#define AR5K_PHY_NF_AVAL(_n) (-((_n) ^ AR5K_PHY_NF_M) + 1) +-#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) ++#define AR5K_PHY_NF_M 0x000001ff /* Noise floor, written to hardware in 1/2 dBm units */ ++#define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9)) + #define AR5K_PHY_NF_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */ + #define AR5K_PHY_NF_THRESH62_S 12 +-#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* ??? */ ++#define AR5K_PHY_NF_MINCCA_PWR 0x0ff80000 /* Minimum measured noise level, read from hardware in 1 dBm units */ + #define AR5K_PHY_NF_MINCCA_PWR_S 19 + + /* +diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c +index 257ea18c849f..8fd6ec2e522b 100644 +--- a/drivers/net/wireless/ath/ath5k/reset.c ++++ b/drivers/net/wireless/ath/ath5k/reset.c +@@ -1289,7 +1289,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, + * out and/or noise floor calibration might timeout. + */ + AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, +- AR5K_PHY_AGCCTL_CAL); ++ AR5K_PHY_AGCCTL_CAL | AR5K_PHY_AGCCTL_NF); + + /* At the same time start I/Q calibration for QAM constellation + * -no need for CCK- */ +@@ -1310,21 +1310,6 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, + channel->center_freq); + } + +- /* +- * If we run NF calibration before AGC, it always times out. +- * Binary HAL starts NF and AGC calibration at the same time +- * and only waits for AGC to finish. Also if AGC or NF cal. +- * times out, reset doesn't fail on binary HAL. I believe +- * that's wrong because since rx path is routed to a detector, +- * if cal. doesn't finish we won't have RX. Sam's HAL for AR5210/5211 +- * enables noise floor calibration after offset calibration and if noise +- * floor calibration fails, reset fails. I believe that's +- * a better approach, we just need to find a polling interval +- * that suits best, even if reset continues we need to make +- * sure that rx path is ready. +- */ +- ath5k_hw_noise_floor_calibration(ah, channel->center_freq); +- + /* Restore antenna mode */ + ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); + +-- +1.7.10 + Index: debian/patches/series/46 =================================================================== --- debian/patches/series/46 (revision 0) +++ debian/patches/series/46 (working copy) @@ -0,0 +1,2 @@ ++ bugfix/all/ath5k-initialize-default-noise-floor.patch ++ bugfix/all/ath5k-use-noise-calibration-from-madwifi-hal.patch