Commit e5e2647f authored by Bob Copeland's avatar Bob Copeland Committed by John W. Linville

ath5k: use noise calibration from madwifi hal

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: default avatarBob Copeland <me@bobcopeland.com>
Acked-by: default avatarNick Kossifidis <mickflemm@gmail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent e307fcf0
...@@ -198,6 +198,7 @@ ...@@ -198,6 +198,7 @@
#define AR5K_TUNE_CWMAX_11B 1023 #define AR5K_TUNE_CWMAX_11B 1023
#define AR5K_TUNE_CWMAX_XR 7 #define AR5K_TUNE_CWMAX_XR 7
#define AR5K_TUNE_NOISE_FLOOR -72 #define AR5K_TUNE_NOISE_FLOOR -72
#define AR5K_TUNE_CCA_MAX_GOOD_VALUE -95
#define AR5K_TUNE_MAX_TXPOWER 63 #define AR5K_TUNE_MAX_TXPOWER 63
#define AR5K_TUNE_DEFAULT_TXPOWER 25 #define AR5K_TUNE_DEFAULT_TXPOWER 25
#define AR5K_TUNE_TPC_TXPOWER false #define AR5K_TUNE_TPC_TXPOWER false
...@@ -1006,6 +1007,14 @@ struct ath5k_capabilities { ...@@ -1006,6 +1007,14 @@ struct ath5k_capabilities {
} cap_queues; } 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 HARDWARE ABSTRACTION LAYER STRUCTURE
...@@ -1112,6 +1121,8 @@ struct ath5k_hw { ...@@ -1112,6 +1121,8 @@ struct ath5k_hw {
struct ieee80211_channel r_last_channel; struct ieee80211_channel r_last_channel;
} ah_radar; } ah_radar;
struct ath5k_nfcal_hist ah_nfcal_hist;
/* noise floor from last periodic calibration */ /* noise floor from last periodic calibration */
s32 ah_noise_floor; s32 ah_noise_floor;
...@@ -1274,8 +1285,10 @@ extern int ath5k_hw_rfgain_opt_init(struct ath5k_hw *ah); ...@@ -1274,8 +1285,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 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); extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *channel);
/* PHY calibration */ /* 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_phy_calibrate(struct ath5k_hw *ah, struct ieee80211_channel *channel);
extern int ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq); 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); extern void ath5k_hw_calibration_poll(struct ath5k_hw *ah);
/* Spur mitigation */ /* Spur mitigation */
bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah, bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
......
...@@ -331,6 +331,8 @@ int ath5k_hw_attach(struct ath5k_softc *sc) ...@@ -331,6 +331,8 @@ int ath5k_hw_attach(struct ath5k_softc *sc)
ath5k_hw_rfgain_opt_init(ah); ath5k_hw_rfgain_opt_init(ah);
ath5k_hw_init_nfcal_hist(ah);
/* turn on HW LEDs */ /* turn on HW LEDs */
ath5k_hw_set_ledstate(ah, AR5K_LED_INIT); ath5k_hw_set_ledstate(ah, AR5K_LED_INIT);
......
...@@ -1124,77 +1124,148 @@ ath5k_hw_calibration_poll(struct ath5k_hw *ah) ...@@ -1124,77 +1124,148 @@ ath5k_hw_calibration_poll(struct ath5k_hw *ah)
ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION; ah->ah_swi_mask = AR5K_SWI_FULL_CALIBRATION;
AR5K_REG_ENABLE_BITS(ah, AR5K_CR, AR5K_CR_SWI); 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;
} }
/** static s32 ath5k_hw_read_measured_noise_floor(struct ath5k_hw *ah)
* ath5k_hw_noise_floor_calibration - perform PHY noise floor calibration {
* s32 val;
* @ah: struct ath5k_hw pointer we are operating on
* @freq: the channel frequency, just used for error logging val = ath5k_hw_reg_read(ah, AR5K_PHY_NF);
* return sign_extend(AR5K_REG_MS(val, AR5K_PHY_NF_MINCCA_PWR), 9);
* 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. void ath5k_hw_init_nfcal_hist(struct ath5k_hw *ah)
* {
* Note that this is different from what the madwifi HAL does: it reads the int i;
* noise floor and afterwards initiates the calibration. Since the noise floor
* calibration can take some time to finish, depending on the current channel ah->ah_nfcal_hist.index = 0;
* use, that avoids the occasional timeout warnings we are seeing now. for (i = 0; i < ATH5K_NF_CAL_HIST_MAX; i++)
* ah->ah_nfcal_hist.nfval[i] = AR5K_TUNE_CCA_MAX_GOOD_VALUE;
* 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 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 median of the values in the history is then loaded into the
* the patent, we should stop tx queues here. * hardware for its own use for RSSI and CCA measurements.
*/ */
int void ath5k_hw_update_noise_floor(struct ath5k_hw *ah)
ath5k_hw_noise_floor_calibration(struct ath5k_hw *ah, short freq)
{ {
int ret; struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
unsigned int i; u32 val;
s32 noise_floor; s16 nf, threshold;
u8 ee_mode;
/* /* keep last value if calibration hasn't completed */
* Enable noise floor calibration if (ath5k_hw_reg_read(ah, AR5K_PHY_AGCCTL) & AR5K_PHY_AGCCTL_NF) {
*/ ATH5K_DBG(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, "NF did not complete in calibration window\n");
AR5K_PHY_AGCCTL_NF);
ret = ath5k_hw_register_timeout(ah, AR5K_PHY_AGCCTL, return;
AR5K_PHY_AGCCTL_NF, 0, false);
if (ret) {
ATH5K_ERR(ah->ah_sc,
"noise floor calibration timeout (%uMHz)\n", freq);
return -EAGAIN;
} }
/* Wait until the noise floor is calibrated and read the value */ switch (ah->ah_current_channel->hw_value & CHANNEL_MODES) {
for (i = 20; i > 0; i--) { case CHANNEL_A:
mdelay(1); case CHANNEL_T:
noise_floor = ath5k_hw_reg_read(ah, AR5K_PHY_NF); case CHANNEL_XR:
noise_floor = AR5K_PHY_NF_RVAL(noise_floor); ee_mode = AR5K_EEPROM_MODE_11A;
if (noise_floor & AR5K_PHY_NF_ACTIVE) { break;
noise_floor = AR5K_PHY_NF_AVAL(noise_floor); case CHANNEL_G:
case CHANNEL_TG:
if (noise_floor <= AR5K_TUNE_NOISE_FLOOR) ee_mode = AR5K_EEPROM_MODE_11G;
break; break;
} default:
case CHANNEL_B:
ee_mode = AR5K_EEPROM_MODE_11B;
break;
} }
ATH5K_DBG_UNLIMIT(ah->ah_sc, ATH5K_DEBUG_CALIBRATE,
"noise floor %d\n", noise_floor);
if (noise_floor > AR5K_TUNE_NOISE_FLOOR) { /* completed NF calibration, test threshold */
ATH5K_ERR(ah->ah_sc, nf = ath5k_hw_read_measured_noise_floor(ah);
"noise floor calibration failed (%uMHz)\n", freq); threshold = ee->ee_noise_floor_thr[ee_mode];
return -EAGAIN;
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;
} }
ah->ah_noise_floor = noise_floor; ath5k_hw_update_nfcal_hist(ah, nf);
nf = ath5k_hw_get_median_noise_floor(ah);
return 0; /* 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);
/*
* 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_EN |
AR5K_PHY_AGCCTL_NF_NOUPDATE |
AR5K_PHY_AGCCTL_NF);
ah->ah_noise_floor = nf;
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, ...@@ -1287,7 +1358,7 @@ static int ath5k_hw_rf5110_calibrate(struct ath5k_hw *ah,
return ret; return ret;
} }
ath5k_hw_noise_floor_calibration(ah, channel->center_freq); ath5k_hw_update_noise_floor(ah);
/* /*
* Re-enable RX/TX and beacons * Re-enable RX/TX and beacons
...@@ -1360,7 +1431,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah, ...@@ -1360,7 +1431,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah,
* since noise floor calibration interrupts rx path while I/Q * since noise floor calibration interrupts rx path while I/Q
* calibration doesn't. We don't need to run noise floor calibration * calibration doesn't. We don't need to run noise floor calibration
* as often as I/Q 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 */ /* Initiate a gain_F calibration */
ath5k_hw_request_rfgain_probe(ah); ath5k_hw_request_rfgain_probe(ah);
......
...@@ -2033,17 +2033,14 @@ ...@@ -2033,17 +2033,14 @@
#define AR5K_PHY_AGCCTL_NF_NOUPDATE 0x00020000 /* Don't update nf automaticaly */ #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 0x9864 /* Register address */
#define AR5K_PHY_NF_M 0x000001ff /* Noise floor mask */ #define AR5K_PHY_NF_M 0x000001ff /* Noise floor, written to hardware in 1/2 dBm units */
#define AR5K_PHY_NF_ACTIVE 0x00000100 /* Noise floor calibration still active */ #define AR5K_PHY_NF_SVAL(_n) (((_n) & AR5K_PHY_NF_M) | (1 << 9))
#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_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */ #define AR5K_PHY_NF_THRESH62 0x0007f000 /* Thresh62 -check ANI patent- (field) */
#define AR5K_PHY_NF_THRESH62_S 12 #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 #define AR5K_PHY_NF_MINCCA_PWR_S 19
/* /*
......
...@@ -1293,7 +1293,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, ...@@ -1293,7 +1293,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
* out and/or noise floor calibration might timeout. * out and/or noise floor calibration might timeout.
*/ */
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL, 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 /* At the same time start I/Q calibration for QAM constellation
* -no need for CCK- */ * -no need for CCK- */
...@@ -1314,21 +1314,6 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, ...@@ -1314,21 +1314,6 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
channel->center_freq); 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 */ /* Restore antenna mode */
ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment