ath9k_hw: Enable strong signal detection for AR9003
authorVasanthakumar Thiagarajan <vasanth@atheros.com>
Wed, 10 Nov 2010 13:03:12 +0000 (05:03 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Tue, 16 Nov 2010 21:37:06 +0000 (16:37 -0500)
Attenuation from eeprom is configured into attenuator control
register.

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c

index 17f73e4d8f36e327d7f62e1751812663c9dae0e3..da26d3704cb2b2b87cff131e9a247ce76cf7173d 100644 (file)
@@ -57,6 +57,8 @@
 #define SUB_NUM_CTL_MODES_AT_5G_40 2    /* excluding HT40, EXT-OFDM */
 #define SUB_NUM_CTL_MODES_AT_2G_40 3    /* excluding HT40, EXT-OFDM, EXT-CCK */
 
+static int ar9003_hw_power_interpolate(int32_t x,
+                                      int32_t *px, int32_t *py, u_int16_t np);
 static const struct ar9300_eeprom ar9300_default = {
        .eepromVersion = 2,
        .templateVersion = 2,
@@ -3443,6 +3445,82 @@ static void ar9003_hw_drive_strength_apply(struct ath_hw *ah)
        REG_WRITE(ah, AR_PHY_65NM_CH0_BIAS4, reg);
 }
 
+static u16 ar9003_hw_atten_chain_get(struct ath_hw *ah, int chain,
+                                    struct ath9k_channel *chan)
+{
+       int f[3], t[3];
+       u16 value;
+       struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+
+       if (chain >= 0 && chain < 3) {
+               if (IS_CHAN_2GHZ(chan))
+                       return eep->modalHeader2G.xatten1DB[chain];
+               else if (eep->base_ext2.xatten1DBLow[chain] != 0) {
+                       t[0] = eep->base_ext2.xatten1DBLow[chain];
+                       f[0] = 5180;
+                       t[1] = eep->modalHeader5G.xatten1DB[chain];
+                       f[1] = 5500;
+                       t[2] = eep->base_ext2.xatten1DBHigh[chain];
+                       f[2] = 5785;
+                       value = ar9003_hw_power_interpolate((s32) chan->channel,
+                                                           f, t, 3);
+                       return value;
+               } else
+                       return eep->modalHeader5G.xatten1DB[chain];
+       }
+
+       return 0;
+}
+
+
+static u16 ar9003_hw_atten_chain_get_margin(struct ath_hw *ah, int chain,
+                                           struct ath9k_channel *chan)
+{
+       int f[3], t[3];
+       u16 value;
+       struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+
+       if (chain >= 0 && chain < 3) {
+               if (IS_CHAN_2GHZ(chan))
+                       return eep->modalHeader2G.xatten1Margin[chain];
+               else if (eep->base_ext2.xatten1MarginLow[chain] != 0) {
+                       t[0] = eep->base_ext2.xatten1MarginLow[chain];
+                       f[0] = 5180;
+                       t[1] = eep->modalHeader5G.xatten1Margin[chain];
+                       f[1] = 5500;
+                       t[2] = eep->base_ext2.xatten1MarginHigh[chain];
+                       f[2] = 5785;
+                       value = ar9003_hw_power_interpolate((s32) chan->channel,
+                                                           f, t, 3);
+                       return value;
+               } else
+                       return eep->modalHeader5G.xatten1Margin[chain];
+       }
+
+       return 0;
+}
+
+static void ar9003_hw_atten_apply(struct ath_hw *ah, struct ath9k_channel *chan)
+{
+       int i;
+       u16 value;
+       unsigned long ext_atten_reg[3] = {AR_PHY_EXT_ATTEN_CTL_0,
+                                         AR_PHY_EXT_ATTEN_CTL_1,
+                                         AR_PHY_EXT_ATTEN_CTL_2,
+                                        };
+
+       /* Test value. if 0 then attenuation is unused. Don't load anything. */
+       for (i = 0; i < 3; i++) {
+               value = ar9003_hw_atten_chain_get(ah, i, chan);
+               REG_RMW_FIELD(ah, ext_atten_reg[i],
+                             AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, value);
+
+               value = ar9003_hw_atten_chain_get_margin(ah, i, chan);
+               REG_RMW_FIELD(ah, ext_atten_reg[i],
+                             AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, value);
+       }
+}
+
 static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah)
 {
        int internal_regulator =
@@ -3474,6 +3552,7 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah,
        ar9003_hw_xpa_bias_level_apply(ah, IS_CHAN_2GHZ(chan));
        ar9003_hw_ant_ctrl_apply(ah, IS_CHAN_2GHZ(chan));
        ar9003_hw_drive_strength_apply(ah);
+       ar9003_hw_atten_apply(ah, chan);
        ar9003_hw_internal_regulator_apply(ah);
 }