ath9k_hw: fix PA predistortion training power selection
authorFelix Fietkau <nbd@openwrt.org>
Mon, 13 Dec 2010 07:40:54 +0000 (08:40 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 13 Dec 2010 20:23:33 +0000 (15:23 -0500)
The EEPROM contains scale factors for the tx power, which define
the range of allowable difference between target power and training
power. If the difference is too big, PA predistortion cannot be used.
For 2.4 GHz there is only one scale factor, for 5 GHz there are
three, depending on the specific frequency range.

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
drivers/net/wireless/ath/ath9k/ar9003_paprd.c
drivers/net/wireless/ath/ath9k/ar9003_phy.h
drivers/net/wireless/ath/ath9k/hw.h
drivers/net/wireless/ath/ath9k/main.c

index 5ad37d05a85d4d9731d8c93bc0d10dd70f43bdc0..4149ffb6d54a846aa9133d52fb13fd68c71defa8 100644 (file)
@@ -4798,6 +4798,19 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah,
        /* Write target power array to registers */
        ar9003_hw_tx_power_regwrite(ah, targetPowerValT2);
        ar9003_hw_calibration_apply(ah, chan->channel);
+
+       if (IS_CHAN_2GHZ(chan)) {
+               if (IS_CHAN_HT40(chan))
+                       i = ALL_TARGET_HT40_0_8_16;
+               else
+                       i = ALL_TARGET_HT20_0_8_16;
+       } else {
+               if (IS_CHAN_HT40(chan))
+                       i = ALL_TARGET_HT40_7;
+               else
+                       i = ALL_TARGET_HT20_7;
+       }
+       ah->paprd_target_power = targetPowerValT2[i];
 }
 
 static u16 ath9k_hw_ar9300_get_spur_channel(struct ath_hw *ah,
index cdca4c3265b914ac9937444f1bac369cfe5f30a2..69f779237d28a1a8a1a59cc25e1753c32c948778 100644 (file)
@@ -30,8 +30,68 @@ void ar9003_paprd_enable(struct ath_hw *ah, bool val)
 }
 EXPORT_SYMBOL(ar9003_paprd_enable);
 
-static void ar9003_paprd_setup_single_table(struct ath_hw *ah)
+static int ar9003_get_training_power_2g(struct ath_hw *ah)
 {
+       struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+       struct ar9300_modal_eep_header *hdr = &eep->modalHeader2G;
+       unsigned int power, scale, delta;
+
+       scale = MS(le32_to_cpu(hdr->papdRateMaskHt20), AR9300_PAPRD_SCALE_1);
+       power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5,
+                              AR_PHY_POWERTX_RATE5_POWERTXHT20_0);
+
+       delta = abs((int) ah->paprd_target_power - (int) power);
+       if (delta > scale)
+               return -1;
+
+       if (delta < 4)
+               power -= 4 - delta;
+
+       return power;
+}
+
+static int get_streams(int mask)
+{
+       return !!(mask & BIT(0)) + !!(mask & BIT(1)) + !!(mask & BIT(2));
+}
+
+static int ar9003_get_training_power_5g(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
+       struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
+       struct ar9300_modal_eep_header *hdr = &eep->modalHeader5G;
+       struct ath9k_channel *chan = ah->curchan;
+       unsigned int power, scale, delta;
+
+       if (chan->channel >= 5700)
+               scale = MS(le32_to_cpu(hdr->papdRateMaskHt20),
+                          AR9300_PAPRD_SCALE_1);
+       else if (chan->channel >= 5400)
+               scale = MS(le32_to_cpu(hdr->papdRateMaskHt40),
+                          AR9300_PAPRD_SCALE_2);
+       else
+               scale = MS(le32_to_cpu(hdr->papdRateMaskHt40),
+                          AR9300_PAPRD_SCALE_1);
+
+       if (IS_CHAN_HT40(chan))
+               power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE8,
+                       AR_PHY_POWERTX_RATE8_POWERTXHT40_5);
+       else
+               power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE6,
+                       AR_PHY_POWERTX_RATE6_POWERTXHT20_5);
+
+       power += scale;
+       delta = abs((int) ah->paprd_target_power - (int) power);
+       if (delta > scale)
+               return -1;
+
+       power += 2 * get_streams(common->tx_chainmask);
+       return power;
+}
+
+static int ar9003_paprd_setup_single_table(struct ath_hw *ah)
+{
+       struct ath_common *common = ath9k_hw_common(ah);
        struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep;
        struct ar9300_modal_eep_header *hdr;
        static const u32 ctrl0[3] = {
@@ -45,6 +105,7 @@ static void ar9003_paprd_setup_single_table(struct ath_hw *ah)
                AR_PHY_PAPRD_CTRL1_B2
        };
        u32 am_mask, ht40_mask;
+       int training_power;
        int i;
 
        if (ah->curchan && IS_CHAN_5GHZ(ah->curchan))
@@ -55,11 +116,25 @@ static void ar9003_paprd_setup_single_table(struct ath_hw *ah)
        am_mask = le32_to_cpu(hdr->papdRateMaskHt20) & AR9300_PAPRD_RATE_MASK;
        ht40_mask = le32_to_cpu(hdr->papdRateMaskHt40) & AR9300_PAPRD_RATE_MASK;
 
+       if (IS_CHAN_2GHZ(ah->curchan))
+               training_power = ar9003_get_training_power_2g(ah);
+       else
+               training_power = ar9003_get_training_power_5g(ah);
+
+       if (training_power < 0) {
+               ath_dbg(common, ATH_DBG_CALIBRATE,
+                       "PAPRD target power delta out of range");
+               return -ERANGE;
+       }
+       ah->paprd_training_power = training_power;
+       ath_dbg(common, ATH_DBG_CALIBRATE,
+               "Training power: %d, Target power: %d\n",
+               ah->paprd_training_power, ah->paprd_target_power);
+
        REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2AM, AR_PHY_PAPRD_AM2AM_MASK, am_mask);
        REG_RMW_FIELD(ah, AR_PHY_PAPRD_AM2PM, AR_PHY_PAPRD_AM2PM_MASK, am_mask);
        REG_RMW_FIELD(ah, AR_PHY_PAPRD_HT40, AR_PHY_PAPRD_HT40_MASK, ht40_mask);
 
