|
@@ -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
|
|
|
*/
|