ath5k: Add Spur filter support on newer chips

* Add spur filter support for RF5413 and later chips

 Signed-off-by: Nick Kossifidis <mickflemm@gmail.com>
 Signed-off-by: Bob Copeland <me@bobcopeland.com>

Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Nick Kossifidis 2009-04-30 15:55:50 -04:00 committed by John W. Linville
parent 2bed03ebf6
commit 57e6c56dbb
3 changed files with 270 additions and 25 deletions

View file

@ -1278,6 +1278,11 @@ extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *chann
/* PHY calibration */
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);
/* Spur mitigation */
bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
struct ieee80211_channel *channel);
void ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
struct ieee80211_channel *channel);
/* Misc PHY functions */
extern u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan);
extern int ath5k_hw_phy_disable(struct ath5k_hw *ah);

View file

@ -1353,6 +1353,257 @@ int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
return ret;
}
/***************************\
* Spur mitigation functions *
\***************************/
bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
struct ieee80211_channel *channel)
{
u8 refclk_freq;
if ((ah->ah_radio == AR5K_RF5112) ||
(ah->ah_radio == AR5K_RF5413) ||
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
refclk_freq = 40;
else
refclk_freq = 32;
if ((channel->center_freq % refclk_freq != 0) &&
((channel->center_freq % refclk_freq < 10) ||
(channel->center_freq % refclk_freq > 22)))
return true;
else
return false;
}
void
ath5k_hw_set_spur_mitigation_filter(struct ath5k_hw *ah,
struct ieee80211_channel *channel)
{
struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom;
u32 mag_mask[4] = {0, 0, 0, 0};
u32 pilot_mask[2] = {0, 0};
/* Note: fbin values are scaled up by 2 */
u16 spur_chan_fbin, chan_fbin, symbol_width, spur_detection_window;
s32 spur_delta_phase, spur_freq_sigma_delta;
s32 spur_offset, num_symbols_x16;
u8 num_symbol_offsets, i, freq_band;
/* Convert current frequency to fbin value (the same way channels
* are stored on EEPROM, check out ath5k_eeprom_bin2freq) and scale
* up by 2 so we can compare it later */
if (channel->hw_value & CHANNEL_2GHZ) {
chan_fbin = (channel->center_freq - 2300) * 10;
freq_band = AR5K_EEPROM_BAND_2GHZ;
} else {
chan_fbin = (channel->center_freq - 4900) * 10;
freq_band = AR5K_EEPROM_BAND_5GHZ;
}
/* Check if any spur_chan_fbin from EEPROM is
* within our current channel's spur detection range */
spur_chan_fbin = AR5K_EEPROM_NO_SPUR;
spur_detection_window = AR5K_SPUR_CHAN_WIDTH;
/* XXX: Half/Quarter channels ?*/
if (channel->hw_value & CHANNEL_TURBO)
spur_detection_window *= 2;
for (i = 0; i < AR5K_EEPROM_N_SPUR_CHANS; i++) {
spur_chan_fbin = ee->ee_spur_chans[i][freq_band];
/* Note: mask cleans AR5K_EEPROM_NO_SPUR flag
* so it's zero if we got nothing from EEPROM */
if (spur_chan_fbin == AR5K_EEPROM_NO_SPUR) {
spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
break;
}
if ((chan_fbin - spur_detection_window <=
(spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK)) &&
(chan_fbin + spur_detection_window >=
(spur_chan_fbin & AR5K_EEPROM_SPUR_CHAN_MASK))) {
spur_chan_fbin &= AR5K_EEPROM_SPUR_CHAN_MASK;
break;
}
}
/* We need to enable spur filter for this channel */
if (spur_chan_fbin) {
spur_offset = spur_chan_fbin - chan_fbin;
/*
* Calculate deltas:
* spur_freq_sigma_delta -> spur_offset / sample_freq << 21
* spur_delta_phase -> spur_offset / chip_freq << 11
* Note: Both values have 100KHz resolution
*/
/* XXX: Half/Quarter rate channels ? */
switch (channel->hw_value) {
case CHANNEL_A:
/* Both sample_freq and chip_freq are 40MHz */
spur_delta_phase = (spur_offset << 17) / 25;
spur_freq_sigma_delta = (spur_delta_phase >> 10);
symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
break;
case CHANNEL_G:
/* sample_freq -> 40MHz chip_freq -> 44MHz
* (for b compatibility) */
spur_freq_sigma_delta = (spur_offset << 8) / 55;
spur_delta_phase = (spur_offset << 17) / 25;
symbol_width = AR5K_SPUR_SYMBOL_WIDTH_BASE_100Hz;
break;
case CHANNEL_T:
case CHANNEL_TG:
/* Both sample_freq and chip_freq are 80MHz */
spur_delta_phase = (spur_offset << 16) / 25;
spur_freq_sigma_delta = (spur_delta_phase >> 10);
symbol_width = AR5K_SPUR_SYMBOL_WIDTH_TURBO_100Hz;
break;
default:
return;
}
/* Calculate pilot and magnitude masks */
/* Scale up spur_offset by 1000 to switch to 100HZ resolution
* and divide by symbol_width to find how many symbols we have
* Note: number of symbols is scaled up by 16 */
num_symbols_x16 = ((spur_offset * 1000) << 4) / symbol_width;
/* Spur is on a symbol if num_symbols_x16 % 16 is zero */
if (!(num_symbols_x16 & 0xF))
/* _X_ */
num_symbol_offsets = 3;
else
/* _xx_ */
num_symbol_offsets = 4;
for (i = 0; i < num_symbol_offsets; i++) {
/* Calculate pilot mask */
s32 curr_sym_off =
(num_symbols_x16 / 16) + i + 25;
/* Pilot magnitude mask seems to be a way to
* declare the boundaries for our detection
* window or something, it's 2 for the middle
* value(s) where the symbol is expected to be
* and 1 on the boundary values */
u8 plt_mag_map =
(i == 0 || i == (num_symbol_offsets - 1))
? 1 : 2;
if (curr_sym_off >= 0 && curr_sym_off <= 32) {
if (curr_sym_off <= 25)
pilot_mask[0] |= 1 << curr_sym_off;
else if (curr_sym_off >= 27)
pilot_mask[0] |= 1 << (curr_sym_off - 1);
} else if (curr_sym_off >= 33 && curr_sym_off <= 52)
pilot_mask[1] |= 1 << (curr_sym_off - 33);
/* Calculate magnitude mask (for viterbi decoder) */
if (curr_sym_off >= -1 && curr_sym_off <= 14)
mag_mask[0] |=
plt_mag_map << (curr_sym_off + 1) * 2;
else if (curr_sym_off >= 15 && curr_sym_off <= 30)
mag_mask[1] |=
plt_mag_map << (curr_sym_off - 15) * 2;
else if (curr_sym_off >= 31 && curr_sym_off <= 46)
mag_mask[2] |=
plt_mag_map << (curr_sym_off - 31) * 2;
else if (curr_sym_off >= 46 && curr_sym_off <= 53)
mag_mask[3] |=
plt_mag_map << (curr_sym_off - 47) * 2;
}
/* Write settings on hw to enable spur filter */
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
AR5K_PHY_BIN_MASK_CTL_RATE, 0xff);
/* XXX: Self correlator also ? */
AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_IQ,
AR5K_PHY_IQ_PILOT_MASK_EN |
AR5K_PHY_IQ_CHAN_MASK_EN |
AR5K_PHY_IQ_SPUR_FILT_EN);
/* Set delta phase and freq sigma delta */
ath5k_hw_reg_write(ah,
AR5K_REG_SM(spur_delta_phase,
AR5K_PHY_TIMING_11_SPUR_DELTA_PHASE) |
AR5K_REG_SM(spur_freq_sigma_delta,
AR5K_PHY_TIMING_11_SPUR_FREQ_SD) |
AR5K_PHY_TIMING_11_USE_SPUR_IN_AGC,
AR5K_PHY_TIMING_11);
/* Write pilot masks */
ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_7);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
AR5K_PHY_TIMING_8_PILOT_MASK_2,
pilot_mask[1]);
ath5k_hw_reg_write(ah, pilot_mask[0], AR5K_PHY_TIMING_9);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
AR5K_PHY_TIMING_10_PILOT_MASK_2,
pilot_mask[1]);
/* Write magnitude masks */
ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK_1);
ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK_2);
ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK_3);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
AR5K_PHY_BIN_MASK_CTL_MASK_4,
mag_mask[3]);
ath5k_hw_reg_write(ah, mag_mask[0], AR5K_PHY_BIN_MASK2_1);
ath5k_hw_reg_write(ah, mag_mask[1], AR5K_PHY_BIN_MASK2_2);
ath5k_hw_reg_write(ah, mag_mask[2], AR5K_PHY_BIN_MASK2_3);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
AR5K_PHY_BIN_MASK2_4_MASK_4,
mag_mask[3]);
} else if (ath5k_hw_reg_read(ah, AR5K_PHY_IQ) &
AR5K_PHY_IQ_SPUR_FILT_EN) {
/* Clean up spur mitigation settings and disable fliter */
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
AR5K_PHY_BIN_MASK_CTL_RATE, 0);
AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_IQ,
AR5K_PHY_IQ_PILOT_MASK_EN |
AR5K_PHY_IQ_CHAN_MASK_EN |
AR5K_PHY_IQ_SPUR_FILT_EN);
ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_11);
/* Clear pilot masks */
ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_7);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_8,
AR5K_PHY_TIMING_8_PILOT_MASK_2,
0);
ath5k_hw_reg_write(ah, 0, AR5K_PHY_TIMING_9);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_TIMING_10,
AR5K_PHY_TIMING_10_PILOT_MASK_2,
0);
/* Clear magnitude masks */
ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_1);
ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_2);
ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK_3);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK_CTL,
AR5K_PHY_BIN_MASK_CTL_MASK_4,
0);
ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_1);
ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_2);
ath5k_hw_reg_write(ah, 0, AR5K_PHY_BIN_MASK2_3);
AR5K_REG_WRITE_BITS(ah, AR5K_PHY_BIN_MASK2_4,
AR5K_PHY_BIN_MASK2_4_MASK_4,
0);
}
}
/********************\
Misc PHY functions
\********************/
int ath5k_hw_phy_disable(struct ath5k_hw *ah)
{
ATH5K_TRACE(ah->ah_sc);
@ -1362,10 +1613,6 @@ int ath5k_hw_phy_disable(struct ath5k_hw *ah)
return 0;
}
/********************\
Misc PHY functions
\********************/
/*
* Get the PHY Chip revision
*/

