Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 170988
b: refs/heads/master
c: e5e2647
h: refs/heads/master
v: v3
  • Loading branch information
Bob Copeland authored and John W. Linville committed Oct 27, 2009
1 parent cb01697 commit 649806e
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 81 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: e307fcf0a10f9c0c21b3d8b2ff7862b29796cc7f
refs/heads/master: e5e2647fd6ceef2cdc479954b84517535eb7febd
13 changes: 13 additions & 0 deletions trunk/drivers/net/wireless/ath/ath5k/ath5k.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,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
Expand Down Expand Up @@ -1006,6 +1007,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
Expand Down Expand Up @@ -1112,6 +1121,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;

Expand Down Expand Up @@ -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 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,
Expand Down
2 changes: 2 additions & 0 deletions trunk/drivers/net/wireless/ath/ath5k/attach.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ int 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);

Expand Down
185 changes: 128 additions & 57 deletions trunk/drivers/net/wireless/ath/ath5k/phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
* Enable noise floor calibration
*/
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_AGCCTL,
AR5K_PHY_AGCCTL_NF);
/* 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");

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;
return;
}

/* 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;
}
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;
}

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;
/* 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;
}

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);
}

/*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1360,7 +1431,7 @@ static int ath5k_hw_rf511x_calibrate(struct ath5k_hw *ah,
* 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);
Expand Down
11 changes: 4 additions & 7 deletions trunk/drivers/net/wireless/ath/ath5k/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -2033,17 +2033,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

/*
Expand Down
17 changes: 1 addition & 16 deletions trunk/drivers/net/wireless/ath/ath5k/reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
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- */
Expand All @@ -1314,21 +1314,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);

Expand Down

0 comments on commit 649806e

Please sign in to comment.