1 1.1 alc /* 2 1.1 alc * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 3 1.1 alc * Copyright (c) 2002-2008 Atheros Communications, Inc. 4 1.1 alc * 5 1.1 alc * Permission to use, copy, modify, and/or distribute this software for any 6 1.1 alc * purpose with or without fee is hereby granted, provided that the above 7 1.1 alc * copyright notice and this permission notice appear in all copies. 8 1.1 alc * 9 1.1 alc * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 1.1 alc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 1.1 alc * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 1.1 alc * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 1.1 alc * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 1.1 alc * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 1.1 alc * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 1.1 alc * 17 1.2 cegger * $Id: ar5416_cal_iq.c,v 1.2 2011/03/07 11:25:44 cegger Exp $ 18 1.1 alc */ 19 1.1 alc #include "opt_ah.h" 20 1.1 alc 21 1.1 alc #include "ah.h" 22 1.1 alc #include "ah_internal.h" 23 1.1 alc #include "ah_devid.h" 24 1.1 alc 25 1.1 alc #include "ar5416/ar5416.h" 26 1.1 alc #include "ar5416/ar5416reg.h" 27 1.1 alc #include "ar5416/ar5416phy.h" 28 1.1 alc 29 1.1 alc /* IQ Cal aliases */ 30 1.1 alc #define totalPowerMeasI(i) caldata[0][i].u 31 1.1 alc #define totalPowerMeasQ(i) caldata[1][i].u 32 1.1 alc #define totalIqCorrMeas(i) caldata[2][i].s 33 1.1 alc 34 1.1 alc /* 35 1.1 alc * Collect data from HW to later perform IQ Mismatch Calibration 36 1.1 alc */ 37 1.1 alc void 38 1.1 alc ar5416IQCalCollect(struct ath_hal *ah) 39 1.1 alc { 40 1.1 alc struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; 41 1.1 alc int i; 42 1.1 alc 43 1.1 alc /* 44 1.1 alc * Accumulate IQ cal measures for active chains 45 1.1 alc */ 46 1.1 alc for (i = 0; i < AR5416_MAX_CHAINS; i++) { 47 1.1 alc cal->totalPowerMeasI(i) += 48 1.1 alc OS_REG_READ(ah, AR_PHY_CAL_MEAS_0(i)); 49 1.1 alc cal->totalPowerMeasQ(i) += 50 1.1 alc OS_REG_READ(ah, AR_PHY_CAL_MEAS_1(i)); 51 1.1 alc cal->totalIqCorrMeas(i) += (int32_t) 52 1.1 alc OS_REG_READ(ah, AR_PHY_CAL_MEAS_2(i)); 53 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, 54 1.1 alc "%d: Chn %d pmi=0x%08x;pmq=0x%08x;iqcm=0x%08x;\n", 55 1.1 alc cal->calSamples, i, cal->totalPowerMeasI(i), 56 1.1 alc cal->totalPowerMeasQ(i), cal->totalIqCorrMeas(i)); 57 1.1 alc } 58 1.1 alc } 59 1.1 alc 60 1.1 alc /* 61 1.1 alc * Use HW data to do IQ Mismatch Calibration 62 1.1 alc */ 63 1.1 alc void 64 1.1 alc ar5416IQCalibration(struct ath_hal *ah, uint8_t numChains) 65 1.1 alc { 66 1.1 alc struct ar5416PerCal *cal = &AH5416(ah)->ah_cal; 67 1.1 alc int i; 68 1.1 alc 69 1.1 alc for (i = 0; i < numChains; i++) { 70 1.1 alc uint32_t powerMeasI = cal->totalPowerMeasI(i); 71 1.1 alc uint32_t powerMeasQ = cal->totalPowerMeasQ(i); 72 1.1 alc uint32_t iqCorrMeas = cal->totalIqCorrMeas(i); 73 1.1 alc uint32_t qCoffDenom, iCoffDenom; 74 1.1 alc int iqCorrNeg; 75 1.1 alc 76 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, 77 1.1 alc "Start IQ Cal and Correction for Chain %d\n", i); 78 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, 79 1.1 alc "Orignal: iq_corr_meas = 0x%08x\n", iqCorrMeas); 80 1.1 alc 81 1.1 alc iqCorrNeg = 0; 82 1.1 alc /* iqCorrMeas is always negative. */ 83 1.1 alc if (iqCorrMeas > 0x80000000) { 84 1.1 alc iqCorrMeas = (0xffffffff - iqCorrMeas) + 1; 85 1.1 alc iqCorrNeg = 1; 86 1.1 alc } 87 1.1 alc 88 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, " pwr_meas_i = 0x%08x\n", 89 1.1 alc powerMeasI); 90 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, " pwr_meas_q = 0x%08x\n", 91 1.1 alc powerMeasQ); 92 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, " iqCorrNeg is 0x%08x\n", 93 1.1 alc iqCorrNeg); 94 1.1 alc 95 1.1 alc iCoffDenom = (powerMeasI/2 + powerMeasQ/2)/ 128; 96 1.1 alc qCoffDenom = powerMeasQ / 64; 97 1.1 alc /* Protect against divide-by-0 */ 98 1.1 alc if (powerMeasQ != 0) { 99 1.1 alc /* IQ corr_meas is already negated if iqcorr_neg == 1 */ 100 1.1 alc int32_t iCoff = iqCorrMeas/iCoffDenom; 101 1.1 alc int32_t qCoff = powerMeasI/qCoffDenom - 64; 102 1.1 alc 103 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, " iCoff = 0x%08x\n", 104 1.1 alc iCoff); 105 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, " qCoff = 0x%08x\n", 106 1.1 alc qCoff); 107 1.1 alc 108 1.1 alc /* Negate iCoff if iqCorrNeg == 0 */ 109 1.1 alc iCoff = iCoff & 0x3f; 110 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, 111 1.1 alc "New: iCoff = 0x%08x\n", iCoff); 112 1.1 alc 113 1.1 alc if (iqCorrNeg == 0x0) 114 1.1 alc iCoff = 0x40 - iCoff; 115 1.1 alc if (qCoff > 15) 116 1.1 alc qCoff = 15; 117 1.1 alc else if (qCoff <= -16) 118 1.2 cegger qCoff = -16; 119 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, 120 1.1 alc " : iCoff = 0x%x qCoff = 0x%x\n", iCoff, qCoff); 121 1.1 alc 122 1.1 alc OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4_CHAIN(i), 123 1.1 alc AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff); 124 1.1 alc OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4_CHAIN(i), 125 1.1 alc AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff); 126 1.1 alc HALDEBUG(ah, HAL_DEBUG_PERCAL, 127 1.1 alc "IQ Cal and Correction done for Chain %d\n", i); 128 1.1 alc } 129 1.1 alc } 130 1.1 alc OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, 131 1.1 alc AR_PHY_TIMING_CTRL4_IQCORR_ENABLE); 132 1.1 alc } 133