View file

@ -536,26 +536,6 @@ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
return;
}
static bool ath5k_hw_chan_has_spur_noise(struct ath5k_hw *ah,
struct ieee80211_channel *channel)
{
u8 refclk_freq;
if ((ah->ah_radio == AR5K_RF5112) ||
(ah->ah_radio == AR5K_RF5413) ||
(ah->ah_mac_version == (AR5K_SREV_AR2417 >> 4)))
refclk_freq = 40;
else
refclk_freq = 32;
if ((channel->center_freq % refclk_freq != 0) &&
((channel->center_freq % refclk_freq < 10) ||
(channel->center_freq % refclk_freq > 22)))
return true;
else
return false;
}
/* TODO: Half/Quarter rate */
static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
struct ieee80211_channel *channel)
@ -998,7 +978,7 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
ath5k_hw_tweak_initval_settings(ah, channel);
/*
* Set TX power (FIXME)
* Set TX power
*/
ret = ath5k_hw_txpower(ah, channel, ee_mode,
ah->ah_txpower.txp_max_pwr / 2);
@ -1024,9 +1004,22 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode,
/* Write OFDM timings on 5212*/
if (ah->ah_version == AR5K_AR5212 &&
channel->hw_value & CHANNEL_OFDM) {
struct ath5k_eeprom_info *ee =
&ah->ah_capabilities.cap_eeprom;
ret = ath5k_hw_write_ofdm_timings(ah, channel);
if (ret)
return ret;
/* Note: According to docs we can have a newer
* EEPROM on old hardware, so we need to verify
* that our hardware is new enough to have spur
* mitigation registers (delta phase etc) */
if (ah->ah_mac_srev >= AR5K_SREV_AR5424 ||
(ah->ah_mac_srev >= AR5K_SREV_AR5424 &&
ee->ee_version >= AR5K_EEPROM_VERSION_5_3))
ath5k_hw_set_spur_mitigation_filter(ah,
channel);
}
/*Enable/disable 802.11b mode on 5111