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:
committed by
John W. Linville
parent
2bed03ebf6
commit
57e6c56dbb
@@ -1278,6 +1278,11 @@ extern int ath5k_hw_channel(struct ath5k_hw *ah, struct ieee80211_channel *chann
|
|||||||
/* PHY calibration */
|
/* PHY calibration */
|
||||||
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);
|
||||||
|
/* 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 */
|
/* Misc PHY functions */
|
||||||
extern u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan);
|
extern u16 ath5k_hw_radio_revision(struct ath5k_hw *ah, unsigned int chan);
|
||||||
extern int ath5k_hw_phy_disable(struct ath5k_hw *ah);
|
extern int ath5k_hw_phy_disable(struct ath5k_hw *ah);
|
||||||
|
@@ -1353,6 +1353,257 @@ int ath5k_hw_phy_calibrate(struct ath5k_hw *ah,
|
|||||||
return ret;
|
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)
|
int ath5k_hw_phy_disable(struct ath5k_hw *ah)
|
||||||
{
|
{
|
||||||
ATH5K_TRACE(ah->ah_sc);
|
ATH5K_TRACE(ah->ah_sc);
|
||||||
@@ -1362,10 +1613,6 @@ int ath5k_hw_phy_disable(struct ath5k_hw *ah)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/********************\
|
|
||||||
Misc PHY functions
|
|
||||||
\********************/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the PHY Chip revision
|
* Get the PHY Chip revision
|
||||||
*/
|
*/
|
||||||
|
@@ -536,26 +536,6 @@ static void ath5k_hw_set_sleep_clock(struct ath5k_hw *ah, bool enable)
|
|||||||
return;
|
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 */
|
/* TODO: Half/Quarter rate */
|
||||||
static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
|
static void ath5k_hw_tweak_initval_settings(struct ath5k_hw *ah,
|
||||||
struct ieee80211_channel *channel)
|
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);
|
ath5k_hw_tweak_initval_settings(ah, channel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set TX power (FIXME)
|
* Set TX power
|
||||||
*/
|
*/
|
||||||
ret = ath5k_hw_txpower(ah, channel, ee_mode,
|
ret = ath5k_hw_txpower(ah, channel, ee_mode,
|
||||||
ah->ah_txpower.txp_max_pwr / 2);
|
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*/
|
/* Write OFDM timings on 5212*/
|
||||||
if (ah->ah_version == AR5K_AR5212 &&
|
if (ah->ah_version == AR5K_AR5212 &&
|
||||||
channel->hw_value & CHANNEL_OFDM) {
|
channel->hw_value & CHANNEL_OFDM) {
|
||||||
|
struct ath5k_eeprom_info *ee =
|
||||||
|
&ah->ah_capabilities.cap_eeprom;
|
||||||
|
|
||||||
ret = ath5k_hw_write_ofdm_timings(ah, channel);
|
ret = ath5k_hw_write_ofdm_timings(ah, channel);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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
|
/*Enable/disable 802.11b mode on 5111
|
||||||
|
Reference in New Issue
Block a user