ath5k: Add Spur filter support on newer chips
authorNick Kossifidis <mick@madwifi-project.org>
Thu, 30 Apr 2009 19:55:50 +0000 (15:55 -0400)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 6 May 2009 19:14:56 +0000 (15:14 -0400)
* 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>
drivers/net/wireless/ath/ath5k/ath5k.h
drivers/net/wireless/ath/ath5k/phy.c
drivers/net/wireless/ath/ath5k/reset.c

index 33da290..8137182 100644 (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);
index fd93c4e..6737ba0 100644 (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
  */
index 0e075bc..c1862f8 100644 (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