ath9k: Fix bugs in handling TX power
authorSenthil Balasubramanian <senthilkumar@atheros.com>
Fri, 18 Sep 2009 09:38:20 +0000 (15:08 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 7 Oct 2009 20:39:40 +0000 (16:39 -0400)
* Get power table offset from the EEPROM instead of using
  a hardcoded value of -5 if the EEPROM rev is >= 21.
* Add support in the 4k eeprom code for tx power offset
  in case we have a 4k AR9280 implementation.
* Fix tx power accuracy at high powers.

Signed-off-by: Senthil Balasubramanian <senthilkumar@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/eeprom.h
drivers/net/wireless/ath/ath9k/eeprom_4k.c
drivers/net/wireless/ath/ath9k/eeprom_def.c

index 33492741735d740ef7feaaa606cbb5ce93b0deb6..2f2993b50e2ffb2159261ad97a4c4632ded5b31d 100644 (file)
 #define AR5416_EEP_MINOR_VER_17      0x11
 #define AR5416_EEP_MINOR_VER_19      0x13
 #define AR5416_EEP_MINOR_VER_20      0x14
 #define AR5416_EEP_MINOR_VER_17      0x11
 #define AR5416_EEP_MINOR_VER_19      0x13
 #define AR5416_EEP_MINOR_VER_20      0x14
+#define AR5416_EEP_MINOR_VER_21      0x15
 #define AR5416_EEP_MINOR_VER_22      0x16
 
 #define AR5416_NUM_5G_CAL_PIERS         8
 #define AR5416_EEP_MINOR_VER_22      0x16
 
 #define AR5416_NUM_5G_CAL_PIERS         8
 #define AR5416_BCHAN_UNUSED             0xFF
 #define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64
 #define AR5416_MAX_CHAINS               3
 #define AR5416_BCHAN_UNUSED             0xFF
 #define AR5416_MAX_PWR_RANGE_IN_HALF_DB 64
 #define AR5416_MAX_CHAINS               3
-#define AR5416_PWR_TABLE_OFFSET         -5
+#define AR5416_PWR_TABLE_OFFSET_DB     -5
 
 /* Rx gain type values */
 #define AR5416_EEP_RXGAIN_23DB_BACKOFF     0
 
 /* Rx gain type values */
 #define AR5416_EEP_RXGAIN_23DB_BACKOFF     0
@@ -302,7 +303,7 @@ struct base_eep_header {
        u8 txGainType;
        u8 rcChainMask;
        u8 desiredScaleCCK;
        u8 txGainType;
        u8 rcChainMask;
        u8 desiredScaleCCK;
-       u8 power_table_offset;
+       u8 pwr_table_offset;
        u8 frac_n_5g;
        u8 futureBase_3[21];
 } __packed;
        u8 frac_n_5g;
        u8 futureBase_3[21];
 } __packed;
index 2a27b1d51a1bed238e578746729f38367761d3c5..58167d861dc6cbd945f6059046b6d3ace65a112c 100644 (file)
@@ -210,6 +210,8 @@ static u32 ath9k_hw_4k_get_eeprom(struct ath_hw *ah,
                return pBase->rxMask;
        case EEP_FRAC_N_5G:
                return 0;
                return pBase->rxMask;
        case EEP_FRAC_N_5G:
                return 0;
+       case EEP_PWR_TABLE_OFFSET:
+               return AR5416_PWR_TABLE_OFFSET_DB;
        default:
                return 0;
        }
        default:
                return 0;
        }
@@ -753,7 +755,7 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah,
 
        if (AR_SREV_9280_10_OR_LATER(ah)) {
                for (i = 0; i < Ar5416RateSize; i++)
 
        if (AR_SREV_9280_10_OR_LATER(ah)) {
                for (i = 0; i < Ar5416RateSize; i++)
-                       ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2;
+                       ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2;
        }
 
        /* OFDM power per rate */
        }
 
        /* OFDM power per rate */
index 8f04b644e2e66cffc230ef57eaeb309153757719..404a0341242c4531e49777d1639cd7600afbfdb1 100644 (file)
@@ -291,6 +291,11 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah,
                        return pBase->frac_n_5g;
                else
                        return 0;
                        return pBase->frac_n_5g;
                else
                        return 0;
+       case EEP_PWR_TABLE_OFFSET:
+               if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_21)
+                       return pBase->pwr_table_offset;
+               else
+                       return AR5416_PWR_TABLE_OFFSET_DB;
        default:
                return 0;
        }
        default:
                return 0;
        }
@@ -741,6 +746,76 @@ static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *ah,
        return;
 }
 
        return;
 }
 
