/*
* Copyright (c) 2010-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "hw.h"
#include "hw-ops.h"
#include "ar9003_phy.h"
#include "ar9003_rtt.h"
#include "ar9003_mci.h"
#define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT
#define MAX_MAG_DELTA 11
#define MAX_PHS_DELTA 10
struct coeff {
int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT];
int iqc_coeff[2];
};
enum ar9003_cal_types {
IQ_MISMATCH_CAL = BIT(0),
TEMP_COMP_CAL = BIT(1),
};
static void ar9003_hw_setup_calibration(struct ath_hw *ah,
struct ath9k_cal_list *currCal)
{
struct ath_common *common = ath9k_hw_common(ah);
/* Select calibration to run */
switch (currCal->calData->calType) {
case IQ_MISMATCH_CAL:
/*
* Start calibration with
* 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples
*/
REG_RMW_FIELD(ah, AR_PHY_TIMING4,
AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX,
currCal->calData->calCountMax);
REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
ath_dbg(common, CALIBRATE,
"starting IQ Mismatch Calibration\n");
/* Kick-off cal */
REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL);
break;
case TEMP_COMP_CAL:
REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM,
AR_PHY_65NM_CH0_THERM_LOCAL, 1);
REG_RMW_FIELD(ah, AR_PHY_65NM_CH0_THERM,
AR_PHY_65NM_CH0_THERM_START, 1);
ath_dbg(common, CALIBRATE,
"starting Temperature Compensation Calibration\n");
break;
}
}
/*
* Generic calibration routine.
* Recalibrate the lower PHY chips to account for temperature/environment
* changes.
*/
static bool ar9003_hw_per_calibration(struct ath_hw *ah,
struct ath9k_channel *ichan,
u8 rxchainmask,
struct ath9k_cal_list *currCal)
{
struct ath9k_hw_cal_data *caldata = ah->caldata;
/* Cal is assumed not done until explicitly set below */
bool iscaldone = false;
/* Calibration in progress. */
if (currCal->calState == CAL_RUNNING) {
/* Check to see if it has finished. */
if (!(REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL)) {
/*
* Accumulate cal measures for active chains
*/
currCal->calData->calCollect(ah);
ah->cal_samples++;
if (ah->cal_samples >=
currCal->calData->calNumSamples) {
unsigned int i, numChains = 0;
for (i = 0; i < AR9300_MAX_CHAINS; i++) {
if (rxchainmask & (1 << i))
numChains++;
}
/*
* Process accumulated data
*/
currCal->calData->calPostProc(ah, numChains);
/* Calibration has finished. */
caldata->CalValid |= currCal->calData->calType;
currCal->calState = CAL_DONE;
iscaldone = true;
} else {
/*
* Set-up collection of another sub-sample until we
* get desired number
*/
ar9003_hw_setup_calibration(ah, currCal);
}
}
} else if (!(caldata->CalValid & currCal->