Path: blob/main/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*/26#include "opt_ah.h"2728#include "ah.h"29#include "ah_internal.h"3031#include "ah_eeprom_v14.h"3233#include "ar9002/ar9280.h"34#include "ar5416/ar5416reg.h"35#include "ar5416/ar5416phy.h"36#include "ar9002/ar9002phy.h"3738#include "ar9002/ar9280_olc.h"3940void41ar9280olcInit(struct ath_hal *ah)42{43uint32_t i;4445/* Only do OLC if it's enabled for this chipset */46if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))47return;4849HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__);5051for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)52AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,53AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);5455AH9280(ah)->PDADCdelta = 0;56}5758void59ar9280olcGetTxGainIndex(struct ath_hal *ah,60const struct ieee80211_channel *chan,61struct calDataPerFreqOpLoop *rawDatasetOpLoop,62uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)63{64uint8_t pcdac, i = 0;65uint16_t idxL = 0, idxR = 0, numPiers;66HAL_BOOL match;67CHAN_CENTERS centers;6869ar5416GetChannelCenters(ah, chan, ¢ers);7071for (numPiers = 0; numPiers < availPiers; numPiers++)72if (calChans[numPiers] == AR5416_BCHAN_UNUSED)73break;7475match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,76IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,77&idxL, &idxR);78if (match) {79pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];80*pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];81} else {82pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];83*pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +84rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;85}86while (pcdac > AH9280(ah)->originalGain[i] &&87i < (AR9280_TX_GAIN_TABLE_SIZE - 1))88i++;8990*pcdacIdx = i;91}9293/*94* XXX txPower here is likely not the target txPower in the traditional95* XXX sense, but is set by a call to ar9280olcGetTxGainIndex().96* XXX Thus, be careful if you're trying to use this routine yourself.97*/98void99ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,100uint8_t *pPDADCValues)101{102uint32_t i;103uint32_t offset;104105OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);106OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);107108OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);109110offset = txPower;111for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)112if (i < offset)113pPDADCValues[i] = 0x0;114else115pPDADCValues[i] = 0xFF;116}117118/*119* Run temperature compensation calibration.120*121* The TX gain table is adjusted depending upon the difference122* between the initial PDADC value and the currently read123* average TX power sample value. This value is only valid if124* frames have been transmitted, so currPDADC will be 0 if125* no frames have yet been transmitted.126*/127void128ar9280olcTemperatureCompensation(struct ath_hal *ah)129{130uint32_t rddata, i;131int delta, currPDADC, regval;132uint8_t hpwr_5g = 0;133134if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))135return;136137rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);138currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);139140HALDEBUG(ah, HAL_DEBUG_PERCAL,141"%s: called: initPDADC=%d, currPDADC=%d\n",142__func__, AH5416(ah)->initPDADC, currPDADC);143144if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)145return;146147(void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));148149if (hpwr_5g)150delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;151else152delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;153154HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",155__func__, delta, AH9280(ah)->PDADCdelta);156157if (delta != AH9280(ah)->PDADCdelta) {158AH9280(ah)->PDADCdelta = delta;159for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {160regval = AH9280(ah)->originalGain[i] - delta;161if (regval < 0)162regval = 0;163164OS_REG_RMW_FIELD(ah,165AR_PHY_TX_GAIN_TBL1 + i * 4,166AR_PHY_TX_GAIN, regval);167}168}169}170171static int16_t172ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,173uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,174int16_t *diff)175{176uint16_t k;177178/* Prior to writing the boundaries or the pdadc vs. power table179* into the chip registers the default starting point on the pdadc180* vs. power table needs to be checked and the curve boundaries181* adjusted accordingly182*/183if (AR_SREV_MERLIN_20_OR_LATER(ah)) {184uint16_t gb_limit;185186if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {187/* get the difference in dB */188*diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);189/* get the number of half dB steps */190*diff *= 2;191/* change the original gain boundary settings192* by the number of half dB steps193*/194for (k = 0; k < numXpdGain; k++)195gb[k] = (uint16_t)(gb[k] - *diff);196}197/* Because of a hardware limitation, ensure the gain boundary198* is not larger than (63 - overlap)199*/200gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);201202for (k = 0; k < numXpdGain; k++)203gb[k] = (uint16_t)min(gb_limit, gb[k]);204}205206return *diff;207}208209static void210ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,211int16_t diff, uint8_t *pdadcValues)212{213#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)214uint16_t k;215216/* If this is a board that has a pwrTableOffset that differs from217* the default AR5416_PWR_TABLE_OFFSET_DB then the start of the218* pdadc vs pwr table needs to be adjusted prior to writing to the219* chip.220*/221if (AR_SREV_MERLIN_20_OR_LATER(ah)) {222if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {223/* shift the table to start at the new offset */224for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {225pdadcValues[k] = pdadcValues[k + diff];226}227228/* fill the back of the table */229for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {230pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];231}232}233}234#undef NUM_PDADC235}236/*237* This effectively disables the gain boundaries leaving it238* to the open-loop TX power control.239*/240static void241ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,242uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])243{244int regChainOffset;245246regChainOffset = ar5416GetRegChainOffset(ah, i);247248/* These are unused for OLC */249(void) pdGainOverlap_t2;250(void) gainBoundaries;251252HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",253__func__, i);254255OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,256SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |257SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) |258SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) |259SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) |260SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));261}262263/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */264/* XXX shouldn't be here! */265#define EEP_MINOR(_ah) \266(AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)267#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)268#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)269270/**************************************************************271* ar9280SetPowerCalTable272*273* Pull the PDADC piers from cal data and interpolate them across the given274* points as well as from the nearest pier(s) to get a power detector275* linear voltage to power level table.276*277* Handle OLC for Merlin where required.278*/279HAL_BOOL280ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,281const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)282{283CAL_DATA_PER_FREQ *pRawDataset;284uint8_t *pCalBChans = AH_NULL;285uint16_t pdGainOverlap_t2;286static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES];287uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];288uint16_t numPiers, i;289int16_t tMinCalPower;290uint16_t numXpdGain, xpdMask;291uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];292uint32_t regChainOffset;293int8_t pwr_table_offset;294295OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));296297xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;298299(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);300301if (IS_EEP_MINOR_V2(ah)) {302pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;303} else {304pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));305}306307if (IEEE80211_IS_CHAN_2GHZ(chan)) {308pCalBChans = pEepData->calFreqPier2G;309numPiers = AR5416_NUM_2G_CAL_PIERS;310} else {311pCalBChans = pEepData->calFreqPier5G;312numPiers = AR5416_NUM_5G_CAL_PIERS;313}314315/* If OLC is being done, set the init PDADC value appropriately */316if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&317ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {318struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];319AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];320} else {321/*322* XXX ath9k doesn't clear this for 5ghz mode if323* it were set in 2ghz mode before!324* The Merlin OLC temperature compensation code325* uses this to calculate the PDADC delta during326* calibration ; 0 here effectively stops the327* temperature compensation calibration from328* occurring.329*/330AH5416(ah)->initPDADC = 0;331}332333/* Calculate the value of xpdgains from the xpdGain Mask */334numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);335336/* Write the detector gain biases and their number */337ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);338339for (i = 0; i < AR5416_MAX_CHAINS; i++) {340regChainOffset = ar5416GetRegChainOffset(ah, i);341if (pEepData->baseEepHeader.txMask & (1 << i)) {342uint16_t diff;343344if (IEEE80211_IS_CHAN_2GHZ(chan)) {345pRawDataset = pEepData->calPierData2G[i];346} else {347pRawDataset = pEepData->calPierData5G[i];348}349350/* Fetch the gain boundaries and the PDADC values */351if (AR_SREV_MERLIN_20_OR_LATER(ah) &&352ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {353uint8_t pcdacIdx;354uint8_t txPower;355356ar9280olcGetTxGainIndex(ah, chan,357(struct calDataPerFreqOpLoop *) pRawDataset,358pCalBChans, numPiers, &txPower, &pcdacIdx);359ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);360} else {361ar5416GetGainBoundariesAndPdadcs(ah, chan,362pRawDataset, pCalBChans, numPiers,363pdGainOverlap_t2, &tMinCalPower,364gainBoundaries, pdadcValues, numXpdGain);365}366367/*368* Prior to writing the boundaries or the pdadc vs. power table369* into the chip registers the default starting point on the pdadc370* vs. power table needs to be checked and the curve boundaries371* adjusted accordingly372*/373diff = ar9280ChangeGainBoundarySettings(ah,374gainBoundaries, numXpdGain, pdGainOverlap_t2,375pwr_table_offset, &diff);376377if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {378/* Set gain boundaries for either open- or closed-loop TPC */379if (AR_SREV_MERLIN_20_OR_LATER(ah) &&380ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))381ar9280SetGainBoundariesOpenLoop(ah,382i, pdGainOverlap_t2,383gainBoundaries);384else385ar5416SetGainBoundariesClosedLoop(ah,386i, pdGainOverlap_t2,387gainBoundaries);388}389390/*391* If this is a board that has a pwrTableOffset that differs from392* the default AR5416_PWR_TABLE_OFFSET_DB then the start of the393* pdadc vs pwr table needs to be adjusted prior to writing to the394* chip.395*/396ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);397398/* Write the power values into the baseband power table */399ar5416WritePdadcValues(ah, i, pdadcValues);400}401}402*pTxPowerIndexOffset = 0;403404return AH_TRUE;405}406407408