ath9k: add power per-rate tables for AR9002 chips
authorLorenzo Bianconi <lorenzo.bianconi83@gmail.com>
Tue, 30 Dec 2014 22:10:18 +0000 (23:10 +0100)
committerKalle Valo <kvalo@codeaurora.org>
Thu, 15 Jan 2015 12:47:06 +0000 (14:47 +0200)
Add TX power per-rate tables for MIMO/legacy modes for AR9002 based chips
in order to cap the maximum TX power value per-rate in the TX descriptor path.
Add TX power adjustments for HT40 mode, open loop CCK rates and eeprom power
bias for AR9280 and later chips

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/ath/ath9k/ar5008_phy.c
drivers/net/wireless/ath/ath9k/eeprom_4k.c
drivers/net/wireless/ath/ath9k/eeprom_9287.c
drivers/net/wireless/ath/ath9k/eeprom_def.c
drivers/net/wireless/ath/ath9k/hw.h

index 5829074208fa8173c343c6eaee050c86c8d30c41..f273427fdd29ff93a3406d582928499ae22398c7 100644 (file)
 
 /* All code below is for AR5008, AR9001, AR9002 */
 
+#define AR5008_OFDM_RATES              8
+#define AR5008_HT_SS_RATES             8
+#define AR5008_HT_DS_RATES             8
+
+#define AR5008_HT20_SHIFT              16
+#define AR5008_HT40_SHIFT              24
+
+#define AR5008_11NA_OFDM_SHIFT         0
+#define AR5008_11NA_HT_SS_SHIFT                8
+#define AR5008_11NA_HT_DS_SHIFT                16
+
+#define AR5008_11NG_OFDM_SHIFT         4
+#define AR5008_11NG_HT_SS_SHIFT                12
+#define AR5008_11NG_HT_DS_SHIFT                20
+
 static const int firstep_table[] =
 /* level:  0   1   2   3   4   5   6   7   8  */
        { -4, -2,  0,  2,  4,  6,  8, 10, 12 }; /* lvl 0-8, default 2 */
@@ -1235,6 +1250,71 @@ static void ar5008_hw_set_radar_conf(struct ath_hw *ah)
        conf->radar_inband = 8;
 }
 