-
        for (i = 0; i < ah->caps.max_txchains; i++) {
                REG_RMW_FIELD(ah, ctrl0[i],
                              AR_PHY_PAPRD_CTRL0_USE_SINGLE_TABLE_MASK, 1);
@@ -141,6 +216,7 @@ static void ar9003_paprd_setup_single_table(struct ath_hw *ah)
                      AR_PHY_PAPRD_PRE_POST_SCALING, 185706);
        REG_RMW_FIELD(ah, AR_PHY_PAPRD_PRE_POST_SCALE_7_B0,
                      AR_PHY_PAPRD_PRE_POST_SCALING, 175487);
+       return 0;
 }
 
 static void ar9003_paprd_get_gain_table(struct ath_hw *ah)
@@ -595,15 +671,10 @@ void ar9003_paprd_populate_single_table(struct ath_hw *ah,
 {
        u32 *paprd_table_val = caldata->pa_table[chain];
        u32 small_signal_gain = caldata->small_signal_gain[chain];
-       u32 training_power;
+       u32 training_power = ah->paprd_training_power;
        u32 reg = 0;
        int i;
 
-       training_power =
-           REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5,
-                          AR_PHY_POWERTX_RATE5_POWERTXHT20_0);
-       training_power -= 4;
-
        if (chain == 0)
                reg = AR_PHY_PAPRD_MEM_TAB_B0;
        else if (chain == 1)
@@ -643,14 +714,8 @@ EXPORT_SYMBOL(ar9003_paprd_populate_single_table);
 
 int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain)
 {
-
        unsigned int i, desired_gain, gain_index;
-       unsigned int train_power;
-
-       train_power = REG_READ_FIELD(ah, AR_PHY_POWERTX_RATE5,
-                                    AR_PHY_POWERTX_RATE5_POWERTXHT20_0);
-
-       train_power = train_power - 4;
+       unsigned int train_power = ah->paprd_training_power;
 
        desired_gain = ar9003_get_desired_gain(ah, chain, train_power);
 
@@ -716,7 +781,12 @@ EXPORT_SYMBOL(ar9003_paprd_create_curve);
 
 int ar9003_paprd_init_table(struct ath_hw *ah)
 {
-       ar9003_paprd_setup_single_table(ah);
+       int ret;
+
+       ret = ar9003_paprd_setup_single_table(ah);
+       if (ret < 0)
+           return ret;
+
        ar9003_paprd_get_gain_table(ah);
        return 0;
 }
index 6f811c7ada05694a19b12372c74761b971277f7e..59bab6bd8a743b73d09becce5390ff18ede9ad6f 100644 (file)
 #define AR_PHY_POWERTX_RATE5_POWERTXHT20_0     0x3F
 #define AR_PHY_POWERTX_RATE5_POWERTXHT20_0_S   0
 
+#define AR_PHY_POWERTX_RATE6                   (AR_SM_BASE + 0x1d4)
+#define AR_PHY_POWERTX_RATE6_POWERTXHT20_5     0x3F00
+#define AR_PHY_POWERTX_RATE6_POWERTXHT20_5_S   8
+
+#define AR_PHY_POWERTX_RATE8                   (AR_SM_BASE + 0x1dc)
+#define AR_PHY_POWERTX_RATE8_POWERTXHT40_5     0x3F00
+#define AR_PHY_POWERTX_RATE8_POWERTXHT40_5_S   8
+
 void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
 
 #endif  /* AR9003_PHY_H */
index c20e0476ee449d9a995bd6edbfe0fc9c14742865..97f22c4286032ec090cb2e682c81956b5df59053 100644 (file)
@@ -833,6 +833,8 @@ struct ath_hw {
        u32 bb_watchdog_last_status;
        u32 bb_watchdog_timeout_ms; /* in ms, 0 to disable */
 
+       unsigned int paprd_target_power;
+       unsigned int paprd_training_power;
        u32 paprd_gain_table_entries[PAPRD_GAIN_TABLE_ENTRIES];
        u8 paprd_gain_table_index[PAPRD_GAIN_TABLE_ENTRIES];
        /*
index d49dacbc96a7afa05d1536c1026874ae59184113..c68205dea1fa4fec39741055a9eab6e1f1c8e930 100644 (file)
@@ -373,6 +373,9 @@ void ath_paprd_calibrate(struct work_struct *work)
        if (!caldata)
                return;
 
+       if (ar9003_paprd_init_table(ah) < 0)
+               return;
+
        skb = alloc_skb(len, GFP_KERNEL);
        if (!skb)
                return;
@@ -388,7 +391,6 @@ void ath_paprd_calibrate(struct work_struct *work)
        memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN);
 
        ath9k_ps_wakeup(sc);
-       ar9003_paprd_init_table(ah);
        for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) {
                if (!(common->tx_chainmask & BIT(chain)))
                        continue;