#include "opt_ah.h"
#include "ah.h"
#include "ah_internal.h"
#include "ah_eeprom_v14.h"
#include "ar9002/ar9280.h"
#include "ar5416/ar5416reg.h"
#include "ar5416/ar5416phy.h"
#include "ar9002/ar9002phy.h"
#include "ar9002/ar9280_olc.h"
void
ar9280olcInit(struct ath_hal *ah)
{
uint32_t i;
if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
return;
HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__);
for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)
AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,
AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);
AH9280(ah)->PDADCdelta = 0;
}
void
ar9280olcGetTxGainIndex(struct ath_hal *ah,
const struct ieee80211_channel *chan,
struct calDataPerFreqOpLoop *rawDatasetOpLoop,
uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)
{
uint8_t pcdac, i = 0;
uint16_t idxL = 0, idxR = 0, numPiers;
HAL_BOOL match;
CHAN_CENTERS centers;
ar5416GetChannelCenters(ah, chan, ¢ers);
for (numPiers = 0; numPiers < availPiers; numPiers++)
if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
break;
match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
&idxL, &idxR);
if (match) {
pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
*pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
} else {
pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
*pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
}
while (pcdac > AH9280(ah)->originalGain[i] &&
i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
i++;
*pcdacIdx = i;
}
void
ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
uint8_t *pPDADCValues)
{
uint32_t i;
uint32_t offset;
OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
offset = txPower;
for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
if (i < offset)
pPDADCValues[i] = 0x0;
else
pPDADCValues[i] = 0xFF;
}
void
ar9280olcTemperatureCompensation(struct ath_hal *ah)
{
uint32_t rddata, i;
int delta, currPDADC, regval;
uint8_t hpwr_5g = 0;
if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
return;
rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
HALDEBUG(ah, HAL_DEBUG_PERCAL,
"%s: called: initPDADC=%d, currPDADC=%d\n",
__func__, AH5416(ah)->initPDADC, currPDADC);
if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
return;
(void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
if (hpwr_5g)
delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
else
delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
__func__, delta, AH9280(ah)->PDADCdelta);
if (delta != AH9280(ah)->PDADCdelta) {
AH9280(ah)->PDADCdelta = delta;
for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
regval = AH9280(ah)->originalGain[i] - delta;
if (regval < 0)
regval = 0;
OS_REG_RMW_FIELD(ah,
AR_PHY_TX_GAIN_TBL1 + i * 4,
AR_PHY_TX_GAIN, regval);
}
}
}
static int16_t
ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
int16_t *diff)
{
uint16_t k;
if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
uint16_t gb_limit;
if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
*diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
*diff *= 2;
for (k = 0; k < numXpdGain; k++)
gb[k] = (uint16_t)(gb[k] - *diff);
}
gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
for (k = 0; k < numXpdGain; k++)
gb[k] = (uint16_t)min(gb_limit, gb[k]);
}
return *diff;
}
static void
ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
int16_t diff, uint8_t *pdadcValues)
{
#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
uint16_t k;
if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
pdadcValues[k] = pdadcValues[k + diff];
}
for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
}
}
}
#undef NUM_PDADC
}
static void
ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
{
int regChainOffset;
regChainOffset = ar5416GetRegChainOffset(ah, i);
(void) pdGainOverlap_t2;
(void) gainBoundaries;
HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
__func__, i);
OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) |
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) |
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) |
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
}
#define EEP_MINOR(_ah) \
(AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
HAL_BOOL
ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
{
CAL_DATA_PER_FREQ *pRawDataset;
uint8_t *pCalBChans = AH_NULL;
uint16_t pdGainOverlap_t2;
static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES];
uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
uint16_t numPiers, i;
int16_t tMinCalPower;
uint16_t numXpdGain, xpdMask;
uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
uint32_t regChainOffset;
int8_t pwr_table_offset;
OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
if (IS_EEP_MINOR_V2(ah)) {
pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
} else {
pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
}
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
pCalBChans = pEepData->calFreqPier2G;
numPiers = AR5416_NUM_2G_CAL_PIERS;
} else {
pCalBChans = pEepData->calFreqPier5G;
numPiers = AR5416_NUM_5G_CAL_PIERS;
}
if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
} else {
AH5416(ah)->initPDADC = 0;
}
numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
regChainOffset = ar5416GetRegChainOffset(ah, i);
if (pEepData->baseEepHeader.txMask & (1 << i)) {
uint16_t diff;
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
pRawDataset = pEepData->calPierData2G[i];
} else {
pRawDataset = pEepData->calPierData5G[i];
}
if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
uint8_t pcdacIdx;
uint8_t txPower;
ar9280olcGetTxGainIndex(ah, chan,
(struct calDataPerFreqOpLoop *) pRawDataset,
pCalBChans, numPiers, &txPower, &pcdacIdx);
ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
} else {
ar5416GetGainBoundariesAndPdadcs(ah, chan,
pRawDataset, pCalBChans, numPiers,
pdGainOverlap_t2, &tMinCalPower,
gainBoundaries, pdadcValues, numXpdGain);
}
diff = ar9280ChangeGainBoundarySettings(ah,
gainBoundaries, numXpdGain, pdGainOverlap_t2,
pwr_table_offset, &diff);
if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
ar9280SetGainBoundariesOpenLoop(ah,
i, pdGainOverlap_t2,
gainBoundaries);
else
ar5416SetGainBoundariesClosedLoop(ah,
i, pdGainOverlap_t2,
gainBoundaries);
}
ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
ar5416WritePdadcValues(ah, i, pdadcValues);
}
}
*pTxPowerIndexOffset = 0;
return AH_TRUE;
}