The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/ath/ath_hal/ar9002/ar9280_olc.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  *
   15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   25  * SUCH DAMAGE.
   26  *
   27  * $FreeBSD$
   28  */
   29 #include "opt_ah.h"
   30 
   31 #include "ah.h"
   32 #include "ah_internal.h"
   33 
   34 #include "ah_eeprom_v14.h"
   35 
   36 #include "ar9002/ar9280.h"
   37 #include "ar5416/ar5416reg.h"
   38 #include "ar5416/ar5416phy.h"
   39 #include "ar9002/ar9002phy.h"
   40 
   41 #include "ar9002/ar9280_olc.h"
   42 
   43 void
   44 ar9280olcInit(struct ath_hal *ah)
   45 {
   46         uint32_t i;
   47 
   48         /* Only do OLC if it's enabled for this chipset */
   49         if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
   50                 return;
   51 
   52         HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__);
   53 
   54         for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)
   55                 AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,
   56                     AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);
   57 
   58         AH9280(ah)->PDADCdelta = 0;
   59 }
   60 
   61 void
   62 ar9280olcGetTxGainIndex(struct ath_hal *ah,
   63     const struct ieee80211_channel *chan,
   64     struct calDataPerFreqOpLoop *rawDatasetOpLoop,
   65     uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)
   66 {
   67         uint8_t pcdac, i = 0;
   68         uint16_t idxL = 0, idxR = 0, numPiers;
   69         HAL_BOOL match;
   70         CHAN_CENTERS centers;
   71 
   72         ar5416GetChannelCenters(ah, chan, &centers);
   73 
   74         for (numPiers = 0; numPiers < availPiers; numPiers++)
   75                 if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
   76                         break;
   77 
   78         match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
   79                     IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
   80                     &idxL, &idxR);
   81         if (match) {
   82                 pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
   83                 *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
   84         } else {
   85                 pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
   86                 *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
   87                                 rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
   88         }
   89         while (pcdac > AH9280(ah)->originalGain[i] &&
   90                         i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
   91                 i++;
   92 
   93         *pcdacIdx = i;
   94 }
   95 
   96 /*
   97  * XXX txPower here is likely not the target txPower in the traditional
   98  * XXX sense, but is set by a call to ar9280olcGetTxGainIndex().
   99  * XXX Thus, be careful if you're trying to use this routine yourself.
  100  */
  101 void
  102 ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
  103     uint8_t *pPDADCValues)
  104 {
  105         uint32_t i;
  106         uint32_t offset;
  107 
  108         OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
  109         OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
  110 
  111         OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
  112 
  113         offset = txPower;
  114         for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
  115                 if (i < offset)
  116                         pPDADCValues[i] = 0x0;
  117                 else
  118                         pPDADCValues[i] = 0xFF;
  119 }
  120 
  121 /*
  122  * Run temperature compensation calibration.
  123  *
  124  * The TX gain table is adjusted depending upon the difference
  125  * between the initial PDADC value and the currently read
  126  * average TX power sample value. This value is only valid if
  127  * frames have been transmitted, so currPDADC will be 0 if
  128  * no frames have yet been transmitted.
  129  */
  130 void
  131 ar9280olcTemperatureCompensation(struct ath_hal *ah)
  132 {
  133         uint32_t rddata, i;
  134         int delta, currPDADC, regval;
  135         uint8_t hpwr_5g = 0;
  136 
  137         if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
  138                 return;
  139 
  140         rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
  141         currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
  142 
  143         HALDEBUG(ah, HAL_DEBUG_PERCAL,
  144             "%s: called: initPDADC=%d, currPDADC=%d\n",
  145             __func__, AH5416(ah)->initPDADC, currPDADC);
  146 
  147         if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
  148                 return;
  149 
  150         (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
  151 
  152         if (hpwr_5g)
  153                 delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
  154         else
  155                 delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
  156 
  157         HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
  158             __func__, delta, AH9280(ah)->PDADCdelta);
  159 
  160         if (delta != AH9280(ah)->PDADCdelta) {
  161                 AH9280(ah)->PDADCdelta = delta;
  162                 for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
  163                         regval = AH9280(ah)->originalGain[i] - delta;
  164                         if (regval < 0)
  165                                 regval = 0;
  166 
  167                         OS_REG_RMW_FIELD(ah,
  168                                       AR_PHY_TX_GAIN_TBL1 + i * 4,
  169                                       AR_PHY_TX_GAIN, regval);
  170                 }
  171         }
  172 }
  173 
  174 static int16_t
  175 ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
  176     uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
  177     int16_t *diff)
  178 {
  179         uint16_t k;
  180 
  181         /* Prior to writing the boundaries or the pdadc vs. power table
  182          * into the chip registers the default starting point on the pdadc
  183          * vs. power table needs to be checked and the curve boundaries
  184          * adjusted accordingly
  185          */
  186         if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
  187                 uint16_t gb_limit;
  188 
  189                 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
  190                         /* get the difference in dB */
  191                         *diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
  192                         /* get the number of half dB steps */
  193                         *diff *= 2;
  194                         /* change the original gain boundary settings
  195                          * by the number of half dB steps
  196                          */
  197                         for (k = 0; k < numXpdGain; k++)
  198                                 gb[k] = (uint16_t)(gb[k] - *diff);
  199                 }
  200                 /* Because of a hardware limitation, ensure the gain boundary
  201                  * is not larger than (63 - overlap)
  202                  */
  203                 gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
  204 
  205                 for (k = 0; k < numXpdGain; k++)
  206                         gb[k] = (uint16_t)min(gb_limit, gb[k]);
  207         }
  208 
  209         return *diff;
  210 }
  211 
  212 static void
  213 ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
  214     int16_t diff, uint8_t *pdadcValues)
  215 {
  216 #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
  217         uint16_t k;
  218 
  219         /* If this is a board that has a pwrTableOffset that differs from
  220          * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
  221          * pdadc vs pwr table needs to be adjusted prior to writing to the
  222          * chip.
  223          */
  224         if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
  225                 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
  226                         /* shift the table to start at the new offset */
  227                         for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
  228                                 pdadcValues[k] = pdadcValues[k + diff];
  229                         }
  230 
  231                         /* fill the back of the table */
  232                         for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
  233                                 pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
  234                         }
  235                 }
  236         }
  237 #undef NUM_PDADC
  238 }
  239 /*
  240  * This effectively disables the gain boundaries leaving it
  241  * to the open-loop TX power control.
  242  */
  243 static void
  244 ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
  245     uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
  246 {
  247         int regChainOffset;
  248 
  249         regChainOffset = ar5416GetRegChainOffset(ah, i);
  250 
  251         /* These are unused for OLC */
  252         (void) pdGainOverlap_t2;
  253         (void) gainBoundaries;
  254 
  255         HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
  256             __func__, i);
  257 
  258         OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
  259             SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
  260             SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)  |
  261             SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)  |
  262             SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)  |
  263             SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
  264 }
  265 
  266 /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
  267 /* XXX shouldn't be here! */
  268 #define EEP_MINOR(_ah) \
  269         (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
  270 #define IS_EEP_MINOR_V2(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
  271 #define IS_EEP_MINOR_V3(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
  272 
  273 /**************************************************************
  274  * ar9280SetPowerCalTable
  275  *
  276  * Pull the PDADC piers from cal data and interpolate them across the given
  277  * points as well as from the nearest pier(s) to get a power detector
  278  * linear voltage to power level table.
  279  *
  280  * Handle OLC for Merlin where required.
  281  */
  282 HAL_BOOL
  283 ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
  284         const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
  285 {
  286         CAL_DATA_PER_FREQ *pRawDataset;
  287         uint8_t  *pCalBChans = AH_NULL;
  288         uint16_t pdGainOverlap_t2;
  289         static uint8_t  pdadcValues[AR5416_NUM_PDADC_VALUES];
  290         uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
  291         uint16_t numPiers, i;
  292         int16_t  tMinCalPower;
  293         uint16_t numXpdGain, xpdMask;
  294         uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
  295         uint32_t regChainOffset;
  296         int8_t pwr_table_offset;
  297 
  298         OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
  299             
  300         xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
  301 
  302         (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
  303 
  304         if (IS_EEP_MINOR_V2(ah)) {
  305                 pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
  306         } else { 
  307                 pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
  308         }
  309 
  310         if (IEEE80211_IS_CHAN_2GHZ(chan)) {
  311                 pCalBChans = pEepData->calFreqPier2G;
  312                 numPiers = AR5416_NUM_2G_CAL_PIERS;
  313         } else {
  314                 pCalBChans = pEepData->calFreqPier5G;
  315                 numPiers = AR5416_NUM_5G_CAL_PIERS;
  316         }
  317 
  318         /* If OLC is being done, set the init PDADC value appropriately */
  319         if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
  320             ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
  321                 struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
  322                 AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
  323         } else {
  324                 /*
  325                  * XXX ath9k doesn't clear this for 5ghz mode if
  326                  * it were set in 2ghz mode before!
  327                  * The Merlin OLC temperature compensation code
  328                  * uses this to calculate the PDADC delta during
  329                  * calibration ; 0 here effectively stops the
  330                  * temperature compensation calibration from
  331                  * occurring.
  332                  */
  333                 AH5416(ah)->initPDADC = 0;
  334         }
  335 
  336         /* Calculate the value of xpdgains from the xpdGain Mask */
  337         numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
  338             
  339         /* Write the detector gain biases and their number */
  340         ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
  341 
  342         for (i = 0; i < AR5416_MAX_CHAINS; i++) {
  343                 regChainOffset = ar5416GetRegChainOffset(ah, i);
  344                 if (pEepData->baseEepHeader.txMask & (1 << i)) {
  345                         uint16_t diff;
  346 
  347                         if (IEEE80211_IS_CHAN_2GHZ(chan)) {
  348                                 pRawDataset = pEepData->calPierData2G[i];
  349                         } else {
  350                                 pRawDataset = pEepData->calPierData5G[i];
  351                         }
  352 
  353                         /* Fetch the gain boundaries and the PDADC values */
  354                         if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
  355                             ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
  356                                 uint8_t pcdacIdx;
  357                                 uint8_t txPower;
  358 
  359                                 ar9280olcGetTxGainIndex(ah, chan,
  360                                     (struct calDataPerFreqOpLoop *) pRawDataset,
  361                                     pCalBChans, numPiers, &txPower, &pcdacIdx);
  362                                 ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
  363                         } else {
  364                                 ar5416GetGainBoundariesAndPdadcs(ah,  chan,
  365                                     pRawDataset, pCalBChans, numPiers,
  366                                     pdGainOverlap_t2, &tMinCalPower,
  367                                     gainBoundaries, pdadcValues, numXpdGain);
  368                         }
  369 
  370                         /*
  371                          * Prior to writing the boundaries or the pdadc vs. power table
  372                          * into the chip registers the default starting point on the pdadc
  373                          * vs. power table needs to be checked and the curve boundaries
  374                          * adjusted accordingly
  375                          */
  376                         diff = ar9280ChangeGainBoundarySettings(ah,
  377                             gainBoundaries, numXpdGain, pdGainOverlap_t2,
  378                             pwr_table_offset, &diff);
  379 
  380                         if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
  381                                 /* Set gain boundaries for either open- or closed-loop TPC */
  382                                 if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
  383                                     ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
  384                                         ar9280SetGainBoundariesOpenLoop(ah,
  385                                             i, pdGainOverlap_t2,
  386                                             gainBoundaries);
  387                                 else
  388                                         ar5416SetGainBoundariesClosedLoop(ah,
  389                                             i, pdGainOverlap_t2,
  390                                             gainBoundaries);
  391                         }
  392 
  393                         /*
  394                          * If this is a board that has a pwrTableOffset that differs from
  395                          * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
  396                          * pdadc vs pwr table needs to be adjusted prior to writing to the
  397                          * chip.
  398                          */
  399                         ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
  400 
  401                         /* Write the power values into the baseband power table */
  402                         ar5416WritePdadcValues(ah, i, pdadcValues);
  403                 }
  404         }
  405         *pTxPowerIndexOffset = 0;
  406 
  407         return AH_TRUE;
  408 }

Cache object: 2f6cf1ff3b562e4294214259d2208d60


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.