summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--package/mac80211/patches/530-ath9k_paprd_thermal.patch122
-rw-r--r--package/mac80211/patches/531-ath9k_paprd_rate_mask.patch28
-rw-r--r--package/mac80211/patches/532-ath9k_paprd_training_power.patch225
3 files changed, 375 insertions, 0 deletions
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 000000000..09c7a6645
--- /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 000000000..153fb90a1
--- /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 000000000..e659fadff
--- /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;