ar9285_cal.c revision 221722
154359Sroberto/* 254359Sroberto * Copyright (c) 2008-2010 Atheros Communications Inc. 354359Sroberto * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. 4132451Sroberto * 554359Sroberto * Redistribution and use in source and binary forms, with or without 682498Sroberto * modification, are permitted provided that the following conditions 754359Sroberto * are met: 854359Sroberto * 1. Redistributions of source code must retain the above copyright 954359Sroberto * notice, this list of conditions and the following disclaimer. 1054359Sroberto * 2. Redistributions in binary form must reproduce the above copyright 1154359Sroberto * notice, this list of conditions and the following disclaimer in the 1254359Sroberto * documentation and/or other materials provided with the distribution. 1354359Sroberto * 1454359Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1554359Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1682498Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1782498Sroberto * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18132451Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1982498Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2082498Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2182498Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2282498Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2354359Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2454359Sroberto * SUCH DAMAGE. 2554359Sroberto * 2654359Sroberto * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9285_cal.c 221722 2011-05-10 04:32:27Z adrian $ 2754359Sroberto */ 2854359Sroberto#include "opt_ah.h" 2954359Sroberto#include "ah.h" 3054359Sroberto#include "ah_internal.h" 3154359Sroberto 3254359Sroberto#include "ah_eeprom_v4k.h" 3354359Sroberto 3454359Sroberto#include "ar9002/ar9285.h" 3554359Sroberto#include "ar5416/ar5416reg.h" 36132451Sroberto#include "ar5416/ar5416phy.h" 37132451Sroberto#include "ar9002/ar9002phy.h" 38132451Sroberto#include "ar9002/ar9285phy.h" 39132451Sroberto 40132451Sroberto#include "ar9002/ar9285_cal.h" 41132451Sroberto 4254359Sroberto#define AR9285_CLCAL_REDO_THRESH 1 43132451Sroberto#define MAX_PACAL_SKIPCOUNT 8 44132451Sroberto 4554359Sroberto#define N(a) (sizeof (a) / sizeof (a[0])) 4654359Sroberto 4754359Srobertostatic void 48132451Srobertoar9285_hw_pa_cal(struct ath_hal *ah, HAL_BOOL is_reset) 49132451Sroberto{ 50132451Sroberto uint32_t regVal; 5154359Sroberto int i, offset, offs_6_1, offs_0; 5254359Sroberto uint32_t ccomp_org, reg_field; 5354359Sroberto uint32_t regList[][2] = { 5454359Sroberto { 0x786c, 0 }, 5554359Sroberto { 0x7854, 0 }, 5654359Sroberto { 0x7820, 0 }, 5754359Sroberto { 0x7824, 0 }, 5854359Sroberto { 0x7868, 0 }, 5954359Sroberto { 0x783c, 0 }, 6054359Sroberto { 0x7838, 0 }, 61132451Sroberto }; 62132451Sroberto 6354359Sroberto /* PA CAL is not needed for high power solution */ 6454359Sroberto if (ath_hal_eepromGet(ah, AR_EEP_TXGAIN_TYPE, AH_NULL) == 65132451Sroberto AR5416_EEP_TXGAIN_HIGH_POWER) 66132451Sroberto return; 67132451Sroberto 68132451Sroberto HALDEBUG(ah, HAL_DEBUG_PERCAL, "Running PA Calibration\n"); 69132451Sroberto 70132451Sroberto for (i = 0; i < N(regList); i++) 71132451Sroberto regList[i][1] = OS_REG_READ(ah, regList[i][0]); 72132451Sroberto 73132451Sroberto regVal = OS_REG_READ(ah, 0x7834); 74132451Sroberto regVal &= (~(0x1)); 75132451Sroberto OS_REG_WRITE(ah, 0x7834, regVal); 76132451Sroberto regVal = OS_REG_READ(ah, 0x9808); 77132451Sroberto regVal |= (0x1 << 27); 78132451Sroberto OS_REG_WRITE(ah, 0x9808, regVal); 79132451Sroberto 80132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); 81132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); 82132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); 83132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); 84132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); 85132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); 86132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); 87132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); 88132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); 89132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); 90132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); 91132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); 92132451Sroberto ccomp_org = MS(OS_REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_CCOMP); 93132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, 0xf); 9454359Sroberto 95132451Sroberto OS_REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); 96132451Sroberto OS_DELAY(30); 97132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, 0); 98132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 0); 99132451Sroberto 100132451Sroberto for (i = 6; i > 0; i--) { 101182007Sroberto regVal = OS_REG_READ(ah, 0x7834); 102182007Sroberto regVal |= (1 << (19 + i)); 103182007Sroberto OS_REG_WRITE(ah, 0x7834, regVal); 10454359Sroberto OS_DELAY(1); 105132451Sroberto regVal = OS_REG_READ(ah, 0x7834); 10654359Sroberto regVal &= (~(0x1 << (19 + i))); 10754359Sroberto reg_field = MS(OS_REG_READ(ah, 0x7840), AR9285_AN_RXTXBB1_SPARE9); 108132451Sroberto regVal |= (reg_field << (19 + i)); 109132451Sroberto OS_REG_WRITE(ah, 0x7834, regVal); 11054359Sroberto } 11154359Sroberto 112132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, 1); 11354359Sroberto OS_DELAY(1); 11454359Sroberto reg_field = MS(OS_REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9); 11554359Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, reg_field); 11654359Sroberto offs_6_1 = MS(OS_REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS); 11754359Sroberto offs_0 = MS(OS_REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP); 11854359Sroberto 119132451Sroberto offset = (offs_6_1<<1) | offs_0; 120132451Sroberto offset = offset - 0; 121132451Sroberto offs_6_1 = offset>>1; 122132451Sroberto offs_0 = offset & 1; 123132451Sroberto 124132451Sroberto if ((!is_reset) && (AH9285(ah)->pacal_info.prev_offset == offset)) { 125132451Sroberto if (AH9285(ah)->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT) 126132451Sroberto AH9285(ah)->pacal_info.max_skipcount = 127132451Sroberto 2 * AH9285(ah)->pacal_info.max_skipcount; 128132451Sroberto AH9285(ah)->pacal_info.skipcount = AH9285(ah)->pacal_info.max_skipcount; 129132451Sroberto } else { 130132451Sroberto AH9285(ah)->pacal_info.max_skipcount = 1; 131132451Sroberto AH9285(ah)->pacal_info.skipcount = 0; 132132451Sroberto AH9285(ah)->pacal_info.prev_offset = offset; 133132451Sroberto } 134132451Sroberto 135132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1); 136132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0); 137132451Sroberto 138132451Sroberto regVal = OS_REG_READ(ah, 0x7834); 139132451Sroberto regVal |= 0x1; 140132451Sroberto OS_REG_WRITE(ah, 0x7834, regVal); 141132451Sroberto regVal = OS_REG_READ(ah, 0x9808); 142132451Sroberto regVal &= (~(0x1 << 27)); 143132451Sroberto OS_REG_WRITE(ah, 0x9808, regVal); 144132451Sroberto 145132451Sroberto for (i = 0; i < N(regList); i++) 146132451Sroberto OS_REG_WRITE(ah, regList[i][0], regList[i][1]); 147132451Sroberto 148132451Sroberto OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_CCOMP, ccomp_org); 149132451Sroberto} 150132451Sroberto 151132451Srobertovoid 152132451Srobertoar9002_hw_pa_cal(struct ath_hal *ah, HAL_BOOL is_reset) 153132451Sroberto{ 154132451Sroberto if (AR_SREV_KITE_11_OR_LATER(ah)) { 155132451Sroberto if (is_reset || !AH9285(ah)->pacal_info.skipcount) 156132451Sroberto ar9285_hw_pa_cal(ah, is_reset); 157132451Sroberto else 158132451Sroberto AH9285(ah)->pacal_info.skipcount--; 159132451Sroberto } 160132451Sroberto} 161132451Sroberto 162132451Sroberto/* Carrier leakage Calibration fix */ 163132451Srobertostatic HAL_BOOL 164132451Srobertoar9285_hw_cl_cal(struct ath_hal *ah, const struct ieee80211_channel *chan) 16554359Sroberto{ 166132451Sroberto OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); 16754359Sroberto if (IEEE80211_IS_CHAN_HT20(chan)) { 16854359Sroberto OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); 169132451Sroberto OS_REG_SET_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); 170132451Sroberto OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, 171132451Sroberto AR_PHY_AGC_CONTROL_FLTR_CAL); 172132451Sroberto OS_REG_CLR_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); 173132451Sroberto OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); 174132451Sroberto if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, 17554359Sroberto AR_PHY_AGC_CONTROL_CAL, 0)) { 176182007Sroberto HALDEBUG(ah, HAL_DEBUG_PERCAL, 177182007Sroberto "offset calibration failed to complete in 1ms; noisy environment?\n"); 178182007Sroberto return AH_FALSE; 179132451Sroberto } 18054359Sroberto OS_REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); 18154359Sroberto OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); 18254359Sroberto OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); 18354359Sroberto } 18454359Sroberto OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); 18554359Sroberto OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); 18654359Sroberto OS_REG_SET_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); 18782498Sroberto OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); 18854359Sroberto if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 18954359Sroberto 0)) { 19054359Sroberto HALDEBUG(ah, HAL_DEBUG_PERCAL, 19154359Sroberto "offset calibration failed to complete in 1ms; noisy environment?\n"); 19254359Sroberto return AH_FALSE; 19354359Sroberto } 19454359Sroberto 19554359Sroberto OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); 19654359Sroberto OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); 19754359Sroberto OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); 19854359Sroberto 19954359Sroberto return AH_TRUE; 20054359Sroberto} 20154359Sroberto 20254359Srobertostatic HAL_BOOL 20354359Srobertoar9285_hw_clc(struct ath_hal *ah, const struct ieee80211_channel *chan) 20454359Sroberto{ 20554359Sroberto int i; 20654359Sroberto uint32_t txgain_max; 20754359Sroberto uint32_t clc_gain, gain_mask = 0, clc_num = 0; 20854359Sroberto uint32_t reg_clc_I0, reg_clc_Q0; 20954359Sroberto uint32_t i0_num = 0; 21054359Sroberto uint32_t q0_num = 0; 21154359Sroberto uint32_t total_num = 0; 21254359Sroberto uint32_t reg_rf2g5_org; 21354359Sroberto HAL_BOOL retv = AH_TRUE; 21454359Sroberto 21554359Sroberto if (!(ar9285_hw_cl_cal(ah, chan))) 21654359Sroberto return AH_FALSE; 21754359Sroberto 21854359Sroberto txgain_max = MS(OS_REG_READ(ah, AR_PHY_TX_PWRCTRL7), 219132451Sroberto AR_PHY_TX_PWRCTRL_TX_GAIN_TAB_MAX); 22054359Sroberto 22154359Sroberto for (i = 0; i < (txgain_max+1); i++) { 22254359Sroberto clc_gain = (OS_REG_READ(ah, (AR_PHY_TX_GAIN_TBL1+(i<<2))) & 22354359Sroberto AR_PHY_TX_GAIN_CLC) >> AR_PHY_TX_GAIN_CLC_S; 22454359Sroberto if (!(gain_mask & (1 << clc_gain))) { 22554359Sroberto gain_mask |= (1 << clc_gain); 22654359Sroberto clc_num++; 22754359Sroberto } 22854359Sroberto } 22954359Sroberto 23054359Sroberto for (i = 0; i < clc_num; i++) { 23154359Sroberto reg_clc_I0 = (OS_REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) 23254359Sroberto & AR_PHY_CLC_I0) >> AR_PHY_CLC_I0_S; 23354359Sroberto reg_clc_Q0 = (OS_REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) 23454359Sroberto & AR_PHY_CLC_Q0) >> AR_PHY_CLC_Q0_S; 23554359Sroberto if (reg_clc_I0 == 0) 23654359Sroberto i0_num++; 23754359Sroberto 23854359Sroberto if (reg_clc_Q0 == 0) 23954359Sroberto q0_num++; 24054359Sroberto } 24154359Sroberto total_num = i0_num + q0_num; 24254359Sroberto if (total_num > AR9285_CLCAL_REDO_THRESH) { 24354359Sroberto reg_rf2g5_org = OS_REG_READ(ah, AR9285_RF2G5); 24454359Sroberto if (AR_SREV_9285E_20(ah)) { 245132451Sroberto OS_REG_WRITE(ah, AR9285_RF2G5, 24654359Sroberto (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | 24754359Sroberto AR9285_RF2G5_IC50TX_XE_SET); 24854359Sroberto } else { 24954359Sroberto OS_REG_WRITE(ah, AR9285_RF2G5, 25054359Sroberto (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | 25154359Sroberto AR9285_RF2G5_IC50TX_SET); 25254359Sroberto } 25354359Sroberto retv = ar9285_hw_cl_cal(ah, chan); 25454359Sroberto OS_REG_WRITE(ah, AR9285_RF2G5, reg_rf2g5_org); 25554359Sroberto } 25654359Sroberto return retv; 25754359Sroberto} 25854359Sroberto 25954359SrobertoHAL_BOOL 26054359Srobertoar9285InitCalHardware(struct ath_hal *ah, 26154359Sroberto const struct ieee80211_channel *chan) 26254359Sroberto{ 26354359Sroberto if (AR_SREV_KITE(ah) && AR_SREV_KITE_10_OR_LATER(ah) && 26454359Sroberto (! ar9285_hw_clc(ah, chan))) 26554359Sroberto return AH_FALSE; 26654359Sroberto 26754359Sroberto return AH_TRUE; 26854359Sroberto} 26954359Sroberto