diff options
author | Felix Fietkau <nbd@openwrt.org> | 2010-12-13 08:40:54 +0100 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-12-13 15:23:33 -0500 |
commit | 1bf38661822049931a0ab8d2b43153b26cc919f6 (patch) | |
tree | bea1bec029d0c8fa879dfef06a40798408c8f6dd /drivers/net/wireless/ath/ath9k/ar9003_paprd.c | |
parent | 1782352d4908c79d195b43e0c1b6b109e0d93d05 (diff) | |
download | linux-stable-1bf38661822049931a0ab8d2b43153b26cc919f6.tar.gz linux-stable-1bf38661822049931a0ab8d2b43153b26cc919f6.tar.bz2 linux-stable-1bf38661822049931a0ab8d2b43153b26cc919f6.zip |
ath9k_hw: fix PA predistortion training power selection
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>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/ar9003_paprd.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/ar9003_paprd.c | 102 |
1 files changed, 86 insertions, 16 deletions
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c index cdca4c3265b9..69f779237d28 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c @@ -30,9 +30,69 @@ 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] = { AR_PHY_PAPRD_CTRL0_B0, @@ -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; } |