+static void ar5008_hw_init_txpower_cck(struct ath_hw *ah, int16_t *rate_array)
+{
+#define CCK_DELTA(x) ((OLC_FOR_AR9280_20_LATER) ? max((x) - 2, 0) : (x))
+       ah->tx_power[0] = CCK_DELTA(rate_array[rate1l]);
+       ah->tx_power[1] = CCK_DELTA(min(rate_array[rate2l],
+                                       rate_array[rate2s]));
+       ah->tx_power[2] = CCK_DELTA(min(rate_array[rate5_5l],
+                                       rate_array[rate5_5s]));
+       ah->tx_power[3] = CCK_DELTA(min(rate_array[rate11l],
+                                       rate_array[rate11s]));
+#undef CCK_DELTA
+}
+
+static void ar5008_hw_init_txpower_ofdm(struct ath_hw *ah, int16_t *rate_array,
+                                       int offset)
+{
+       int i, idx = 0;
+
+       for (i = offset; i < offset + AR5008_OFDM_RATES; i++) {
+               ah->tx_power[i] = rate_array[idx];
+               idx++;
+       }
+}
+
+static void ar5008_hw_init_txpower_ht(struct ath_hw *ah, int16_t *rate_array,
+                                     int ss_offset, int ds_offset,
+                                     bool is_40, int ht40_delta)
+{
+       int i, mcs_idx = (is_40) ? AR5008_HT40_SHIFT : AR5008_HT20_SHIFT;
+
+       for (i = ss_offset; i < ss_offset + AR5008_HT_SS_RATES; i++) {
+               ah->tx_power[i] = rate_array[mcs_idx] + ht40_delta;
+               mcs_idx++;
+       }
+       memcpy(&ah->tx_power[ds_offset], &ah->tx_power[ss_offset],
+              AR5008_HT_SS_RATES);
+}
+
+void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
+                                struct ath9k_channel *chan, int ht40_delta)
+{
+       if (IS_CHAN_5GHZ(chan)) {
+               ar5008_hw_init_txpower_ofdm(ah, rate_array,
+                                           AR5008_11NA_OFDM_SHIFT);
+               if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+                       ar5008_hw_init_txpower_ht(ah, rate_array,
+                                                 AR5008_11NA_HT_SS_SHIFT,
+                                                 AR5008_11NA_HT_DS_SHIFT,
+                                                 IS_CHAN_HT40(chan),
+                                                 ht40_delta);
+               }
+       } else {
+               ar5008_hw_init_txpower_cck(ah, rate_array);
+               ar5008_hw_init_txpower_ofdm(ah, rate_array,
+                                           AR5008_11NG_OFDM_SHIFT);
+               if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) {
+                       ar5008_hw_init_txpower_ht(ah, rate_array,
+                                                 AR5008_11NG_HT_SS_SHIFT,
+                                                 AR5008_11NG_HT_DS_SHIFT,
+                                                 IS_CHAN_HT40(chan),
+                                                 ht40_delta);
+               }
+       }
+}
+
 int ar5008_hw_attach_phy_ops(struct ath_hw *ah)
 {
        struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
index 07b806c56c56b4b0a5167cd704493a85285a8b58..e5a78d4fd66e570765a0ac5fd020be9115894009 100644 (file)
@@ -748,6 +748,20 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
                          | ATH9K_POW_SM(ratesArray[rateDupCck], 0));
        }
 
+       /* TPC initializations */
+       if (ah->tpc_enabled) {
+               int ht40_delta;
+
+               ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0;
+               ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta);
+               /* Enable TPC */
+               REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX,
+                       MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+       } else {
+               /* Disable TPC */
+               REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER);
+       }
+
        REGWRITE_BUFFER_FLUSH(ah);
 }
 
index 5ba1385c9838016a942df4e189bf7d8a484fad6e..6ca33dfde1fdc81fef2d2ebfa7441e3e22808777 100644 (file)
@@ -886,6 +886,21 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah,
                          | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8)
                          | ATH9K_POW_SM(ratesArray[rateDupCck], 0));
        }
+
+       /* TPC initializations */
+       if (ah->tpc_enabled) {
+               int ht40_delta;
+
+               ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0;
+               ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta);
+               /* Enable TPC */
+               REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX,
+                       MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+       } else {
+               /* Disable TPC */
+               REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER);
+       }
+
        REGWRITE_BUFFER_FLUSH(ah);
 }
 
index 122b846b8ec0e796bed2e15f8a6dc4341b9c00bc..098059039351fb065cafcb252a5c91b1e071d43b 100644 (file)
@@ -1332,6 +1332,20 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
                  ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6)
                  | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0));
 
+       /* TPC initializations */
+       if (ah->tpc_enabled) {
+               int ht40_delta;
+
+               ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0;
+               ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta);
+               /* Enable TPC */
+               REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX,
+                       MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE);
+       } else {
+               /* Disable TPC */
+               REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER);
+       }
+
        REGWRITE_BUFFER_FLUSH(ah);
 }
 
index 64bfcbd9fcec5eddb33f64a5dd1f8bc6044ab41f..450704e49f03ef5f262aa690d512612ec4f60b65 100644 (file)
@@ -1087,6 +1087,8 @@ bool ar9003_is_paprd_enabled(struct ath_hw *ah);
 void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx);
 void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array,
                                 struct ath9k_channel *chan);
+void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array,
+                                struct ath9k_channel *chan, int ht40_delta);
 
 /* Hardware family op attach helpers */
 int ar5008_hw_attach_phy_ops(struct ath_hw *ah);