From 5fa415d366b9b62d3e1a4921bae060fb52af83d1 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 12 Dec 2010 01:15:38 +0000 Subject: [PATCH] ath9k: fix a few issues with pa predistortion on ar9003 SVN-Revision: 24504 --- .../patches/530-ath9k_paprd_thermal.patch | 122 ++++++++++ .../patches/531-ath9k_paprd_rate_mask.patch | 28 +++ .../532-ath9k_paprd_training_power.patch | 225 ++++++++++++++++++ 3 files changed, 375 insertions(+) create mode 100644 package/mac80211/patches/530-ath9k_paprd_thermal.patch create mode 100644 package/mac80211/patches/531-ath9k_paprd_rate_mask.patch create mode 100644 package/mac80211/patches/532-ath9k_paprd_training_power.patch diff --git a/package/mac80211/patches/530-ath9k_paprd_thermal.patch b/package/mac80211/patches/530-ath9k_paprd_thermal.patch new file mode 100644 index 000000000000..09c7a6645f6e --- /dev/null +++ b/package/mac80211/patches/530-ath9k_paprd_thermal.patch @@ -0,0 +1,122 @@ +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -320,6 +320,42 @@ static void ath_paprd_activate(struct at + ath9k_ps_restore(sc); + } + ++static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int chain) ++{ ++ struct ieee80211_hw *hw = sc->hw; ++ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); ++ struct ath_tx_control txctl; ++ int time_left; ++ ++ memset(&txctl, 0, sizeof(txctl)); ++ txctl.txq = sc->tx.txq_map[WME_AC_BE]; ++ ++ memset(tx_info, 0, sizeof(*tx_info)); ++ tx_info->band = hw->conf.channel->band; ++ tx_info->flags |= IEEE80211_TX_CTL_NO_ACK; ++ tx_info->control.rates[0].idx = 0; ++ tx_info->control.rates[0].count = 1; ++ tx_info->control.rates[0].flags = IEEE80211_TX_RC_MCS; ++ tx_info->control.rates[1].idx = -1; ++ ++ init_completion(&sc->paprd_complete); ++ sc->paprd_pending = true; ++ txctl.paprd = BIT(chain); ++ if (ath_tx_start(hw, skb, &txctl) != 0) ++ return false; ++ ++ time_left = wait_for_completion_timeout(&sc->paprd_complete, ++ msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); ++ sc->paprd_pending = false; ++ ++ if (!time_left) ++ ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_CALIBRATE, ++ "Timeout waiting for paprd training on TX chain %d\n", ++ chain); ++ ++ return !!time_left; ++} ++ + void ath_paprd_calibrate(struct work_struct *work) + { + struct ath_softc *sc = container_of(work, struct ath_softc, paprd_work); +@@ -327,18 +363,12 @@ void ath_paprd_calibrate(struct work_str + struct ath_hw *ah = sc->sc_ah; + struct ieee80211_hdr *hdr; + struct sk_buff *skb = NULL; +- struct ieee80211_tx_info *tx_info; +- int band = hw->conf.channel->band; +- struct ieee80211_supported_band *sband = &sc->sbands[band]; +- struct ath_tx_control txctl; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_common *common = ath9k_hw_common(ah); + int ftype; + int chain_ok = 0; + int chain; + int len = 1800; +- int time_left; +- int i; + + if (!caldata) + return; +@@ -347,8 +377,6 @@ void ath_paprd_calibrate(struct work_str + if (!skb) + return; + +- tx_info = IEEE80211_SKB_CB(skb); +- + skb_put(skb, len); + memset(skb->data, 0, len); + hdr = (struct ieee80211_hdr *)skb->data; +@@ -359,9 +387,6 @@ void ath_paprd_calibrate(struct work_str + memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + +- memset(&txctl, 0, sizeof(txctl)); +- txctl.txq = sc->tx.txq_map[WME_AC_BE]; +- + ath9k_ps_wakeup(sc); + ar9003_paprd_init_table(ah); + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { +@@ -369,30 +394,19 @@ void ath_paprd_calibrate(struct work_str + continue; + + chain_ok = 0; +- memset(tx_info, 0, sizeof(*tx_info)); +- tx_info->band = band; + +- for (i = 0; i < 4; i++) { +- tx_info->control.rates[i].idx = sband->n_bitrates - 1; +- tx_info->control.rates[i].count = 6; +- } ++ ath_dbg(common, ATH_DBG_CALIBRATE, ++ "Sending PAPRD frame for thermal measurement " ++ "on chain %d\n", chain); ++ if (!ath_paprd_send_frame(sc, skb, chain)) ++ goto fail_paprd; + +- init_completion(&sc->paprd_complete); +- sc->paprd_pending = true; + ar9003_paprd_setup_gain_table(ah, chain); +- txctl.paprd = BIT(chain); +- if (ath_tx_start(hw, skb, &txctl) != 0) +- break; + +- time_left = wait_for_completion_timeout(&sc->paprd_complete, +- msecs_to_jiffies(ATH_PAPRD_TIMEOUT)); +- sc->paprd_pending = false; +- if (!time_left) { +- ath_dbg(ath9k_hw_common(ah), ATH_DBG_CALIBRATE, +- "Timeout waiting for paprd training on TX chain %d\n", +- chain); ++ ath_dbg(common, ATH_DBG_CALIBRATE, ++ "Sending PAPRD training frame on chain %d\n", chain); ++ if (!ath_paprd_send_frame(sc, skb, chain)) + goto fail_paprd; +- } + + if (!ar9003_paprd_is_done(ah)) + break; diff --git a/package/mac80211/patches/531-ath9k_paprd_rate_mask.patch b/package/mac80211/patches/531-ath9k_paprd_rate_mask.patch new file mode 100644 index 000000000000..153fb90a1eac --- /dev/null +++ b/package/mac80211/patches/531-ath9k_paprd_rate_mask.patch @@ -0,0 +1,28 @@ +--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h ++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +@@ -31,6 +31,12 @@ + #define AR9300_ANT_16S 25 + #define AR9300_FUTURE_MODAL_SZ 6 + ++#define AR9300_PAPRD_RATE_MASK 0x01ffffff ++#define AR9300_PAPRD_SCALE_1 0x0e000000 ++#define AR9300_PAPRD_SCALE_1_S 25 ++#define AR9300_PAPRD_SCALE_2 0x70000000 ++#define AR9300_PAPRD_SCALE_2_S 28 ++ + /* Delta from which to start power to pdadc table */ + /* This offset is used in both open loop and closed loop power control + * schemes. In open loop power control, it is not really needed, but for +--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +@@ -52,8 +52,8 @@ static void ar9003_paprd_setup_single_ta + else + hdr = &eep->modalHeader2G; + +- am_mask = le32_to_cpu(hdr->papdRateMaskHt20); +- ht40_mask = le32_to_cpu(hdr->papdRateMaskHt40); ++ am_mask = le32_to_cpu(hdr->papdRateMaskHt20) & AR9300_PAPRD_RATE_MASK; ++ ht40_mask = le32_to_cpu(hdr->papdRateMaskHt40) & AR9300_PAPRD_RATE_MASK; + + 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); diff --git a/package/mac80211/patches/532-ath9k_paprd_training_power.patch b/package/mac80211/patches/532-ath9k_paprd_training_power.patch new file mode 100644 index 000000000000..e659fadfff92 --- /dev/null +++ b/package/mac80211/patches/532-ath9k_paprd_training_power.patch @@ -0,0 +1,225 @@ +--- a/drivers/net/wireless/ath/ath9k/ar9003_paprd.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_paprd.c +@@ -30,9 +30,66 @@ void ar9003_paprd_enable(struct ath_hw * + } + 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(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(hdr->papdRateMaskHt20, AR9300_PAPRD_SCALE_1); ++ else if (chan->channel >= 5400) ++ scale = MS(hdr->papdRateMaskHt40, AR9300_PAPRD_SCALE_2); ++ else ++ scale = MS(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 +102,7 @@ static void ar9003_paprd_setup_single_ta + 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 +113,25 @@ static void ar9003_paprd_setup_single_ta + 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 +213,7 @@ static void ar9003_paprd_setup_single_ta + 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 +668,10 @@ void ar9003_paprd_populate_single_table( + { + 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 +711,8 @@ EXPORT_SYMBOL(ar9003_paprd_populate_sing + + 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 +778,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; + } +--- a/drivers/net/wireless/ath/ath9k/hw.h ++++ b/drivers/net/wireless/ath/ath9k/hw.h +@@ -835,6 +835,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]; + /* +--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h ++++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h +@@ -1090,6 +1090,14 @@ + #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 */ +--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c ++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +@@ -4798,6 +4798,14 @@ static void ath9k_hw_ar9300_set_txpower( + /* 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)) ++ 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, +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -373,6 +373,9 @@ void ath_paprd_calibrate(struct work_str + 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_str + 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; -- 2.30.2