ath9k: add power per-rate tables for AR9002 chips
[cascardo/linux.git] / drivers / net / wireless / ath / ath9k / ar5008_phy.c
index 5829074..f273427 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);