ath9k: fix a few issues with pa predistortion on ar9003
authorFelix Fietkau <nbd@openwrt.org>
Sun, 12 Dec 2010 01:15:38 +0000 (01:15 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sun, 12 Dec 2010 01:15:38 +0000 (01:15 +0000)
SVN-Revision: 24504

package/mac80211/patches/530-ath9k_paprd_thermal.patch [new file with mode: 0644]
package/mac80211/patches/531-ath9k_paprd_rate_mask.patch [new file with mode: 0644]
package/mac80211/patches/532-ath9k_paprd_training_power.patch [new file with mode: 0644]

diff --git a/package/mac80211/patches/530-ath9k_paprd_thermal.patch b/package/mac80211/patches/530-ath9k_paprd_thermal.patch
new file mode 100644 (file)
index 0000000..09c7a66
--- /dev/null
@@ -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 (file)
index 0000000..153fb90
--- /dev/null
@@ -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 (file)
index 0000000..e659fad
--- /dev/null
@@ -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;