+static int16_t ath9k_change_gain_boundary_setting(struct ath_hw *ah,
+                               u16 *gb,
+                               u16 numXpdGain,
+                               u16 pdGainOverlap_t2,
+                               int8_t pwr_table_offset,
+                               int16_t *diff)
+
+{
+       u16 k;
+
+       /* Prior to writing the boundaries or the pdadc vs. power table
+        * into the chip registers the default starting point on the pdadc
+        * vs. power table needs to be checked and the curve boundaries
+        * adjusted accordingly
+        */
+       if (AR_SREV_9280_20_OR_LATER(ah)) {
+               u16 gb_limit;
+
+               if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
+                       /* get the difference in dB */
+                       *diff = (u16)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
+                       /* get the number of half dB steps */
+                       *diff *= 2;
+                       /* change the original gain boundary settings
+                        * by the number of half dB steps
+                        */
+                       for (k = 0; k < numXpdGain; k++)
+                               gb[k] = (u16)(gb[k] - *diff);
+               }
+               /* Because of a hardware limitation, ensure the gain boundary
+                * is not larger than (63 - overlap)
+                */
+               gb_limit = (u16)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
+
+               for (k = 0; k < numXpdGain; k++)
+                       gb[k] = (u16)min(gb_limit, gb[k]);
+       }
+
+       return *diff;
+}
+
+static void ath9k_adjust_pdadc_values(struct ath_hw *ah,
+                                     int8_t pwr_table_offset,
+                                     int16_t diff,
+                                     u8 *pdadcValues)
+{
+#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
+       u16 k;
+
+       /* If this is a board that has a pwrTableOffset that differs from
+        * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
+        * pdadc vs pwr table needs to be adjusted prior to writing to the
+        * chip.
+        */
+       if (AR_SREV_9280_20_OR_LATER(ah)) {
+               if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
+                       /* shift the table to start at the new offset */
+                       for (k = 0; k < (u16)NUM_PDADC(diff); k++ ) {
+                               pdadcValues[k] = pdadcValues[k + diff];
+                       }
+
+                       /* fill the back of the table */
+                       for (k = (u16)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
+                               pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
+                       }
+               }
+       }
+#undef NUM_PDADC
+}
+
 static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
                                  struct ath9k_channel *chan,
                                  int16_t *pTxPowerIndexOffset)
 static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
                                  struct ath9k_channel *chan,
                                  int16_t *pTxPowerIndexOffset)
@@ -756,15 +831,18 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
        static u8 pdadcValues[AR5416_NUM_PDADC_VALUES];
        u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK];
        u16 numPiers, i, j;
        static u8 pdadcValues[AR5416_NUM_PDADC_VALUES];
        u16 gainBoundaries[AR5416_PD_GAINS_IN_MASK];
        u16 numPiers, i, j;
-       int16_t tMinCalPower;
+       int16_t tMinCalPower, diff = 0;
        u16 numXpdGain, xpdMask;
        u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 };
        u32 reg32, regOffset, regChainOffset;
        int16_t modalIdx;
        u16 numXpdGain, xpdMask;
        u16 xpdGainValues[AR5416_NUM_PD_GAINS] = { 0, 0, 0, 0 };
        u32 reg32, regOffset, regChainOffset;
        int16_t modalIdx;
+       int8_t pwr_table_offset;
 
        modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0;
        xpdMask = pEepData->modalHeader[modalIdx].xpdGain;
 
 
        modalIdx = IS_CHAN_2GHZ(chan) ? 1 : 0;
        xpdMask = pEepData->modalHeader[modalIdx].xpdGain;
 
+       pwr_table_offset = ah->eep_ops->get_eeprom(ah, EEP_PWR_TABLE_OFFSET);
+
        if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
            AR5416_EEP_MINOR_VER_2) {
                pdGainOverlap_t2 =
        if ((pEepData->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >=
            AR5416_EEP_MINOR_VER_2) {
                pdGainOverlap_t2 =
@@ -844,6 +922,13 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
                                                        numXpdGain);
                        }
 
                                                        numXpdGain);
                        }
 
+                       diff = ath9k_change_gain_boundary_setting(ah,
+                                                          gainBoundaries,
+                                                          numXpdGain,
+                                                          pdGainOverlap_t2,
+                                                          pwr_table_offset,
+                                                          &diff);
+
                        if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) {
                                if (OLC_FOR_AR9280_20_LATER) {
                                        REG_WRITE(ah,
                        if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) {
                                if (OLC_FOR_AR9280_20_LATER) {
                                        REG_WRITE(ah,
@@ -864,6 +949,10 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah,
                                }
                        }
 
                                }
                        }
 
+
+                       ath9k_adjust_pdadc_values(ah, pwr_table_offset,
+                                                 diff, pdadcValues);
+
                        regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset;
                        for (j = 0; j < 32; j++) {
                                reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) |
                        regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset;
                        for (j = 0; j < 32; j++) {
                                reg32 = ((pdadcValues[4 * j + 0] & 0xFF) << 0) |
@@ -1199,8 +1288,13 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
        }
 
        if (AR_SREV_9280_10_OR_LATER(ah)) {
        }
 
        if (AR_SREV_9280_10_OR_LATER(ah)) {
-               for (i = 0; i < Ar5416RateSize; i++)
-                       ratesArray[i] -= AR5416_PWR_TABLE_OFFSET * 2;
+               for (i = 0; i < Ar5416RateSize; i++) {
+                       int8_t pwr_table_offset;
+
+                       pwr_table_offset = ah->eep_ops->get_eeprom(ah,
+                                                       EEP_PWR_TABLE_OFFSET);
+                       ratesArray[i] -= pwr_table_offset * 2;
+               }
        }
 
        REG_WRITE(ah, AR_PHY_POWER_TX_RATE1,
        }
 
        REG_WRITE(ah, AR_PHY_POWER_TX_RATE1,
@@ -1299,7 +1393,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah,
 
        if (AR_SREV_9280_10_OR_LATER(ah))
                regulatory->max_power_level =
 
        if (AR_SREV_9280_10_OR_LATER(ah))
                regulatory->max_power_level =
-                       ratesArray[i] + AR5416_PWR_TABLE_OFFSET * 2;
+                       ratesArray[i] + AR5416_PWR_TABLE_OFFSET_DB * 2;
        else
                regulatory->max_power_level = ratesArray[i];
 
        else
                regulatory->max_power_level = ratesArray[i];