1331722Seadler/* 2219481Sadrian * Copyright (c) 2008-2010 Atheros Communications Inc. 3219481Sadrian * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. 4219481Sadrian * 5219481Sadrian * Redistribution and use in source and binary forms, with or without 6219481Sadrian * modification, are permitted provided that the following conditions 7219481Sadrian * are met: 8219481Sadrian * 1. Redistributions of source code must retain the above copyright 9219481Sadrian * notice, this list of conditions and the following disclaimer. 10219481Sadrian * 2. Redistributions in binary form must reproduce the above copyright 11219481Sadrian * notice, this list of conditions and the following disclaimer in the 12219481Sadrian * documentation and/or other materials provided with the distribution. 13219481Sadrian * 14219481Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15219481Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16219481Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17219481Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18219481Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19219481Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20219481Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21219481Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22219481Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23219481Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24219481Sadrian * SUCH DAMAGE. 25219481Sadrian * 26219481Sadrian * $FreeBSD$ 27219481Sadrian */ 28219481Sadrian#include "opt_ah.h" 29219481Sadrian#include "ah.h" 30219481Sadrian#include "ah_internal.h" 31219481Sadrian 32219481Sadrian#include "ah_eeprom_v4k.h" 33219481Sadrian 34219481Sadrian#include "ar9002/ar9285.h" 35219481Sadrian#include "ar5416/ar5416reg.h" 36219481Sadrian#include "ar5416/ar5416phy.h" 37219481Sadrian#include "ar9002/ar9002phy.h" 38219481Sadrian#include "ar9002/ar9285phy.h" 39221806Sadrian#include "ar9002/ar9285an.h" 40219481Sadrian 41219481Sadrian#include "ar9002/ar9285_cal.h" 42219481Sadrian 43219481Sadrian#define AR9285_CLCAL_REDO_THRESH 1 44219481Sadrian#define MAX_PACAL_SKIPCOUNT 8 45219481Sadrian 46219481Sadrian#define N(a) (sizeof (a) / sizeof (a[0])) 47219481Sadrian 48219481Sadrianstatic void 49219481Sadrianar9285_hw_pa_cal(struct ath_hal *ah, HAL_BOOL is_reset) 50219481Sadrian{ 51219481Sadrian uint32_t regVal; 52219481Sadrian int i, offset, offs_6_1, offs_0; 53219481Sadrian uint32_t ccomp_org, reg_field; 54219481Sadrian uint32_t regList[][2] = { 55219481Sadrian { 0x786c, 0 }, 56219481Sadrian { 0x7854, 0 }, 57219481Sadrian { 0x7820, 0 }, 58219481Sadrian { 0x7824, 0 }, 59219481Sadrian { 0x7868, 0 }, 60219481Sadrian { 0x783c, 0 }, 61219481Sadrian { 0x7838, 0 }, 62219481Sadrian }; 63219481Sadrian 64219481Sadrian /* PA CAL is not needed for high power solution */ 65219481Sadrian if (ath_hal_eepromGet(ah, AR_EEP_TXGAIN_TYPE, AH_NULL) == 66219481Sadrian AR5416_EEP_TXGAIN_HIGH_POWER) 67219481Sadrian return; 68219481Sadrian 69221722Sadrian HALDEBUG(ah, HAL_DEBUG_PERCAL, "Running PA Calibration\n"); 70221722Sadrian 71219481Sadrian for (i = 0; i < N(regList); i++) 72219481Sadrian regList[i][1] = OS_REG_READ(ah, regList[i][0]); 73219481Sadrian 74219481Sadrian regVal = OS_REG_READ(ah, 0x7834); 75219481Sadrian regVal &= (~(0x1)); 76219481Sadrian OS_REG_WRITE(ah, 0x7834, regVal); 77219481Sadrian regVal = OS_REG_READ(ah, 0x9808); 78219481Sadrian regVal |= (0x1 << 27); 79219481Sadrian OS_REG_WRITE(ah, 0x9808, regVal); 80219481Sadrian 81219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); 82219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); 83219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); 84219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); 85219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); 86219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); 87219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); 88219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); 89219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); 90219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); 91219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); 92219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); 93219481Sadrian ccomp_org = MS(OS_REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP); 94219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 0xf); 95219481Sadrian 96219481Sadrian OS_REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); 97219481Sadrian OS_DELAY(30); 98219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, 0); 99219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 0); 100219481Sadrian 101219481Sadrian for (i = 6; i > 0; i--) { 102219481Sadrian regVal = OS_REG_READ(ah, 0x7834); 103219481Sadrian regVal |= (1 << (19 + i)); 104219481Sadrian OS_REG_WRITE(ah, 0x7834, regVal); 105219481Sadrian OS_DELAY(1); 106219481Sadrian regVal = OS_REG_READ(ah, 0x7834); 107219481Sadrian regVal &= (~(0x1 << (19 + i))); 108219481Sadrian reg_field = MS(OS_REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9); 109219481Sadrian regVal |= (reg_field << (19 + i)); 110219481Sadrian OS_REG_WRITE(ah, 0x7834, regVal); 111219481Sadrian } 112219481Sadrian 113219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 1); 114219481Sadrian OS_DELAY(1); 115219481Sadrian reg_field = MS(OS_REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9); 116219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, reg_field); 117219481Sadrian offs_6_1 = MS(OS_REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS); 118219481Sadrian offs_0 = MS(OS_REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP); 119219481Sadrian 120219481Sadrian offset = (offs_6_1<<1) | offs_0; 121219481Sadrian offset = offset - 0; 122219481Sadrian offs_6_1 = offset>>1; 123219481Sadrian offs_0 = offset & 1; 124219481Sadrian 125219481Sadrian if ((!is_reset) && (AH9285(ah)->pacal_info.prev_offset == offset)) { 126219481Sadrian if (AH9285(ah)->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT) 127219481Sadrian AH9285(ah)->pacal_info.max_skipcount = 128219481Sadrian 2 * AH9285(ah)->pacal_info.max_skipcount; 129219481Sadrian AH9285(ah)->pacal_info.skipcount = AH9285(ah)->pacal_info.max_skipcount; 130219481Sadrian } else { 131219481Sadrian AH9285(ah)->pacal_info.max_skipcount = 1; 132219481Sadrian AH9285(ah)->pacal_info.skipcount = 0; 133219481Sadrian AH9285(ah)->pacal_info.prev_offset = offset; 134219481Sadrian } 135219481Sadrian 136219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1); 137219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0); 138219481Sadrian 139219481Sadrian regVal = OS_REG_READ(ah, 0x7834); 140219481Sadrian regVal |= 0x1; 141219481Sadrian OS_REG_WRITE(ah, 0x7834, regVal); 142219481Sadrian regVal = OS_REG_READ(ah, 0x9808); 143219481Sadrian regVal &= (~(0x1 << 27)); 144219481Sadrian OS_REG_WRITE(ah, 0x9808, regVal); 145219481Sadrian 146219481Sadrian for (i = 0; i < N(regList); i++) 147219481Sadrian OS_REG_WRITE(ah, regList[i][0], regList[i][1]); 148219481Sadrian 149219481Sadrian OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org); 150219481Sadrian} 151219481Sadrian 152219481Sadrianvoid 153219481Sadrianar9002_hw_pa_cal(struct ath_hal *ah, HAL_BOOL is_reset) 154219481Sadrian{ 155221722Sadrian if (AR_SREV_KITE_11_OR_LATER(ah)) { 156219481Sadrian if (is_reset || !AH9285(ah)->pacal_info.skipcount) 157219481Sadrian ar9285_hw_pa_cal(ah, is_reset); 158219481Sadrian else 159219481Sadrian AH9285(ah)->pacal_info.skipcount--; 160219481Sadrian } 161219481Sadrian} 162219481Sadrian 163219481Sadrian/* Carrier leakage Calibration fix */ 164219481Sadrianstatic HAL_BOOL 165219481Sadrianar9285_hw_cl_cal(struct ath_hal *ah, const struct ieee80211_channel *chan) 166219481Sadrian{ 167219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); 168219481Sadrian if (IEEE80211_IS_CHAN_HT20(chan)) { 169219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); 170219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); 171219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, 172219481Sadrian AR_PHY_AGC_CONTROL_FLTR_CAL); 173219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); 174219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); 175219481Sadrian if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, 176219481Sadrian AR_PHY_AGC_CONTROL_CAL, 0)) { 177219481Sadrian HALDEBUG(ah, HAL_DEBUG_PERCAL, 178219481Sadrian "offset calibration failed to complete in 1ms; noisy environment?\n"); 179219481Sadrian return AH_FALSE; 180219481Sadrian } 181219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); 182219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); 183219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); 184219481Sadrian } 185219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); 186219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); 187219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); 188219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); 189219481Sadrian if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 190219481Sadrian 0)) { 191219481Sadrian HALDEBUG(ah, HAL_DEBUG_PERCAL, 192219481Sadrian "offset calibration failed to complete in 1ms; noisy environment?\n"); 193219481Sadrian return AH_FALSE; 194219481Sadrian } 195219481Sadrian 196219481Sadrian OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); 197219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); 198219481Sadrian OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); 199219481Sadrian 200219481Sadrian return AH_TRUE; 201219481Sadrian} 202219481Sadrian 203219481Sadrianstatic HAL_BOOL 204219481Sadrianar9285_hw_clc(struct ath_hal *ah, const struct ieee80211_channel *chan) 205219481Sadrian{ 206219481Sadrian int i; 207219481Sadrian uint32_t txgain_max; 208219481Sadrian uint32_t clc_gain, gain_mask = 0, clc_num = 0; 209219481Sadrian uint32_t reg_clc_I0, reg_clc_Q0; 210219481Sadrian uint32_t i0_num = 0; 211219481Sadrian uint32_t q0_num = 0; 212219481Sadrian uint32_t total_num = 0; 213219481Sadrian uint32_t reg_rf2g5_org; 214219481Sadrian HAL_BOOL retv = AH_TRUE; 215219481Sadrian 216219481Sadrian if (!(ar9285_hw_cl_cal(ah, chan))) 217219481Sadrian return AH_FALSE; 218219481Sadrian 219219481Sadrian txgain_max = MS(OS_REG_READ(ah, AR_PHY_TX_PWRCTRL7), 220219481Sadrian AR_PHY_TX_PWRCTRL_TX_GAIN_TAB_MAX); 221219481Sadrian 222219481Sadrian for (i = 0; i < (txgain_max+1); i++) { 223219481Sadrian clc_gain = (OS_REG_READ(ah, (AR_PHY_TX_GAIN_TBL1+(i<<2))) & 224219481Sadrian AR_PHY_TX_GAIN_CLC) >> AR_PHY_TX_GAIN_CLC_S; 225219481Sadrian if (!(gain_mask & (1 << clc_gain))) { 226219481Sadrian gain_mask |= (1 << clc_gain); 227219481Sadrian clc_num++; 228219481Sadrian } 229219481Sadrian } 230219481Sadrian 231219481Sadrian for (i = 0; i < clc_num; i++) { 232219481Sadrian reg_clc_I0 = (OS_REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) 233219481Sadrian & AR_PHY_CLC_I0) >> AR_PHY_CLC_I0_S; 234219481Sadrian reg_clc_Q0 = (OS_REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) 235219481Sadrian & AR_PHY_CLC_Q0) >> AR_PHY_CLC_Q0_S; 236219481Sadrian if (reg_clc_I0 == 0) 237219481Sadrian i0_num++; 238219481Sadrian 239219481Sadrian if (reg_clc_Q0 == 0) 240219481Sadrian q0_num++; 241219481Sadrian } 242219481Sadrian total_num = i0_num + q0_num; 243219481Sadrian if (total_num > AR9285_CLCAL_REDO_THRESH) { 244219481Sadrian reg_rf2g5_org = OS_REG_READ(ah, AR9285_RF2G5); 245219481Sadrian if (AR_SREV_9285E_20(ah)) { 246219481Sadrian OS_REG_WRITE(ah, AR9285_RF2G5, 247219481Sadrian (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | 248219481Sadrian AR9285_RF2G5_IC50TX_XE_SET); 249219481Sadrian } else { 250219481Sadrian OS_REG_WRITE(ah, AR9285_RF2G5, 251219481Sadrian (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | 252219481Sadrian AR9285_RF2G5_IC50TX_SET); 253219481Sadrian } 254219481Sadrian retv = ar9285_hw_cl_cal(ah, chan); 255219481Sadrian OS_REG_WRITE(ah, AR9285_RF2G5, reg_rf2g5_org); 256219481Sadrian } 257219481Sadrian return retv; 258219481Sadrian} 259219481Sadrian 260219481SadrianHAL_BOOL 261219481Sadrianar9285InitCalHardware(struct ath_hal *ah, 262219481Sadrian const struct ieee80211_channel *chan) 263219481Sadrian{ 264221722Sadrian if (AR_SREV_KITE(ah) && AR_SREV_KITE_10_OR_LATER(ah) && 265221722Sadrian (! ar9285_hw_clc(ah, chan))) 266219481Sadrian return AH_FALSE; 267219481Sadrian 268219481Sadrian return AH_TRUE; 269219481Sadrian} 270