18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2010-2011 Atheros Communications Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "hw.h" 188c2ecf20Sopenharmony_ci#include "hw-ops.h" 198c2ecf20Sopenharmony_ci#include "ar9003_phy.h" 208c2ecf20Sopenharmony_ci#include "ar9003_rtt.h" 218c2ecf20Sopenharmony_ci#include "ar9003_mci.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define MAX_MEASUREMENT MAX_IQCAL_MEASUREMENT 248c2ecf20Sopenharmony_ci#define MAX_MAG_DELTA 11 258c2ecf20Sopenharmony_ci#define MAX_PHS_DELTA 10 268c2ecf20Sopenharmony_ci#define MAXIQCAL 3 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct coeff { 298c2ecf20Sopenharmony_ci int mag_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; 308c2ecf20Sopenharmony_ci int phs_coeff[AR9300_MAX_CHAINS][MAX_MEASUREMENT][MAXIQCAL]; 318c2ecf20Sopenharmony_ci int iqc_coeff[2]; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cienum ar9003_cal_types { 358c2ecf20Sopenharmony_ci IQ_MISMATCH_CAL = BIT(0), 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic void ar9003_hw_setup_calibration(struct ath_hw *ah, 398c2ecf20Sopenharmony_ci struct ath9k_cal_list *currCal) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* Select calibration to run */ 448c2ecf20Sopenharmony_ci switch (currCal->calData->calType) { 458c2ecf20Sopenharmony_ci case IQ_MISMATCH_CAL: 468c2ecf20Sopenharmony_ci /* 478c2ecf20Sopenharmony_ci * Start calibration with 488c2ecf20Sopenharmony_ci * 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_TIMING4, 518c2ecf20Sopenharmony_ci AR_PHY_TIMING4_IQCAL_LOG_COUNT_MAX, 528c2ecf20Sopenharmony_ci currCal->calData->calCountMax); 538c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 568c2ecf20Sopenharmony_ci "starting IQ Mismatch Calibration\n"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* Kick-off cal */ 598c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_TIMING4, AR_PHY_TIMING4_DO_CAL); 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci default: 628c2ecf20Sopenharmony_ci ath_err(common, "Invalid calibration type\n"); 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * Generic calibration routine. 698c2ecf20Sopenharmony_ci * Recalibrate the lower PHY chips to account for temperature/environment 708c2ecf20Sopenharmony_ci * changes. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic bool ar9003_hw_per_calibration(struct ath_hw *ah, 738c2ecf20Sopenharmony_ci struct ath9k_channel *ichan, 748c2ecf20Sopenharmony_ci u8 rxchainmask, 758c2ecf20Sopenharmony_ci struct ath9k_cal_list *currCal) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 788c2ecf20Sopenharmony_ci const struct ath9k_percal_data *cur_caldata = currCal->calData; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Calibration in progress. */ 818c2ecf20Sopenharmony_ci if (currCal->calState == CAL_RUNNING) { 828c2ecf20Sopenharmony_ci /* Check to see if it has finished. */ 838c2ecf20Sopenharmony_ci if (REG_READ(ah, AR_PHY_TIMING4) & AR_PHY_TIMING4_DO_CAL) 848c2ecf20Sopenharmony_ci return false; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * Accumulate cal measures for active chains 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci cur_caldata->calCollect(ah); 908c2ecf20Sopenharmony_ci ah->cal_samples++; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (ah->cal_samples >= cur_caldata->calNumSamples) { 938c2ecf20Sopenharmony_ci unsigned int i, numChains = 0; 948c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 958c2ecf20Sopenharmony_ci if (rxchainmask & (1 << i)) 968c2ecf20Sopenharmony_ci numChains++; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * Process accumulated data 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci cur_caldata->calPostProc(ah, numChains); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* Calibration has finished. */ 1058c2ecf20Sopenharmony_ci caldata->CalValid |= cur_caldata->calType; 1068c2ecf20Sopenharmony_ci currCal->calState = CAL_DONE; 1078c2ecf20Sopenharmony_ci return true; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * Set-up collection of another sub-sample until we 1118c2ecf20Sopenharmony_ci * get desired number 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci ar9003_hw_setup_calibration(ah, currCal); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci } else if (!(caldata->CalValid & cur_caldata->calType)) { 1168c2ecf20Sopenharmony_ci /* If current cal is marked invalid in channel, kick it off */ 1178c2ecf20Sopenharmony_ci ath9k_hw_reset_calibration(ah, currCal); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return false; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int ar9003_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, 1248c2ecf20Sopenharmony_ci u8 rxchainmask, bool longcal) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci bool iscaldone = true; 1278c2ecf20Sopenharmony_ci struct ath9k_cal_list *currCal = ah->cal_list_curr; 1288c2ecf20Sopenharmony_ci int ret; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * For given calibration: 1328c2ecf20Sopenharmony_ci * 1. Call generic cal routine 1338c2ecf20Sopenharmony_ci * 2. When this cal is done (isCalDone) if we have more cals waiting 1348c2ecf20Sopenharmony_ci * (eg after reset), mask this to upper layers by not propagating 1358c2ecf20Sopenharmony_ci * isCalDone if it is set to TRUE. 1368c2ecf20Sopenharmony_ci * Instead, change isCalDone to FALSE and setup the waiting cal(s) 1378c2ecf20Sopenharmony_ci * to be run. 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_ci if (currCal && 1408c2ecf20Sopenharmony_ci (currCal->calState == CAL_RUNNING || 1418c2ecf20Sopenharmony_ci currCal->calState == CAL_WAITING)) { 1428c2ecf20Sopenharmony_ci iscaldone = ar9003_hw_per_calibration(ah, chan, 1438c2ecf20Sopenharmony_ci rxchainmask, currCal); 1448c2ecf20Sopenharmony_ci if (iscaldone) { 1458c2ecf20Sopenharmony_ci ah->cal_list_curr = currCal = currCal->calNext; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (currCal->calState == CAL_WAITING) { 1488c2ecf20Sopenharmony_ci iscaldone = false; 1498c2ecf20Sopenharmony_ci ath9k_hw_reset_calibration(ah, currCal); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * Do NF cal only at longer intervals. Get the value from 1568c2ecf20Sopenharmony_ci * the previous NF cal and update history buffer. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci if (longcal && ath9k_hw_getnf(ah, chan)) { 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Load the NF from history buffer of the current channel. 1618c2ecf20Sopenharmony_ci * NF is slow time-variant, so it is OK to use a historical 1628c2ecf20Sopenharmony_ci * value. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci ret = ath9k_hw_loadnf(ah, ah->curchan); 1658c2ecf20Sopenharmony_ci if (ret < 0) 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* start NF calibration, without updating BB NF register */ 1698c2ecf20Sopenharmony_ci ath9k_hw_start_nfcal(ah, false); 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return iscaldone; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void ar9003_hw_iqcal_collect(struct ath_hw *ah) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci int i; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Accumulate IQ cal measures for active chains */ 1808c2ecf20Sopenharmony_ci for (i = 0; i < AR5416_MAX_CHAINS; i++) { 1818c2ecf20Sopenharmony_ci if (ah->txchainmask & BIT(i)) { 1828c2ecf20Sopenharmony_ci ah->totalPowerMeasI[i] += 1838c2ecf20Sopenharmony_ci REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); 1848c2ecf20Sopenharmony_ci ah->totalPowerMeasQ[i] += 1858c2ecf20Sopenharmony_ci REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); 1868c2ecf20Sopenharmony_ci ah->totalIqCorrMeas[i] += 1878c2ecf20Sopenharmony_ci (int32_t) REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); 1888c2ecf20Sopenharmony_ci ath_dbg(ath9k_hw_common(ah), CALIBRATE, 1898c2ecf20Sopenharmony_ci "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", 1908c2ecf20Sopenharmony_ci ah->cal_samples, i, ah->totalPowerMeasI[i], 1918c2ecf20Sopenharmony_ci ah->totalPowerMeasQ[i], 1928c2ecf20Sopenharmony_ci ah->totalIqCorrMeas[i]); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void ar9003_hw_iqcalibrate(struct ath_hw *ah, u8 numChains) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 2008c2ecf20Sopenharmony_ci u32 powerMeasQ, powerMeasI, iqCorrMeas; 2018c2ecf20Sopenharmony_ci u32 qCoffDenom, iCoffDenom; 2028c2ecf20Sopenharmony_ci int32_t qCoff, iCoff; 2038c2ecf20Sopenharmony_ci int iqCorrNeg, i; 2048c2ecf20Sopenharmony_ci static const u_int32_t offset_array[3] = { 2058c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_B0, 2068c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_B1, 2078c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_B2, 2088c2ecf20Sopenharmony_ci }; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < numChains; i++) { 2118c2ecf20Sopenharmony_ci powerMeasI = ah->totalPowerMeasI[i]; 2128c2ecf20Sopenharmony_ci powerMeasQ = ah->totalPowerMeasQ[i]; 2138c2ecf20Sopenharmony_ci iqCorrMeas = ah->totalIqCorrMeas[i]; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 2168c2ecf20Sopenharmony_ci "Starting IQ Cal and Correction for Chain %d\n", i); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 2198c2ecf20Sopenharmony_ci "Original: Chn %d iq_corr_meas = 0x%08x\n", 2208c2ecf20Sopenharmony_ci i, ah->totalIqCorrMeas[i]); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci iqCorrNeg = 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (iqCorrMeas > 0x80000000) { 2258c2ecf20Sopenharmony_ci iqCorrMeas = (0xffffffff - iqCorrMeas) + 1; 2268c2ecf20Sopenharmony_ci iqCorrNeg = 1; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_i = 0x%08x\n", 2308c2ecf20Sopenharmony_ci i, powerMeasI); 2318c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Chn %d pwr_meas_q = 0x%08x\n", 2328c2ecf20Sopenharmony_ci i, powerMeasQ); 2338c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "iqCorrNeg is 0x%08x\n", iqCorrNeg); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 256; 2368c2ecf20Sopenharmony_ci qCoffDenom = powerMeasQ / 64; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if ((iCoffDenom != 0) && (qCoffDenom != 0)) { 2398c2ecf20Sopenharmony_ci iCoff = iqCorrMeas / iCoffDenom; 2408c2ecf20Sopenharmony_ci qCoff = powerMeasI / qCoffDenom - 64; 2418c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Chn %d iCoff = 0x%08x\n", 2428c2ecf20Sopenharmony_ci i, iCoff); 2438c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Chn %d qCoff = 0x%08x\n", 2448c2ecf20Sopenharmony_ci i, qCoff); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Force bounds on iCoff */ 2478c2ecf20Sopenharmony_ci if (iCoff >= 63) 2488c2ecf20Sopenharmony_ci iCoff = 63; 2498c2ecf20Sopenharmony_ci else if (iCoff <= -63) 2508c2ecf20Sopenharmony_ci iCoff = -63; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* Negate iCoff if iqCorrNeg == 0 */ 2538c2ecf20Sopenharmony_ci if (iqCorrNeg == 0x0) 2548c2ecf20Sopenharmony_ci iCoff = -iCoff; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Force bounds on qCoff */ 2578c2ecf20Sopenharmony_ci if (qCoff >= 63) 2588c2ecf20Sopenharmony_ci qCoff = 63; 2598c2ecf20Sopenharmony_ci else if (qCoff <= -63) 2608c2ecf20Sopenharmony_ci qCoff = -63; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci iCoff = iCoff & 0x7f; 2638c2ecf20Sopenharmony_ci qCoff = qCoff & 0x7f; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 2668c2ecf20Sopenharmony_ci "Chn %d : iCoff = 0x%x qCoff = 0x%x\n", 2678c2ecf20Sopenharmony_ci i, iCoff, qCoff); 2688c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 2698c2ecf20Sopenharmony_ci "Register offset (0x%04x) before update = 0x%x\n", 2708c2ecf20Sopenharmony_ci offset_array[i], 2718c2ecf20Sopenharmony_ci REG_READ(ah, offset_array[i])); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (AR_SREV_9565(ah) && 2748c2ecf20Sopenharmony_ci (iCoff == 63 || qCoff == 63 || 2758c2ecf20Sopenharmony_ci iCoff == -63 || qCoff == -63)) 2768c2ecf20Sopenharmony_ci return; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, offset_array[i], 2798c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, 2808c2ecf20Sopenharmony_ci iCoff); 2818c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, offset_array[i], 2828c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, 2838c2ecf20Sopenharmony_ci qCoff); 2848c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 2858c2ecf20Sopenharmony_ci "Register offset (0x%04x) QI COFF (bitfields 0x%08x) after update = 0x%x\n", 2868c2ecf20Sopenharmony_ci offset_array[i], 2878c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_IQCORR_Q_I_COFF, 2888c2ecf20Sopenharmony_ci REG_READ(ah, offset_array[i])); 2898c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 2908c2ecf20Sopenharmony_ci "Register offset (0x%04x) QQ COFF (bitfields 0x%08x) after update = 0x%x\n", 2918c2ecf20Sopenharmony_ci offset_array[i], 2928c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_IQCORR_Q_Q_COFF, 2938c2ecf20Sopenharmony_ci REG_READ(ah, offset_array[i])); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 2968c2ecf20Sopenharmony_ci "IQ Cal and Correction done for Chain %d\n", i); 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_RX_IQCAL_CORR_B0, 3018c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE); 3028c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 3038c2ecf20Sopenharmony_ci "IQ Cal and Correction (offset 0x%04x) enabled (bit position 0x%08x). New Value 0x%08x\n", 3048c2ecf20Sopenharmony_ci (unsigned) (AR_PHY_RX_IQCAL_CORR_B0), 3058c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_IQCORR_ENABLE, 3068c2ecf20Sopenharmony_ci REG_READ(ah, AR_PHY_RX_IQCAL_CORR_B0)); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct ath9k_percal_data iq_cal_single_sample = { 3108c2ecf20Sopenharmony_ci IQ_MISMATCH_CAL, 3118c2ecf20Sopenharmony_ci MIN_CAL_SAMPLES, 3128c2ecf20Sopenharmony_ci PER_MAX_LOG_COUNT, 3138c2ecf20Sopenharmony_ci ar9003_hw_iqcal_collect, 3148c2ecf20Sopenharmony_ci ar9003_hw_iqcalibrate 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic void ar9003_hw_init_cal_settings(struct ath_hw *ah) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci ah->iq_caldata.calData = &iq_cal_single_sample; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (AR_SREV_9300_20_OR_LATER(ah)) { 3228c2ecf20Sopenharmony_ci ah->enabled_cals |= TX_IQ_CAL; 3238c2ecf20Sopenharmony_ci if (AR_SREV_9485_OR_LATER(ah) && !AR_SREV_9340(ah)) 3248c2ecf20Sopenharmony_ci ah->enabled_cals |= TX_IQ_ON_AGC_CAL; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ah->supp_cals = IQ_MISMATCH_CAL; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci#define OFF_UPPER_LT 24 3318c2ecf20Sopenharmony_ci#define OFF_LOWER_LT 7 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic bool ar9003_hw_dynamic_osdac_selection(struct ath_hw *ah, 3348c2ecf20Sopenharmony_ci bool txiqcal_done) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 3378c2ecf20Sopenharmony_ci int ch0_done, osdac_ch0, dc_off_ch0_i1, dc_off_ch0_q1, dc_off_ch0_i2, 3388c2ecf20Sopenharmony_ci dc_off_ch0_q2, dc_off_ch0_i3, dc_off_ch0_q3; 3398c2ecf20Sopenharmony_ci int ch1_done, osdac_ch1, dc_off_ch1_i1, dc_off_ch1_q1, dc_off_ch1_i2, 3408c2ecf20Sopenharmony_ci dc_off_ch1_q2, dc_off_ch1_i3, dc_off_ch1_q3; 3418c2ecf20Sopenharmony_ci int ch2_done, osdac_ch2, dc_off_ch2_i1, dc_off_ch2_q1, dc_off_ch2_i2, 3428c2ecf20Sopenharmony_ci dc_off_ch2_q2, dc_off_ch2_i3, dc_off_ch2_q3; 3438c2ecf20Sopenharmony_ci bool status; 3448c2ecf20Sopenharmony_ci u32 temp, val; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* 3478c2ecf20Sopenharmony_ci * Clear offset and IQ calibration, run AGC cal. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, 3508c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_OFFSET_CAL); 3518c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, 3528c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); 3538c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_AGC_CONTROL, 3548c2ecf20Sopenharmony_ci REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, 3578c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_CAL, 3588c2ecf20Sopenharmony_ci 0, AH_WAIT_TIMEOUT); 3598c2ecf20Sopenharmony_ci if (!status) { 3608c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 3618c2ecf20Sopenharmony_ci "AGC cal without offset cal failed to complete in 1ms"); 3628c2ecf20Sopenharmony_ci return false; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* 3668c2ecf20Sopenharmony_ci * Allow only offset calibration and disable the others 3678c2ecf20Sopenharmony_ci * (Carrier Leak calibration, TX Filter calibration and 3688c2ecf20Sopenharmony_ci * Peak Detector offset calibration). 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, 3718c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_OFFSET_CAL); 3728c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, 3738c2ecf20Sopenharmony_ci AR_PHY_CL_CAL_ENABLE); 3748c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, 3758c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_FLTR_CAL); 3768c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, 3778c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_PKDET_CAL); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci ch0_done = 0; 3808c2ecf20Sopenharmony_ci ch1_done = 0; 3818c2ecf20Sopenharmony_ci ch2_done = 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci while ((ch0_done == 0) || (ch1_done == 0) || (ch2_done == 0)) { 3848c2ecf20Sopenharmony_ci osdac_ch0 = (REG_READ(ah, AR_PHY_65NM_CH0_BB1) >> 30) & 0x3; 3858c2ecf20Sopenharmony_ci osdac_ch1 = (REG_READ(ah, AR_PHY_65NM_CH1_BB1) >> 30) & 0x3; 3868c2ecf20Sopenharmony_ci osdac_ch2 = (REG_READ(ah, AR_PHY_65NM_CH2_BB1) >> 30) & 0x3; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_AGC_CONTROL, 3918c2ecf20Sopenharmony_ci REG_READ(ah, AR_PHY_AGC_CONTROL) | AR_PHY_AGC_CONTROL_CAL); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, 3948c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_CAL, 3958c2ecf20Sopenharmony_ci 0, AH_WAIT_TIMEOUT); 3968c2ecf20Sopenharmony_ci if (!status) { 3978c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 3988c2ecf20Sopenharmony_ci "DC offset cal failed to complete in 1ms"); 3998c2ecf20Sopenharmony_ci return false; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * High gain. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, 4088c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (1 << 8))); 4098c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, 4108c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (1 << 8))); 4118c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, 4128c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (1 << 8))); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); 4158c2ecf20Sopenharmony_ci dc_off_ch0_i1 = (temp >> 26) & 0x1f; 4168c2ecf20Sopenharmony_ci dc_off_ch0_q1 = (temp >> 21) & 0x1f; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); 4198c2ecf20Sopenharmony_ci dc_off_ch1_i1 = (temp >> 26) & 0x1f; 4208c2ecf20Sopenharmony_ci dc_off_ch1_q1 = (temp >> 21) & 0x1f; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); 4238c2ecf20Sopenharmony_ci dc_off_ch2_i1 = (temp >> 26) & 0x1f; 4248c2ecf20Sopenharmony_ci dc_off_ch2_q1 = (temp >> 21) & 0x1f; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* 4278c2ecf20Sopenharmony_ci * Low gain. 4288c2ecf20Sopenharmony_ci */ 4298c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, 4308c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (2 << 8))); 4318c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, 4328c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (2 << 8))); 4338c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, 4348c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (2 << 8))); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); 4378c2ecf20Sopenharmony_ci dc_off_ch0_i2 = (temp >> 26) & 0x1f; 4388c2ecf20Sopenharmony_ci dc_off_ch0_q2 = (temp >> 21) & 0x1f; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); 4418c2ecf20Sopenharmony_ci dc_off_ch1_i2 = (temp >> 26) & 0x1f; 4428c2ecf20Sopenharmony_ci dc_off_ch1_q2 = (temp >> 21) & 0x1f; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); 4458c2ecf20Sopenharmony_ci dc_off_ch2_i2 = (temp >> 26) & 0x1f; 4468c2ecf20Sopenharmony_ci dc_off_ch2_q2 = (temp >> 21) & 0x1f; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* 4498c2ecf20Sopenharmony_ci * Loopback. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH0_BB3, 4528c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH0_BB3) & 0xfffffcff) | (3 << 8))); 4538c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH1_BB3, 4548c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH1_BB3) & 0xfffffcff) | (3 << 8))); 4558c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH2_BB3, 4568c2ecf20Sopenharmony_ci ((REG_READ(ah, AR_PHY_65NM_CH2_BB3) & 0xfffffcff) | (3 << 8))); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH0_BB3); 4598c2ecf20Sopenharmony_ci dc_off_ch0_i3 = (temp >> 26) & 0x1f; 4608c2ecf20Sopenharmony_ci dc_off_ch0_q3 = (temp >> 21) & 0x1f; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH1_BB3); 4638c2ecf20Sopenharmony_ci dc_off_ch1_i3 = (temp >> 26) & 0x1f; 4648c2ecf20Sopenharmony_ci dc_off_ch1_q3 = (temp >> 21) & 0x1f; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci temp = REG_READ(ah, AR_PHY_65NM_CH2_BB3); 4678c2ecf20Sopenharmony_ci dc_off_ch2_i3 = (temp >> 26) & 0x1f; 4688c2ecf20Sopenharmony_ci dc_off_ch2_q3 = (temp >> 21) & 0x1f; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if ((dc_off_ch0_i1 > OFF_UPPER_LT) || (dc_off_ch0_i1 < OFF_LOWER_LT) || 4718c2ecf20Sopenharmony_ci (dc_off_ch0_i2 > OFF_UPPER_LT) || (dc_off_ch0_i2 < OFF_LOWER_LT) || 4728c2ecf20Sopenharmony_ci (dc_off_ch0_i3 > OFF_UPPER_LT) || (dc_off_ch0_i3 < OFF_LOWER_LT) || 4738c2ecf20Sopenharmony_ci (dc_off_ch0_q1 > OFF_UPPER_LT) || (dc_off_ch0_q1 < OFF_LOWER_LT) || 4748c2ecf20Sopenharmony_ci (dc_off_ch0_q2 > OFF_UPPER_LT) || (dc_off_ch0_q2 < OFF_LOWER_LT) || 4758c2ecf20Sopenharmony_ci (dc_off_ch0_q3 > OFF_UPPER_LT) || (dc_off_ch0_q3 < OFF_LOWER_LT)) { 4768c2ecf20Sopenharmony_ci if (osdac_ch0 == 3) { 4778c2ecf20Sopenharmony_ci ch0_done = 1; 4788c2ecf20Sopenharmony_ci } else { 4798c2ecf20Sopenharmony_ci osdac_ch0++; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci val = REG_READ(ah, AR_PHY_65NM_CH0_BB1) & 0x3fffffff; 4828c2ecf20Sopenharmony_ci val |= (osdac_ch0 << 30); 4838c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH0_BB1, val); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci ch0_done = 0; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } else { 4888c2ecf20Sopenharmony_ci ch0_done = 1; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if ((dc_off_ch1_i1 > OFF_UPPER_LT) || (dc_off_ch1_i1 < OFF_LOWER_LT) || 4928c2ecf20Sopenharmony_ci (dc_off_ch1_i2 > OFF_UPPER_LT) || (dc_off_ch1_i2 < OFF_LOWER_LT) || 4938c2ecf20Sopenharmony_ci (dc_off_ch1_i3 > OFF_UPPER_LT) || (dc_off_ch1_i3 < OFF_LOWER_LT) || 4948c2ecf20Sopenharmony_ci (dc_off_ch1_q1 > OFF_UPPER_LT) || (dc_off_ch1_q1 < OFF_LOWER_LT) || 4958c2ecf20Sopenharmony_ci (dc_off_ch1_q2 > OFF_UPPER_LT) || (dc_off_ch1_q2 < OFF_LOWER_LT) || 4968c2ecf20Sopenharmony_ci (dc_off_ch1_q3 > OFF_UPPER_LT) || (dc_off_ch1_q3 < OFF_LOWER_LT)) { 4978c2ecf20Sopenharmony_ci if (osdac_ch1 == 3) { 4988c2ecf20Sopenharmony_ci ch1_done = 1; 4998c2ecf20Sopenharmony_ci } else { 5008c2ecf20Sopenharmony_ci osdac_ch1++; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci val = REG_READ(ah, AR_PHY_65NM_CH1_BB1) & 0x3fffffff; 5038c2ecf20Sopenharmony_ci val |= (osdac_ch1 << 30); 5048c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH1_BB1, val); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ch1_done = 0; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci } else { 5098c2ecf20Sopenharmony_ci ch1_done = 1; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if ((dc_off_ch2_i1 > OFF_UPPER_LT) || (dc_off_ch2_i1 < OFF_LOWER_LT) || 5138c2ecf20Sopenharmony_ci (dc_off_ch2_i2 > OFF_UPPER_LT) || (dc_off_ch2_i2 < OFF_LOWER_LT) || 5148c2ecf20Sopenharmony_ci (dc_off_ch2_i3 > OFF_UPPER_LT) || (dc_off_ch2_i3 < OFF_LOWER_LT) || 5158c2ecf20Sopenharmony_ci (dc_off_ch2_q1 > OFF_UPPER_LT) || (dc_off_ch2_q1 < OFF_LOWER_LT) || 5168c2ecf20Sopenharmony_ci (dc_off_ch2_q2 > OFF_UPPER_LT) || (dc_off_ch2_q2 < OFF_LOWER_LT) || 5178c2ecf20Sopenharmony_ci (dc_off_ch2_q3 > OFF_UPPER_LT) || (dc_off_ch2_q3 < OFF_LOWER_LT)) { 5188c2ecf20Sopenharmony_ci if (osdac_ch2 == 3) { 5198c2ecf20Sopenharmony_ci ch2_done = 1; 5208c2ecf20Sopenharmony_ci } else { 5218c2ecf20Sopenharmony_ci osdac_ch2++; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci val = REG_READ(ah, AR_PHY_65NM_CH2_BB1) & 0x3fffffff; 5248c2ecf20Sopenharmony_ci val |= (osdac_ch2 << 30); 5258c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_65NM_CH2_BB1, val); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ch2_done = 0; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci } else { 5308c2ecf20Sopenharmony_ci ch2_done = 1; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci } 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, 5358c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_OFFSET_CAL); 5368c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* 5398c2ecf20Sopenharmony_ci * We don't need to check txiqcal_done here since it is always 5408c2ecf20Sopenharmony_ci * set for AR9550. 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, 5438c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci return true; 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci/* 5498c2ecf20Sopenharmony_ci * solve 4x4 linear equation used in loopback iq cal. 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_cistatic bool ar9003_hw_solve_iq_cal(struct ath_hw *ah, 5528c2ecf20Sopenharmony_ci s32 sin_2phi_1, 5538c2ecf20Sopenharmony_ci s32 cos_2phi_1, 5548c2ecf20Sopenharmony_ci s32 sin_2phi_2, 5558c2ecf20Sopenharmony_ci s32 cos_2phi_2, 5568c2ecf20Sopenharmony_ci s32 mag_a0_d0, 5578c2ecf20Sopenharmony_ci s32 phs_a0_d0, 5588c2ecf20Sopenharmony_ci s32 mag_a1_d0, 5598c2ecf20Sopenharmony_ci s32 phs_a1_d0, 5608c2ecf20Sopenharmony_ci s32 solved_eq[]) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci s32 f1 = cos_2phi_1 - cos_2phi_2, 5638c2ecf20Sopenharmony_ci f3 = sin_2phi_1 - sin_2phi_2, 5648c2ecf20Sopenharmony_ci f2; 5658c2ecf20Sopenharmony_ci s32 mag_tx, phs_tx, mag_rx, phs_rx; 5668c2ecf20Sopenharmony_ci const s32 result_shift = 1 << 15; 5678c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci f2 = ((f1 >> 3) * (f1 >> 3) + (f3 >> 3) * (f3 >> 3)) >> 9; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (!f2) { 5728c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Divide by 0\n"); 5738c2ecf20Sopenharmony_ci return false; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* mag mismatch, tx */ 5778c2ecf20Sopenharmony_ci mag_tx = f1 * (mag_a0_d0 - mag_a1_d0) + f3 * (phs_a0_d0 - phs_a1_d0); 5788c2ecf20Sopenharmony_ci /* phs mismatch, tx */ 5798c2ecf20Sopenharmony_ci phs_tx = f3 * (-mag_a0_d0 + mag_a1_d0) + f1 * (phs_a0_d0 - phs_a1_d0); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci mag_tx = (mag_tx / f2); 5828c2ecf20Sopenharmony_ci phs_tx = (phs_tx / f2); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* mag mismatch, rx */ 5858c2ecf20Sopenharmony_ci mag_rx = mag_a0_d0 - (cos_2phi_1 * mag_tx + sin_2phi_1 * phs_tx) / 5868c2ecf20Sopenharmony_ci result_shift; 5878c2ecf20Sopenharmony_ci /* phs mismatch, rx */ 5888c2ecf20Sopenharmony_ci phs_rx = phs_a0_d0 + (sin_2phi_1 * mag_tx - cos_2phi_1 * phs_tx) / 5898c2ecf20Sopenharmony_ci result_shift; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci solved_eq[0] = mag_tx; 5928c2ecf20Sopenharmony_ci solved_eq[1] = phs_tx; 5938c2ecf20Sopenharmony_ci solved_eq[2] = mag_rx; 5948c2ecf20Sopenharmony_ci solved_eq[3] = phs_rx; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return true; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic s32 ar9003_hw_find_mag_approx(struct ath_hw *ah, s32 in_re, s32 in_im) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci s32 abs_i = abs(in_re), 6028c2ecf20Sopenharmony_ci abs_q = abs(in_im), 6038c2ecf20Sopenharmony_ci max_abs, min_abs; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci if (abs_i > abs_q) { 6068c2ecf20Sopenharmony_ci max_abs = abs_i; 6078c2ecf20Sopenharmony_ci min_abs = abs_q; 6088c2ecf20Sopenharmony_ci } else { 6098c2ecf20Sopenharmony_ci max_abs = abs_q; 6108c2ecf20Sopenharmony_ci min_abs = abs_i; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return max_abs - (max_abs / 32) + (min_abs / 8) + (min_abs / 4); 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci#define DELPT 32 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cistatic bool ar9003_hw_calc_iq_corr(struct ath_hw *ah, 6198c2ecf20Sopenharmony_ci s32 chain_idx, 6208c2ecf20Sopenharmony_ci const s32 iq_res[], 6218c2ecf20Sopenharmony_ci s32 iqc_coeff[]) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci s32 i2_m_q2_a0_d0, i2_p_q2_a0_d0, iq_corr_a0_d0, 6248c2ecf20Sopenharmony_ci i2_m_q2_a0_d1, i2_p_q2_a0_d1, iq_corr_a0_d1, 6258c2ecf20Sopenharmony_ci i2_m_q2_a1_d0, i2_p_q2_a1_d0, iq_corr_a1_d0, 6268c2ecf20Sopenharmony_ci i2_m_q2_a1_d1, i2_p_q2_a1_d1, iq_corr_a1_d1; 6278c2ecf20Sopenharmony_ci s32 mag_a0_d0, mag_a1_d0, mag_a0_d1, mag_a1_d1, 6288c2ecf20Sopenharmony_ci phs_a0_d0, phs_a1_d0, phs_a0_d1, phs_a1_d1, 6298c2ecf20Sopenharmony_ci sin_2phi_1, cos_2phi_1, 6308c2ecf20Sopenharmony_ci sin_2phi_2, cos_2phi_2; 6318c2ecf20Sopenharmony_ci s32 mag_tx, phs_tx, mag_rx, phs_rx; 6328c2ecf20Sopenharmony_ci s32 solved_eq[4], mag_corr_tx, phs_corr_tx, mag_corr_rx, phs_corr_rx, 6338c2ecf20Sopenharmony_ci q_q_coff, q_i_coff; 6348c2ecf20Sopenharmony_ci const s32 res_scale = 1 << 15; 6358c2ecf20Sopenharmony_ci const s32 delpt_shift = 1 << 8; 6368c2ecf20Sopenharmony_ci s32 mag1, mag2; 6378c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci i2_m_q2_a0_d0 = iq_res[0] & 0xfff; 6408c2ecf20Sopenharmony_ci i2_p_q2_a0_d0 = (iq_res[0] >> 12) & 0xfff; 6418c2ecf20Sopenharmony_ci iq_corr_a0_d0 = ((iq_res[0] >> 24) & 0xff) + ((iq_res[1] & 0xf) << 8); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (i2_m_q2_a0_d0 > 0x800) 6448c2ecf20Sopenharmony_ci i2_m_q2_a0_d0 = -((0xfff - i2_m_q2_a0_d0) + 1); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci if (i2_p_q2_a0_d0 > 0x800) 6478c2ecf20Sopenharmony_ci i2_p_q2_a0_d0 = -((0xfff - i2_p_q2_a0_d0) + 1); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (iq_corr_a0_d0 > 0x800) 6508c2ecf20Sopenharmony_ci iq_corr_a0_d0 = -((0xfff - iq_corr_a0_d0) + 1); 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci i2_m_q2_a0_d1 = (iq_res[1] >> 4) & 0xfff; 6538c2ecf20Sopenharmony_ci i2_p_q2_a0_d1 = (iq_res[2] & 0xfff); 6548c2ecf20Sopenharmony_ci iq_corr_a0_d1 = (iq_res[2] >> 12) & 0xfff; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (i2_m_q2_a0_d1 > 0x800) 6578c2ecf20Sopenharmony_ci i2_m_q2_a0_d1 = -((0xfff - i2_m_q2_a0_d1) + 1); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (iq_corr_a0_d1 > 0x800) 6608c2ecf20Sopenharmony_ci iq_corr_a0_d1 = -((0xfff - iq_corr_a0_d1) + 1); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci i2_m_q2_a1_d0 = ((iq_res[2] >> 24) & 0xff) + ((iq_res[3] & 0xf) << 8); 6638c2ecf20Sopenharmony_ci i2_p_q2_a1_d0 = (iq_res[3] >> 4) & 0xfff; 6648c2ecf20Sopenharmony_ci iq_corr_a1_d0 = iq_res[4] & 0xfff; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci if (i2_m_q2_a1_d0 > 0x800) 6678c2ecf20Sopenharmony_ci i2_m_q2_a1_d0 = -((0xfff - i2_m_q2_a1_d0) + 1); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci if (i2_p_q2_a1_d0 > 0x800) 6708c2ecf20Sopenharmony_ci i2_p_q2_a1_d0 = -((0xfff - i2_p_q2_a1_d0) + 1); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (iq_corr_a1_d0 > 0x800) 6738c2ecf20Sopenharmony_ci iq_corr_a1_d0 = -((0xfff - iq_corr_a1_d0) + 1); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci i2_m_q2_a1_d1 = (iq_res[4] >> 12) & 0xfff; 6768c2ecf20Sopenharmony_ci i2_p_q2_a1_d1 = ((iq_res[4] >> 24) & 0xff) + ((iq_res[5] & 0xf) << 8); 6778c2ecf20Sopenharmony_ci iq_corr_a1_d1 = (iq_res[5] >> 4) & 0xfff; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (i2_m_q2_a1_d1 > 0x800) 6808c2ecf20Sopenharmony_ci i2_m_q2_a1_d1 = -((0xfff - i2_m_q2_a1_d1) + 1); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if (i2_p_q2_a1_d1 > 0x800) 6838c2ecf20Sopenharmony_ci i2_p_q2_a1_d1 = -((0xfff - i2_p_q2_a1_d1) + 1); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (iq_corr_a1_d1 > 0x800) 6868c2ecf20Sopenharmony_ci iq_corr_a1_d1 = -((0xfff - iq_corr_a1_d1) + 1); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if ((i2_p_q2_a0_d0 == 0) || (i2_p_q2_a0_d1 == 0) || 6898c2ecf20Sopenharmony_ci (i2_p_q2_a1_d0 == 0) || (i2_p_q2_a1_d1 == 0)) { 6908c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 6918c2ecf20Sopenharmony_ci "Divide by 0:\n" 6928c2ecf20Sopenharmony_ci "a0_d0=%d\n" 6938c2ecf20Sopenharmony_ci "a0_d1=%d\n" 6948c2ecf20Sopenharmony_ci "a2_d0=%d\n" 6958c2ecf20Sopenharmony_ci "a1_d1=%d\n", 6968c2ecf20Sopenharmony_ci i2_p_q2_a0_d0, i2_p_q2_a0_d1, 6978c2ecf20Sopenharmony_ci i2_p_q2_a1_d0, i2_p_q2_a1_d1); 6988c2ecf20Sopenharmony_ci return false; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if ((i2_p_q2_a0_d0 < 1024) || (i2_p_q2_a0_d0 > 2047) || 7028c2ecf20Sopenharmony_ci (i2_p_q2_a1_d0 < 0) || (i2_p_q2_a1_d1 < 0) || 7038c2ecf20Sopenharmony_ci (i2_p_q2_a0_d0 <= i2_m_q2_a0_d0) || 7048c2ecf20Sopenharmony_ci (i2_p_q2_a0_d0 <= iq_corr_a0_d0) || 7058c2ecf20Sopenharmony_ci (i2_p_q2_a0_d1 <= i2_m_q2_a0_d1) || 7068c2ecf20Sopenharmony_ci (i2_p_q2_a0_d1 <= iq_corr_a0_d1) || 7078c2ecf20Sopenharmony_ci (i2_p_q2_a1_d0 <= i2_m_q2_a1_d0) || 7088c2ecf20Sopenharmony_ci (i2_p_q2_a1_d0 <= iq_corr_a1_d0) || 7098c2ecf20Sopenharmony_ci (i2_p_q2_a1_d1 <= i2_m_q2_a1_d1) || 7108c2ecf20Sopenharmony_ci (i2_p_q2_a1_d1 <= iq_corr_a1_d1)) { 7118c2ecf20Sopenharmony_ci return false; 7128c2ecf20Sopenharmony_ci } 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci mag_a0_d0 = (i2_m_q2_a0_d0 * res_scale) / i2_p_q2_a0_d0; 7158c2ecf20Sopenharmony_ci phs_a0_d0 = (iq_corr_a0_d0 * res_scale) / i2_p_q2_a0_d0; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci mag_a0_d1 = (i2_m_q2_a0_d1 * res_scale) / i2_p_q2_a0_d1; 7188c2ecf20Sopenharmony_ci phs_a0_d1 = (iq_corr_a0_d1 * res_scale) / i2_p_q2_a0_d1; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci mag_a1_d0 = (i2_m_q2_a1_d0 * res_scale) / i2_p_q2_a1_d0; 7218c2ecf20Sopenharmony_ci phs_a1_d0 = (iq_corr_a1_d0 * res_scale) / i2_p_q2_a1_d0; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci mag_a1_d1 = (i2_m_q2_a1_d1 * res_scale) / i2_p_q2_a1_d1; 7248c2ecf20Sopenharmony_ci phs_a1_d1 = (iq_corr_a1_d1 * res_scale) / i2_p_q2_a1_d1; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* w/o analog phase shift */ 7278c2ecf20Sopenharmony_ci sin_2phi_1 = (((mag_a0_d0 - mag_a0_d1) * delpt_shift) / DELPT); 7288c2ecf20Sopenharmony_ci /* w/o analog phase shift */ 7298c2ecf20Sopenharmony_ci cos_2phi_1 = (((phs_a0_d1 - phs_a0_d0) * delpt_shift) / DELPT); 7308c2ecf20Sopenharmony_ci /* w/ analog phase shift */ 7318c2ecf20Sopenharmony_ci sin_2phi_2 = (((mag_a1_d0 - mag_a1_d1) * delpt_shift) / DELPT); 7328c2ecf20Sopenharmony_ci /* w/ analog phase shift */ 7338c2ecf20Sopenharmony_ci cos_2phi_2 = (((phs_a1_d1 - phs_a1_d0) * delpt_shift) / DELPT); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* 7368c2ecf20Sopenharmony_ci * force sin^2 + cos^2 = 1; 7378c2ecf20Sopenharmony_ci * find magnitude by approximation 7388c2ecf20Sopenharmony_ci */ 7398c2ecf20Sopenharmony_ci mag1 = ar9003_hw_find_mag_approx(ah, cos_2phi_1, sin_2phi_1); 7408c2ecf20Sopenharmony_ci mag2 = ar9003_hw_find_mag_approx(ah, cos_2phi_2, sin_2phi_2); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci if ((mag1 == 0) || (mag2 == 0)) { 7438c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Divide by 0: mag1=%d, mag2=%d\n", 7448c2ecf20Sopenharmony_ci mag1, mag2); 7458c2ecf20Sopenharmony_ci return false; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci /* normalization sin and cos by mag */ 7498c2ecf20Sopenharmony_ci sin_2phi_1 = (sin_2phi_1 * res_scale / mag1); 7508c2ecf20Sopenharmony_ci cos_2phi_1 = (cos_2phi_1 * res_scale / mag1); 7518c2ecf20Sopenharmony_ci sin_2phi_2 = (sin_2phi_2 * res_scale / mag2); 7528c2ecf20Sopenharmony_ci cos_2phi_2 = (cos_2phi_2 * res_scale / mag2); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* calculate IQ mismatch */ 7558c2ecf20Sopenharmony_ci if (!ar9003_hw_solve_iq_cal(ah, 7568c2ecf20Sopenharmony_ci sin_2phi_1, cos_2phi_1, 7578c2ecf20Sopenharmony_ci sin_2phi_2, cos_2phi_2, 7588c2ecf20Sopenharmony_ci mag_a0_d0, phs_a0_d0, 7598c2ecf20Sopenharmony_ci mag_a1_d0, 7608c2ecf20Sopenharmony_ci phs_a1_d0, solved_eq)) { 7618c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 7628c2ecf20Sopenharmony_ci "Call to ar9003_hw_solve_iq_cal() failed\n"); 7638c2ecf20Sopenharmony_ci return false; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci mag_tx = solved_eq[0]; 7678c2ecf20Sopenharmony_ci phs_tx = solved_eq[1]; 7688c2ecf20Sopenharmony_ci mag_rx = solved_eq[2]; 7698c2ecf20Sopenharmony_ci phs_rx = solved_eq[3]; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 7728c2ecf20Sopenharmony_ci "chain %d: mag mismatch=%d phase mismatch=%d\n", 7738c2ecf20Sopenharmony_ci chain_idx, mag_tx/res_scale, phs_tx/res_scale); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (res_scale == mag_tx) { 7768c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 7778c2ecf20Sopenharmony_ci "Divide by 0: mag_tx=%d, res_scale=%d\n", 7788c2ecf20Sopenharmony_ci mag_tx, res_scale); 7798c2ecf20Sopenharmony_ci return false; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci /* calculate and quantize Tx IQ correction factor */ 7838c2ecf20Sopenharmony_ci mag_corr_tx = (mag_tx * res_scale) / (res_scale - mag_tx); 7848c2ecf20Sopenharmony_ci phs_corr_tx = -phs_tx; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci q_q_coff = (mag_corr_tx * 128 / res_scale); 7878c2ecf20Sopenharmony_ci q_i_coff = (phs_corr_tx * 256 / res_scale); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "tx chain %d: mag corr=%d phase corr=%d\n", 7908c2ecf20Sopenharmony_ci chain_idx, q_q_coff, q_i_coff); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci if (q_i_coff < -63) 7938c2ecf20Sopenharmony_ci q_i_coff = -63; 7948c2ecf20Sopenharmony_ci if (q_i_coff > 63) 7958c2ecf20Sopenharmony_ci q_i_coff = 63; 7968c2ecf20Sopenharmony_ci if (q_q_coff < -63) 7978c2ecf20Sopenharmony_ci q_q_coff = -63; 7988c2ecf20Sopenharmony_ci if (q_q_coff > 63) 7998c2ecf20Sopenharmony_ci q_q_coff = 63; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci iqc_coeff[0] = (q_q_coff * 128) + (0x7f & q_i_coff); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "tx chain %d: iq corr coeff=%x\n", 8048c2ecf20Sopenharmony_ci chain_idx, iqc_coeff[0]); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (-mag_rx == res_scale) { 8078c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 8088c2ecf20Sopenharmony_ci "Divide by 0: mag_rx=%d, res_scale=%d\n", 8098c2ecf20Sopenharmony_ci mag_rx, res_scale); 8108c2ecf20Sopenharmony_ci return false; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* calculate and quantize Rx IQ correction factors */ 8148c2ecf20Sopenharmony_ci mag_corr_rx = (-mag_rx * res_scale) / (res_scale + mag_rx); 8158c2ecf20Sopenharmony_ci phs_corr_rx = -phs_rx; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci q_q_coff = (mag_corr_rx * 128 / res_scale); 8188c2ecf20Sopenharmony_ci q_i_coff = (phs_corr_rx * 256 / res_scale); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "rx chain %d: mag corr=%d phase corr=%d\n", 8218c2ecf20Sopenharmony_ci chain_idx, q_q_coff, q_i_coff); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (q_i_coff < -63) 8248c2ecf20Sopenharmony_ci q_i_coff = -63; 8258c2ecf20Sopenharmony_ci if (q_i_coff > 63) 8268c2ecf20Sopenharmony_ci q_i_coff = 63; 8278c2ecf20Sopenharmony_ci if (q_q_coff < -63) 8288c2ecf20Sopenharmony_ci q_q_coff = -63; 8298c2ecf20Sopenharmony_ci if (q_q_coff > 63) 8308c2ecf20Sopenharmony_ci q_q_coff = 63; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci iqc_coeff[1] = (q_q_coff * 128) + (0x7f & q_i_coff); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "rx chain %d: iq corr coeff=%x\n", 8358c2ecf20Sopenharmony_ci chain_idx, iqc_coeff[1]); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return true; 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic void ar9003_hw_detect_outlier(int mp_coeff[][MAXIQCAL], 8418c2ecf20Sopenharmony_ci int nmeasurement, 8428c2ecf20Sopenharmony_ci int max_delta) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci int mp_max = -64, max_idx = 0; 8458c2ecf20Sopenharmony_ci int mp_min = 63, min_idx = 0; 8468c2ecf20Sopenharmony_ci int mp_avg = 0, i, outlier_idx = 0, mp_count = 0; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci /* find min/max mismatch across all calibrated gains */ 8498c2ecf20Sopenharmony_ci for (i = 0; i < nmeasurement; i++) { 8508c2ecf20Sopenharmony_ci if (mp_coeff[i][0] > mp_max) { 8518c2ecf20Sopenharmony_ci mp_max = mp_coeff[i][0]; 8528c2ecf20Sopenharmony_ci max_idx = i; 8538c2ecf20Sopenharmony_ci } else if (mp_coeff[i][0] < mp_min) { 8548c2ecf20Sopenharmony_ci mp_min = mp_coeff[i][0]; 8558c2ecf20Sopenharmony_ci min_idx = i; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* find average (exclude max abs value) */ 8608c2ecf20Sopenharmony_ci for (i = 0; i < nmeasurement; i++) { 8618c2ecf20Sopenharmony_ci if ((abs(mp_coeff[i][0]) < abs(mp_max)) || 8628c2ecf20Sopenharmony_ci (abs(mp_coeff[i][0]) < abs(mp_min))) { 8638c2ecf20Sopenharmony_ci mp_avg += mp_coeff[i][0]; 8648c2ecf20Sopenharmony_ci mp_count++; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* 8698c2ecf20Sopenharmony_ci * finding mean magnitude/phase if possible, otherwise 8708c2ecf20Sopenharmony_ci * just use the last value as the mean 8718c2ecf20Sopenharmony_ci */ 8728c2ecf20Sopenharmony_ci if (mp_count) 8738c2ecf20Sopenharmony_ci mp_avg /= mp_count; 8748c2ecf20Sopenharmony_ci else 8758c2ecf20Sopenharmony_ci mp_avg = mp_coeff[nmeasurement - 1][0]; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci /* detect outlier */ 8788c2ecf20Sopenharmony_ci if (abs(mp_max - mp_min) > max_delta) { 8798c2ecf20Sopenharmony_ci if (abs(mp_max - mp_avg) > abs(mp_min - mp_avg)) 8808c2ecf20Sopenharmony_ci outlier_idx = max_idx; 8818c2ecf20Sopenharmony_ci else 8828c2ecf20Sopenharmony_ci outlier_idx = min_idx; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci mp_coeff[outlier_idx][0] = mp_avg; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci} 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic void ar9003_hw_tx_iq_cal_outlier_detection(struct ath_hw *ah, 8898c2ecf20Sopenharmony_ci struct coeff *coeff, 8908c2ecf20Sopenharmony_ci bool is_reusable) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci int i, im, nmeasurement; 8938c2ecf20Sopenharmony_ci int magnitude, phase; 8948c2ecf20Sopenharmony_ci u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS]; 8958c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff)); 8988c2ecf20Sopenharmony_ci for (i = 0; i < MAX_MEASUREMENT / 2; i++) { 8998c2ecf20Sopenharmony_ci tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] = 9008c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_B0(i); 9018c2ecf20Sopenharmony_ci if (!AR_SREV_9485(ah)) { 9028c2ecf20Sopenharmony_ci tx_corr_coeff[i * 2][1] = 9038c2ecf20Sopenharmony_ci tx_corr_coeff[(i * 2) + 1][1] = 9048c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_B1(i); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci tx_corr_coeff[i * 2][2] = 9078c2ecf20Sopenharmony_ci tx_corr_coeff[(i * 2) + 1][2] = 9088c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_B2(i); 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci /* Load the average of 2 passes */ 9138c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 9148c2ecf20Sopenharmony_ci if (!(ah->txchainmask & (1 << i))) 9158c2ecf20Sopenharmony_ci continue; 9168c2ecf20Sopenharmony_ci nmeasurement = REG_READ_FIELD(ah, 9178c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_STATUS_B0, 9188c2ecf20Sopenharmony_ci AR_PHY_CALIBRATED_GAINS_0); 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (nmeasurement > MAX_MEASUREMENT) 9218c2ecf20Sopenharmony_ci nmeasurement = MAX_MEASUREMENT; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci /* 9248c2ecf20Sopenharmony_ci * Skip normal outlier detection for AR9550. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_ci if (!AR_SREV_9550(ah)) { 9278c2ecf20Sopenharmony_ci /* detect outlier only if nmeasurement > 1 */ 9288c2ecf20Sopenharmony_ci if (nmeasurement > 1) { 9298c2ecf20Sopenharmony_ci /* Detect magnitude outlier */ 9308c2ecf20Sopenharmony_ci ar9003_hw_detect_outlier(coeff->mag_coeff[i], 9318c2ecf20Sopenharmony_ci nmeasurement, 9328c2ecf20Sopenharmony_ci MAX_MAG_DELTA); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* Detect phase outlier */ 9358c2ecf20Sopenharmony_ci ar9003_hw_detect_outlier(coeff->phs_coeff[i], 9368c2ecf20Sopenharmony_ci nmeasurement, 9378c2ecf20Sopenharmony_ci MAX_PHS_DELTA); 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci for (im = 0; im < nmeasurement; im++) { 9428c2ecf20Sopenharmony_ci magnitude = coeff->mag_coeff[i][im][0]; 9438c2ecf20Sopenharmony_ci phase = coeff->phs_coeff[i][im][0]; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci coeff->iqc_coeff[0] = 9468c2ecf20Sopenharmony_ci (phase & 0x7f) | ((magnitude & 0x7f) << 7); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if ((im % 2) == 0) 9498c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, tx_corr_coeff[im][i], 9508c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, 9518c2ecf20Sopenharmony_ci coeff->iqc_coeff[0]); 9528c2ecf20Sopenharmony_ci else 9538c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, tx_corr_coeff[im][i], 9548c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, 9558c2ecf20Sopenharmony_ci coeff->iqc_coeff[0]); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (caldata) 9588c2ecf20Sopenharmony_ci caldata->tx_corr_coeff[im][i] = 9598c2ecf20Sopenharmony_ci coeff->iqc_coeff[0]; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci if (caldata) 9628c2ecf20Sopenharmony_ci caldata->num_measures[i] = nmeasurement; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, 9668c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); 9678c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, 9688c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci if (caldata) { 9718c2ecf20Sopenharmony_ci if (is_reusable) 9728c2ecf20Sopenharmony_ci set_bit(TXIQCAL_DONE, &caldata->cal_flags); 9738c2ecf20Sopenharmony_ci else 9748c2ecf20Sopenharmony_ci clear_bit(TXIQCAL_DONE, &caldata->cal_flags); 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return; 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic bool ar9003_hw_tx_iq_cal_run(struct ath_hw *ah) 9818c2ecf20Sopenharmony_ci{ 9828c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 9838c2ecf20Sopenharmony_ci u8 tx_gain_forced; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci tx_gain_forced = REG_READ_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 9868c2ecf20Sopenharmony_ci AR_PHY_TXGAIN_FORCE); 9878c2ecf20Sopenharmony_ci if (tx_gain_forced) 9888c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_TX_FORCED_GAIN, 9898c2ecf20Sopenharmony_ci AR_PHY_TXGAIN_FORCE, 0); 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_START, 9928c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_START_DO_CAL, 1); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (!ath9k_hw_wait(ah, AR_PHY_TX_IQCAL_START, 9958c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_START_DO_CAL, 0, 9968c2ecf20Sopenharmony_ci AH_WAIT_TIMEOUT)) { 9978c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Tx IQ Cal is not completed\n"); 9988c2ecf20Sopenharmony_ci return false; 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci return true; 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void __ar955x_tx_iq_cal_sort(struct ath_hw *ah, 10048c2ecf20Sopenharmony_ci struct coeff *coeff, 10058c2ecf20Sopenharmony_ci int i, int nmeasurement) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 10088c2ecf20Sopenharmony_ci int im, ix, iy, temp; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci for (im = 0; im < nmeasurement; im++) { 10118c2ecf20Sopenharmony_ci for (ix = 0; ix < MAXIQCAL - 1; ix++) { 10128c2ecf20Sopenharmony_ci for (iy = ix + 1; iy <= MAXIQCAL - 1; iy++) { 10138c2ecf20Sopenharmony_ci if (coeff->mag_coeff[i][im][iy] < 10148c2ecf20Sopenharmony_ci coeff->mag_coeff[i][im][ix]) { 10158c2ecf20Sopenharmony_ci temp = coeff->mag_coeff[i][im][ix]; 10168c2ecf20Sopenharmony_ci coeff->mag_coeff[i][im][ix] = 10178c2ecf20Sopenharmony_ci coeff->mag_coeff[i][im][iy]; 10188c2ecf20Sopenharmony_ci coeff->mag_coeff[i][im][iy] = temp; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci if (coeff->phs_coeff[i][im][iy] < 10218c2ecf20Sopenharmony_ci coeff->phs_coeff[i][im][ix]) { 10228c2ecf20Sopenharmony_ci temp = coeff->phs_coeff[i][im][ix]; 10238c2ecf20Sopenharmony_ci coeff->phs_coeff[i][im][ix] = 10248c2ecf20Sopenharmony_ci coeff->phs_coeff[i][im][iy]; 10258c2ecf20Sopenharmony_ci coeff->phs_coeff[i][im][iy] = temp; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci } 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci coeff->mag_coeff[i][im][0] = coeff->mag_coeff[i][im][MAXIQCAL / 2]; 10308c2ecf20Sopenharmony_ci coeff->phs_coeff[i][im][0] = coeff->phs_coeff[i][im][MAXIQCAL / 2]; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 10338c2ecf20Sopenharmony_ci "IQCAL: Median [ch%d][gain%d]: mag = %d phase = %d\n", 10348c2ecf20Sopenharmony_ci i, im, 10358c2ecf20Sopenharmony_ci coeff->mag_coeff[i][im][0], 10368c2ecf20Sopenharmony_ci coeff->phs_coeff[i][im][0]); 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic bool ar955x_tx_iq_cal_median(struct ath_hw *ah, 10418c2ecf20Sopenharmony_ci struct coeff *coeff, 10428c2ecf20Sopenharmony_ci int iqcal_idx, 10438c2ecf20Sopenharmony_ci int nmeasurement) 10448c2ecf20Sopenharmony_ci{ 10458c2ecf20Sopenharmony_ci int i; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if ((iqcal_idx + 1) != MAXIQCAL) 10488c2ecf20Sopenharmony_ci return false; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 10518c2ecf20Sopenharmony_ci __ar955x_tx_iq_cal_sort(ah, coeff, i, nmeasurement); 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci return true; 10558c2ecf20Sopenharmony_ci} 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_cistatic void ar9003_hw_tx_iq_cal_post_proc(struct ath_hw *ah, 10588c2ecf20Sopenharmony_ci int iqcal_idx, 10598c2ecf20Sopenharmony_ci bool is_reusable) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 10628c2ecf20Sopenharmony_ci const u32 txiqcal_status[AR9300_MAX_CHAINS] = { 10638c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_STATUS_B0, 10648c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_STATUS_B1, 10658c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_STATUS_B2, 10668c2ecf20Sopenharmony_ci }; 10678c2ecf20Sopenharmony_ci const u_int32_t chan_info_tab[] = { 10688c2ecf20Sopenharmony_ci AR_PHY_CHAN_INFO_TAB_0, 10698c2ecf20Sopenharmony_ci AR_PHY_CHAN_INFO_TAB_1, 10708c2ecf20Sopenharmony_ci AR_PHY_CHAN_INFO_TAB_2, 10718c2ecf20Sopenharmony_ci }; 10728c2ecf20Sopenharmony_ci static struct coeff coeff; 10738c2ecf20Sopenharmony_ci s32 iq_res[6]; 10748c2ecf20Sopenharmony_ci int i, im, j; 10758c2ecf20Sopenharmony_ci int nmeasurement = 0; 10768c2ecf20Sopenharmony_ci bool outlier_detect = true; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 10798c2ecf20Sopenharmony_ci if (!(ah->txchainmask & (1 << i))) 10808c2ecf20Sopenharmony_ci continue; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci nmeasurement = REG_READ_FIELD(ah, 10838c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_STATUS_B0, 10848c2ecf20Sopenharmony_ci AR_PHY_CALIBRATED_GAINS_0); 10858c2ecf20Sopenharmony_ci if (nmeasurement > MAX_MEASUREMENT) 10868c2ecf20Sopenharmony_ci nmeasurement = MAX_MEASUREMENT; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci for (im = 0; im < nmeasurement; im++) { 10898c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 10908c2ecf20Sopenharmony_ci "Doing Tx IQ Cal for chain %d\n", i); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (REG_READ(ah, txiqcal_status[i]) & 10938c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_STATUS_FAILED) { 10948c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 10958c2ecf20Sopenharmony_ci "Tx IQ Cal failed for chain %d\n", i); 10968c2ecf20Sopenharmony_ci goto tx_iqcal_fail; 10978c2ecf20Sopenharmony_ci } 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci for (j = 0; j < 3; j++) { 11008c2ecf20Sopenharmony_ci u32 idx = 2 * j, offset = 4 * (3 * im + j); 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, 11038c2ecf20Sopenharmony_ci AR_PHY_CHAN_INFO_MEMORY, 11048c2ecf20Sopenharmony_ci AR_PHY_CHAN_INFO_TAB_S2_READ, 11058c2ecf20Sopenharmony_ci 0); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* 32 bits */ 11088c2ecf20Sopenharmony_ci iq_res[idx] = REG_READ(ah, 11098c2ecf20Sopenharmony_ci chan_info_tab[i] + 11108c2ecf20Sopenharmony_ci offset); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, 11138c2ecf20Sopenharmony_ci AR_PHY_CHAN_INFO_MEMORY, 11148c2ecf20Sopenharmony_ci AR_PHY_CHAN_INFO_TAB_S2_READ, 11158c2ecf20Sopenharmony_ci 1); 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* 16 bits */ 11188c2ecf20Sopenharmony_ci iq_res[idx + 1] = 0xffff & REG_READ(ah, 11198c2ecf20Sopenharmony_ci chan_info_tab[i] + offset); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 11228c2ecf20Sopenharmony_ci "IQ_RES[%d]=0x%x IQ_RES[%d]=0x%x\n", 11238c2ecf20Sopenharmony_ci idx, iq_res[idx], idx + 1, 11248c2ecf20Sopenharmony_ci iq_res[idx + 1]); 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (!ar9003_hw_calc_iq_corr(ah, i, iq_res, 11288c2ecf20Sopenharmony_ci coeff.iqc_coeff)) { 11298c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 11308c2ecf20Sopenharmony_ci "Failed in calculation of IQ correction\n"); 11318c2ecf20Sopenharmony_ci goto tx_iqcal_fail; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci coeff.phs_coeff[i][im][iqcal_idx] = 11358c2ecf20Sopenharmony_ci coeff.iqc_coeff[0] & 0x7f; 11368c2ecf20Sopenharmony_ci coeff.mag_coeff[i][im][iqcal_idx] = 11378c2ecf20Sopenharmony_ci (coeff.iqc_coeff[0] >> 7) & 0x7f; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (coeff.mag_coeff[i][im][iqcal_idx] > 63) 11408c2ecf20Sopenharmony_ci coeff.mag_coeff[i][im][iqcal_idx] -= 128; 11418c2ecf20Sopenharmony_ci if (coeff.phs_coeff[i][im][iqcal_idx] > 63) 11428c2ecf20Sopenharmony_ci coeff.phs_coeff[i][im][iqcal_idx] -= 128; 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci if (AR_SREV_9550(ah)) 11478c2ecf20Sopenharmony_ci outlier_detect = ar955x_tx_iq_cal_median(ah, &coeff, 11488c2ecf20Sopenharmony_ci iqcal_idx, nmeasurement); 11498c2ecf20Sopenharmony_ci if (outlier_detect) 11508c2ecf20Sopenharmony_ci ar9003_hw_tx_iq_cal_outlier_detection(ah, &coeff, is_reusable); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_citx_iqcal_fail: 11558c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "Tx IQ Cal failed\n"); 11568c2ecf20Sopenharmony_ci return; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 11628c2ecf20Sopenharmony_ci u32 tx_corr_coeff[MAX_MEASUREMENT][AR9300_MAX_CHAINS]; 11638c2ecf20Sopenharmony_ci int i, im; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci memset(tx_corr_coeff, 0, sizeof(tx_corr_coeff)); 11668c2ecf20Sopenharmony_ci for (i = 0; i < MAX_MEASUREMENT / 2; i++) { 11678c2ecf20Sopenharmony_ci tx_corr_coeff[i * 2][0] = tx_corr_coeff[(i * 2) + 1][0] = 11688c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_B0(i); 11698c2ecf20Sopenharmony_ci if (!AR_SREV_9485(ah)) { 11708c2ecf20Sopenharmony_ci tx_corr_coeff[i * 2][1] = 11718c2ecf20Sopenharmony_ci tx_corr_coeff[(i * 2) + 1][1] = 11728c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_B1(i); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci tx_corr_coeff[i * 2][2] = 11758c2ecf20Sopenharmony_ci tx_corr_coeff[(i * 2) + 1][2] = 11768c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_B2(i); 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 11818c2ecf20Sopenharmony_ci if (!(ah->txchainmask & (1 << i))) 11828c2ecf20Sopenharmony_ci continue; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci for (im = 0; im < caldata->num_measures[i]; im++) { 11858c2ecf20Sopenharmony_ci if ((im % 2) == 0) 11868c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, tx_corr_coeff[im][i], 11878c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_00_COEFF_TABLE, 11888c2ecf20Sopenharmony_ci caldata->tx_corr_coeff[im][i]); 11898c2ecf20Sopenharmony_ci else 11908c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, tx_corr_coeff[im][i], 11918c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CORR_COEFF_01_COEFF_TABLE, 11928c2ecf20Sopenharmony_ci caldata->tx_corr_coeff[im][i]); 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_3, 11978c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_3_IQCORR_EN, 0x1); 11988c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, 11998c2ecf20Sopenharmony_ci AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); 12008c2ecf20Sopenharmony_ci} 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci int offset[8] = {0}, total = 0, test; 12058c2ecf20Sopenharmony_ci int agc_out, i, peak_detect_threshold = 0; 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) 12088c2ecf20Sopenharmony_ci peak_detect_threshold = 8; 12098c2ecf20Sopenharmony_ci else if (AR_SREV_9561(ah)) 12108c2ecf20Sopenharmony_ci peak_detect_threshold = 11; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* 12138c2ecf20Sopenharmony_ci * Turn off LNA/SW. 12148c2ecf20Sopenharmony_ci */ 12158c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), 12168c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0x1); 12178c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), 12188c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC, 0x0); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9330_11(ah)) { 12218c2ecf20Sopenharmony_ci if (is_2g) 12228c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), 12238c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0); 12248c2ecf20Sopenharmony_ci else 12258c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), 12268c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0); 12278c2ecf20Sopenharmony_ci } 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* 12308c2ecf20Sopenharmony_ci * Turn off RXON. 12318c2ecf20Sopenharmony_ci */ 12328c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), 12338c2ecf20Sopenharmony_ci AR_PHY_65NM_RXTX2_RXON_OVR, 0x1); 12348c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), 12358c2ecf20Sopenharmony_ci AR_PHY_65NM_RXTX2_RXON, 0x0); 12368c2ecf20Sopenharmony_ci 12378c2ecf20Sopenharmony_ci /* 12388c2ecf20Sopenharmony_ci * Turn on AGC for cal. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12418c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); 12428c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12438c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR, 0x1); 12448c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12458c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1); 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (AR_SREV_9330_11(ah)) 12488c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12498c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci if (is_2g) 12528c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12538c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 12548c2ecf20Sopenharmony_ci peak_detect_threshold); 12558c2ecf20Sopenharmony_ci else 12568c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12578c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 12588c2ecf20Sopenharmony_ci peak_detect_threshold); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci for (i = 6; i > 0; i--) { 12618c2ecf20Sopenharmony_ci offset[i] = BIT(i - 1); 12628c2ecf20Sopenharmony_ci test = total + offset[i]; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (is_2g) 12658c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12668c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 12678c2ecf20Sopenharmony_ci test); 12688c2ecf20Sopenharmony_ci else 12698c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12708c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, 12718c2ecf20Sopenharmony_ci test); 12728c2ecf20Sopenharmony_ci udelay(100); 12738c2ecf20Sopenharmony_ci agc_out = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12748c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC_OUT); 12758c2ecf20Sopenharmony_ci offset[i] = (agc_out) ? 0 : 1; 12768c2ecf20Sopenharmony_ci total += (offset[i] << (i - 1)); 12778c2ecf20Sopenharmony_ci } 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci if (is_2g) 12808c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12818c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, total); 12828c2ecf20Sopenharmony_ci else 12838c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 12848c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, total); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci /* 12878c2ecf20Sopenharmony_ci * Turn on LNA. 12888c2ecf20Sopenharmony_ci */ 12898c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), 12908c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0); 12918c2ecf20Sopenharmony_ci /* 12928c2ecf20Sopenharmony_ci * Turn off RXON. 12938c2ecf20Sopenharmony_ci */ 12948c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), 12958c2ecf20Sopenharmony_ci AR_PHY_65NM_RXTX2_RXON_OVR, 0); 12968c2ecf20Sopenharmony_ci /* 12978c2ecf20Sopenharmony_ci * Turn off peak detect calibration. 12988c2ecf20Sopenharmony_ci */ 12998c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), 13008c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0); 13018c2ecf20Sopenharmony_ci} 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_cistatic void ar9003_hw_do_pcoem_manual_peak_cal(struct ath_hw *ah, 13048c2ecf20Sopenharmony_ci struct ath9k_channel *chan, 13058c2ecf20Sopenharmony_ci bool run_rtt_cal) 13068c2ecf20Sopenharmony_ci{ 13078c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 13088c2ecf20Sopenharmony_ci int i; 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && !run_rtt_cal) 13118c2ecf20Sopenharmony_ci return; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 13148c2ecf20Sopenharmony_ci if (!(ah->rxchainmask & (1 << i))) 13158c2ecf20Sopenharmony_ci continue; 13168c2ecf20Sopenharmony_ci ar9003_hw_manual_peak_cal(ah, i, IS_CHAN_2GHZ(chan)); 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci if (caldata) 13208c2ecf20Sopenharmony_ci set_bit(SW_PKDET_DONE, &caldata->cal_flags); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && caldata) { 13238c2ecf20Sopenharmony_ci if (IS_CHAN_2GHZ(chan)){ 13248c2ecf20Sopenharmony_ci caldata->caldac[0] = REG_READ_FIELD(ah, 13258c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC(0), 13268c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR); 13278c2ecf20Sopenharmony_ci caldata->caldac[1] = REG_READ_FIELD(ah, 13288c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC(1), 13298c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR); 13308c2ecf20Sopenharmony_ci } else { 13318c2ecf20Sopenharmony_ci caldata->caldac[0] = REG_READ_FIELD(ah, 13328c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC(0), 13338c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR); 13348c2ecf20Sopenharmony_ci caldata->caldac[1] = REG_READ_FIELD(ah, 13358c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC(1), 13368c2ecf20Sopenharmony_ci AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR); 13378c2ecf20Sopenharmony_ci } 13388c2ecf20Sopenharmony_ci } 13398c2ecf20Sopenharmony_ci} 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cistatic void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) 13428c2ecf20Sopenharmony_ci{ 13438c2ecf20Sopenharmony_ci u32 cl_idx[AR9300_MAX_CHAINS] = { AR_PHY_CL_TAB_0, 13448c2ecf20Sopenharmony_ci AR_PHY_CL_TAB_1, 13458c2ecf20Sopenharmony_ci AR_PHY_CL_TAB_2 }; 13468c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 13478c2ecf20Sopenharmony_ci bool txclcal_done = false; 13488c2ecf20Sopenharmony_ci int i, j; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (!caldata || !(ah->enabled_cals & TX_CL_CAL)) 13518c2ecf20Sopenharmony_ci return; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & 13548c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_CLC_SUCCESS); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if (test_bit(TXCLCAL_DONE, &caldata->cal_flags)) { 13578c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 13588c2ecf20Sopenharmony_ci if (!(ah->txchainmask & (1 << i))) 13598c2ecf20Sopenharmony_ci continue; 13608c2ecf20Sopenharmony_ci for (j = 0; j < MAX_CL_TAB_ENTRY; j++) 13618c2ecf20Sopenharmony_ci REG_WRITE(ah, CL_TAB_ENTRY(cl_idx[i]), 13628c2ecf20Sopenharmony_ci caldata->tx_clcal[i][j]); 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci } else if (is_reusable && txclcal_done) { 13658c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 13668c2ecf20Sopenharmony_ci if (!(ah->txchainmask & (1 << i))) 13678c2ecf20Sopenharmony_ci continue; 13688c2ecf20Sopenharmony_ci for (j = 0; j < MAX_CL_TAB_ENTRY; j++) 13698c2ecf20Sopenharmony_ci caldata->tx_clcal[i][j] = 13708c2ecf20Sopenharmony_ci REG_READ(ah, CL_TAB_ENTRY(cl_idx[i])); 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci set_bit(TXCLCAL_DONE, &caldata->cal_flags); 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic void ar9003_hw_init_cal_common(struct ath_hw *ah) 13778c2ecf20Sopenharmony_ci{ 13788c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* Initialize list pointers */ 13818c2ecf20Sopenharmony_ci ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci INIT_CAL(&ah->iq_caldata); 13848c2ecf20Sopenharmony_ci INSERT_CAL(ah, &ah->iq_caldata); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci /* Initialize current pointer to first element in list */ 13878c2ecf20Sopenharmony_ci ah->cal_list_curr = ah->cal_list; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (ah->cal_list_curr) 13908c2ecf20Sopenharmony_ci ath9k_hw_reset_calibration(ah, ah->cal_list_curr); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci if (caldata) 13938c2ecf20Sopenharmony_ci caldata->CalValid = 0; 13948c2ecf20Sopenharmony_ci} 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_cistatic bool ar9003_hw_init_cal_pcoem(struct ath_hw *ah, 13978c2ecf20Sopenharmony_ci struct ath9k_channel *chan) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 14008c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 14018c2ecf20Sopenharmony_ci bool txiqcal_done = false; 14028c2ecf20Sopenharmony_ci bool is_reusable = true, status = true; 14038c2ecf20Sopenharmony_ci bool run_rtt_cal = false, run_agc_cal; 14048c2ecf20Sopenharmony_ci bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); 14058c2ecf20Sopenharmony_ci u32 rx_delay = 0; 14068c2ecf20Sopenharmony_ci u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | 14078c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_FLTR_CAL | 14088c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_PKDET_CAL; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* Use chip chainmask only for calibration */ 14118c2ecf20Sopenharmony_ci ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask); 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_ci if (rtt) { 14148c2ecf20Sopenharmony_ci if (!ar9003_hw_rtt_restore(ah, chan)) 14158c2ecf20Sopenharmony_ci run_rtt_cal = true; 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci if (run_rtt_cal) 14188c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, "RTT calibration to be done\n"); 14198c2ecf20Sopenharmony_ci } 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci run_agc_cal = run_rtt_cal; 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci if (run_rtt_cal) { 14248c2ecf20Sopenharmony_ci ar9003_hw_rtt_enable(ah); 14258c2ecf20Sopenharmony_ci ar9003_hw_rtt_set_mask(ah, 0x00); 14268c2ecf20Sopenharmony_ci ar9003_hw_rtt_clear_hist(ah); 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci if (rtt) { 14308c2ecf20Sopenharmony_ci if (!run_rtt_cal) { 14318c2ecf20Sopenharmony_ci agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL); 14328c2ecf20Sopenharmony_ci agc_supp_cals &= agc_ctrl; 14338c2ecf20Sopenharmony_ci agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL | 14348c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_FLTR_CAL | 14358c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_PKDET_CAL); 14368c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); 14378c2ecf20Sopenharmony_ci } else { 14388c2ecf20Sopenharmony_ci if (ah->ah_flags & AH_FASTCC) 14398c2ecf20Sopenharmony_ci run_agc_cal = true; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci } 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (ah->enabled_cals & TX_CL_CAL) { 14448c2ecf20Sopenharmony_ci if (caldata && test_bit(TXCLCAL_DONE, &caldata->cal_flags)) 14458c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, 14468c2ecf20Sopenharmony_ci AR_PHY_CL_CAL_ENABLE); 14478c2ecf20Sopenharmony_ci else { 14488c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, 14498c2ecf20Sopenharmony_ci AR_PHY_CL_CAL_ENABLE); 14508c2ecf20Sopenharmony_ci run_agc_cal = true; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci if ((IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) || 14558c2ecf20Sopenharmony_ci !(ah->enabled_cals & TX_IQ_CAL)) 14568c2ecf20Sopenharmony_ci goto skip_tx_iqcal; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* Do Tx IQ Calibration */ 14598c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1, 14608c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, 14618c2ecf20Sopenharmony_ci DELPT); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* 14648c2ecf20Sopenharmony_ci * For AR9485 or later chips, TxIQ cal runs as part of 14658c2ecf20Sopenharmony_ci * AGC calibration 14668c2ecf20Sopenharmony_ci */ 14678c2ecf20Sopenharmony_ci if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { 14688c2ecf20Sopenharmony_ci if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags)) 14698c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, 14708c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); 14718c2ecf20Sopenharmony_ci else 14728c2ecf20Sopenharmony_ci REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, 14738c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); 14748c2ecf20Sopenharmony_ci txiqcal_done = run_agc_cal = true; 14758c2ecf20Sopenharmony_ci } 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_ciskip_tx_iqcal: 14788c2ecf20Sopenharmony_ci if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) 14798c2ecf20Sopenharmony_ci ar9003_mci_init_cal_req(ah, &is_reusable); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) { 14828c2ecf20Sopenharmony_ci rx_delay = REG_READ(ah, AR_PHY_RX_DELAY); 14838c2ecf20Sopenharmony_ci /* Disable BB_active */ 14848c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); 14858c2ecf20Sopenharmony_ci udelay(5); 14868c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_RX_DELAY, AR_PHY_RX_DELAY_DELAY); 14878c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); 14888c2ecf20Sopenharmony_ci } 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { 14918c2ecf20Sopenharmony_ci /* Calibrate the AGC */ 14928c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_AGC_CONTROL, 14938c2ecf20Sopenharmony_ci REG_READ(ah, AR_PHY_AGC_CONTROL) | 14948c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_CAL); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* Poll for offset calibration complete */ 14978c2ecf20Sopenharmony_ci status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, 14988c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_CAL, 14998c2ecf20Sopenharmony_ci 0, AH_WAIT_TIMEOUT); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci ar9003_hw_do_pcoem_manual_peak_cal(ah, chan, run_rtt_cal); 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) { 15058c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_RX_DELAY, rx_delay); 15068c2ecf20Sopenharmony_ci udelay(5); 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) 15108c2ecf20Sopenharmony_ci ar9003_mci_init_cal_done(ah); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (rtt && !run_rtt_cal) { 15138c2ecf20Sopenharmony_ci agc_ctrl |= agc_supp_cals; 15148c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (!status) { 15188c2ecf20Sopenharmony_ci if (run_rtt_cal) 15198c2ecf20Sopenharmony_ci ar9003_hw_rtt_disable(ah); 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 15228c2ecf20Sopenharmony_ci "offset calibration failed to complete in %d ms; noisy environment?\n", 15238c2ecf20Sopenharmony_ci AH_WAIT_TIMEOUT / 1000); 15248c2ecf20Sopenharmony_ci return false; 15258c2ecf20Sopenharmony_ci } 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci if (txiqcal_done) 15288c2ecf20Sopenharmony_ci ar9003_hw_tx_iq_cal_post_proc(ah, 0, is_reusable); 15298c2ecf20Sopenharmony_ci else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags)) 15308c2ecf20Sopenharmony_ci ar9003_hw_tx_iq_cal_reload(ah); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci ar9003_hw_cl_cal_post_proc(ah, is_reusable); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (run_rtt_cal && caldata) { 15358c2ecf20Sopenharmony_ci if (is_reusable) { 15368c2ecf20Sopenharmony_ci if (!ath9k_hw_rfbus_req(ah)) { 15378c2ecf20Sopenharmony_ci ath_err(ath9k_hw_common(ah), 15388c2ecf20Sopenharmony_ci "Could not stop baseband\n"); 15398c2ecf20Sopenharmony_ci } else { 15408c2ecf20Sopenharmony_ci ar9003_hw_rtt_fill_hist(ah); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci if (test_bit(SW_PKDET_DONE, &caldata->cal_flags)) 15438c2ecf20Sopenharmony_ci ar9003_hw_rtt_load_hist(ah); 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci ath9k_hw_rfbus_done(ah); 15478c2ecf20Sopenharmony_ci } 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci ar9003_hw_rtt_disable(ah); 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci /* Revert chainmask to runtime parameters */ 15538c2ecf20Sopenharmony_ci ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci ar9003_hw_init_cal_common(ah); 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci return true; 15588c2ecf20Sopenharmony_ci} 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_cistatic bool do_ar9003_agc_cal(struct ath_hw *ah) 15618c2ecf20Sopenharmony_ci{ 15628c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(ah); 15638c2ecf20Sopenharmony_ci bool status; 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_AGC_CONTROL, 15668c2ecf20Sopenharmony_ci REG_READ(ah, AR_PHY_AGC_CONTROL) | 15678c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_CAL); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, 15708c2ecf20Sopenharmony_ci AR_PHY_AGC_CONTROL_CAL, 15718c2ecf20Sopenharmony_ci 0, AH_WAIT_TIMEOUT); 15728c2ecf20Sopenharmony_ci if (!status) { 15738c2ecf20Sopenharmony_ci ath_dbg(common, CALIBRATE, 15748c2ecf20Sopenharmony_ci "offset calibration failed to complete in %d ms," 15758c2ecf20Sopenharmony_ci "noisy environment?\n", 15768c2ecf20Sopenharmony_ci AH_WAIT_TIMEOUT / 1000); 15778c2ecf20Sopenharmony_ci return false; 15788c2ecf20Sopenharmony_ci } 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci return true; 15818c2ecf20Sopenharmony_ci} 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_cistatic bool ar9003_hw_init_cal_soc(struct ath_hw *ah, 15848c2ecf20Sopenharmony_ci struct ath9k_channel *chan) 15858c2ecf20Sopenharmony_ci{ 15868c2ecf20Sopenharmony_ci bool txiqcal_done = false; 15878c2ecf20Sopenharmony_ci bool status = true; 15888c2ecf20Sopenharmony_ci bool run_agc_cal = false, sep_iq_cal = false; 15898c2ecf20Sopenharmony_ci int i = 0; 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci /* Use chip chainmask only for calibration */ 15928c2ecf20Sopenharmony_ci ar9003_hw_set_chain_masks(ah, ah->caps.rx_chainmask, ah->caps.tx_chainmask); 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci if (ah->enabled_cals & TX_CL_CAL) { 15958c2ecf20Sopenharmony_ci REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); 15968c2ecf20Sopenharmony_ci run_agc_cal = true; 15978c2ecf20Sopenharmony_ci } 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ci if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) 16008c2ecf20Sopenharmony_ci goto skip_tx_iqcal; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci /* Do Tx IQ Calibration */ 16038c2ecf20Sopenharmony_ci REG_RMW_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_1, 16048c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_1_IQCORR_I_Q_COFF_DELPT, 16058c2ecf20Sopenharmony_ci DELPT); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci /* 16088c2ecf20Sopenharmony_ci * For AR9485 or later chips, TxIQ cal runs as part of 16098c2ecf20Sopenharmony_ci * AGC calibration. Specifically, AR9550 in SoC chips. 16108c2ecf20Sopenharmony_ci */ 16118c2ecf20Sopenharmony_ci if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { 16128c2ecf20Sopenharmony_ci if (REG_READ_FIELD(ah, AR_PHY_TX_IQCAL_CONTROL_0, 16138c2ecf20Sopenharmony_ci AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL)) { 16148c2ecf20Sopenharmony_ci txiqcal_done = true; 16158c2ecf20Sopenharmony_ci } else { 16168c2ecf20Sopenharmony_ci txiqcal_done = false; 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci run_agc_cal = true; 16198c2ecf20Sopenharmony_ci } else { 16208c2ecf20Sopenharmony_ci sep_iq_cal = true; 16218c2ecf20Sopenharmony_ci run_agc_cal = true; 16228c2ecf20Sopenharmony_ci } 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_ci /* 16258c2ecf20Sopenharmony_ci * In the SoC family, this will run for AR9300, AR9331 and AR9340. 16268c2ecf20Sopenharmony_ci */ 16278c2ecf20Sopenharmony_ci if (sep_iq_cal) { 16288c2ecf20Sopenharmony_ci txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); 16298c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); 16308c2ecf20Sopenharmony_ci udelay(5); 16318c2ecf20Sopenharmony_ci REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci if (AR_SREV_9550(ah) && IS_CHAN_2GHZ(chan)) { 16358c2ecf20Sopenharmony_ci if (!ar9003_hw_dynamic_osdac_selection(ah, txiqcal_done)) 16368c2ecf20Sopenharmony_ci return false; 16378c2ecf20Sopenharmony_ci } 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ciskip_tx_iqcal: 16408c2ecf20Sopenharmony_ci if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { 16418c2ecf20Sopenharmony_ci for (i = 0; i < AR9300_MAX_CHAINS; i++) { 16428c2ecf20Sopenharmony_ci if (!(ah->rxchainmask & (1 << i))) 16438c2ecf20Sopenharmony_ci continue; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci ar9003_hw_manual_peak_cal(ah, i, 16468c2ecf20Sopenharmony_ci IS_CHAN_2GHZ(chan)); 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci /* 16508c2ecf20Sopenharmony_ci * For non-AR9550 chips, we just trigger AGC calibration 16518c2ecf20Sopenharmony_ci * in the HW, poll for completion and then process 16528c2ecf20Sopenharmony_ci * the results. 16538c2ecf20Sopenharmony_ci * 16548c2ecf20Sopenharmony_ci * For AR955x, we run it multiple times and use 16558c2ecf20Sopenharmony_ci * median IQ correction. 16568c2ecf20Sopenharmony_ci */ 16578c2ecf20Sopenharmony_ci if (!AR_SREV_9550(ah)) { 16588c2ecf20Sopenharmony_ci status = do_ar9003_agc_cal(ah); 16598c2ecf20Sopenharmony_ci if (!status) 16608c2ecf20Sopenharmony_ci return false; 16618c2ecf20Sopenharmony_ci 16628c2ecf20Sopenharmony_ci if (txiqcal_done) 16638c2ecf20Sopenharmony_ci ar9003_hw_tx_iq_cal_post_proc(ah, 0, false); 16648c2ecf20Sopenharmony_ci } else { 16658c2ecf20Sopenharmony_ci if (!txiqcal_done) { 16668c2ecf20Sopenharmony_ci status = do_ar9003_agc_cal(ah); 16678c2ecf20Sopenharmony_ci if (!status) 16688c2ecf20Sopenharmony_ci return false; 16698c2ecf20Sopenharmony_ci } else { 16708c2ecf20Sopenharmony_ci for (i = 0; i < MAXIQCAL; i++) { 16718c2ecf20Sopenharmony_ci status = do_ar9003_agc_cal(ah); 16728c2ecf20Sopenharmony_ci if (!status) 16738c2ecf20Sopenharmony_ci return false; 16748c2ecf20Sopenharmony_ci ar9003_hw_tx_iq_cal_post_proc(ah, i, false); 16758c2ecf20Sopenharmony_ci } 16768c2ecf20Sopenharmony_ci } 16778c2ecf20Sopenharmony_ci } 16788c2ecf20Sopenharmony_ci } 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci /* Revert chainmask to runtime parameters */ 16818c2ecf20Sopenharmony_ci ar9003_hw_set_chain_masks(ah, ah->rxchainmask, ah->txchainmask); 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci ar9003_hw_init_cal_common(ah); 16848c2ecf20Sopenharmony_ci 16858c2ecf20Sopenharmony_ci return true; 16868c2ecf20Sopenharmony_ci} 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_civoid ar9003_hw_attach_calib_ops(struct ath_hw *ah) 16898c2ecf20Sopenharmony_ci{ 16908c2ecf20Sopenharmony_ci struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); 16918c2ecf20Sopenharmony_ci struct ath_hw_ops *ops = ath9k_hw_ops(ah); 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci if (AR_SREV_9003_PCOEM(ah)) 16948c2ecf20Sopenharmony_ci priv_ops->init_cal = ar9003_hw_init_cal_pcoem; 16958c2ecf20Sopenharmony_ci else 16968c2ecf20Sopenharmony_ci priv_ops->init_cal = ar9003_hw_init_cal_soc; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci priv_ops->init_cal_settings = ar9003_hw_init_cal_settings; 16998c2ecf20Sopenharmony_ci priv_ops->setup_calibration = ar9003_hw_setup_calibration; 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci ops->calibrate = ar9003_hw_calibrate; 17028c2ecf20Sopenharmony_ci} 1703