/****************************************************************************** * * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * ******************************************************************************/ /****************************************************************************** * * * Module: rtl8192c_rf6052.c (Source C File) * * Note: Provide RF 6052 series relative API. * * Function: * * Export: * * Abbrev: * * History: * Data Who Remark * * 09/25/2008 MHC Create initial version. * 11/05/2008 MHC Add API for tw power setting. * * ******************************************************************************/ #define _RTL8723A_RF6052_C_ #include #include #include #include /*----------------------------------------------------------------------------- * Function: PHY_RF6052SetBandwidth() * * Overview: This function is called by SetBWMode23aCallback8190Pci() only * * Input: struct rtw_adapter * Adapter * WIRELESS_BANDWIDTH_E Bandwidth 20M or 40M * * Output: NONE * * Return: NONE * * Note: For RF type 0222D *---------------------------------------------------------------------------*/ void rtl8723a_phy_rf6052set_bw(struct rtw_adapter *Adapter, enum ht_channel_width Bandwidth) /* 20M or 40M */ { struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); switch (Bandwidth) { case HT_CHANNEL_WIDTH_20: pHalData->RfRegChnlVal[0] = (pHalData->RfRegChnlVal[0] & 0xfffff3ff) | 0x0400; PHY_SetRFReg(Adapter, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask, pHalData->RfRegChnlVal[0]); break; case HT_CHANNEL_WIDTH_40: pHalData->RfRegChnlVal[0] = (pHalData->RfRegChnlVal[0] & 0xfffff3ff); PHY_SetRFReg(Adapter, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask, pHalData->RfRegChnlVal[0]); break; default: break; } } /*----------------------------------------------------------------------------- * Function: PHY_RF6052SetCckTxPower * * Overview: * * Input: NONE * * Output: NONE * * Return: NONE * * Revised History: * When Who Remark * 11/05/2008 MHC Simulate 8192series.. * *---------------------------------------------------------------------------*/ void rtl823a_phy_rf6052setccktxpower(struct rtw_adapter *Adapter, u8 *pPowerlevel) { struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); struct dm_priv *pdmpriv = &pHalData->dmpriv; struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv; u32 TxAGC[2] = {0, 0}, tmpval = 0; u8 idx1, idx2; u8 *ptr; if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) { TxAGC[RF_PATH_A] = 0x3f3f3f3f; TxAGC[RF_PATH_B] = 0x3f3f3f3f; for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) { TxAGC[idx1] = pPowerlevel[idx1] | (pPowerlevel[idx1] << 8) | (pPowerlevel[idx1] << 16) | (pPowerlevel[idx1] << 24); /* * 2010/10/18 MH For external PA module. We need * to limit power index to be less than 0x20. */ if (TxAGC[idx1] > 0x20 && pHalData->ExternalPA) TxAGC[idx1] = 0x20; } } else { /* 20100427 Joseph: Driver dynamic Tx power shall not affect Tx * power. It shall be determined by power training mechanism. */ /* Currently, we cannot fully disable driver dynamic tx power * mechanism because it is referenced by BT coexist mechanism. */ /* In the future, two mechanism shall be separated from each other * and maintained independently. Thanks for Lanhsin's reminder. */ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1) { TxAGC[RF_PATH_A] = 0x10101010; TxAGC[RF_PATH_B] = 0x10101010; } else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2) { TxAGC[RF_PATH_A] = 0x00000000; TxAGC[RF_PATH_B] = 0x00000000; } else { for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) { TxAGC[idx1] = pPowerlevel[idx1] | (pPowerlevel[idx1] << 8) | (pPowerlevel[idx1] << 16) | (pPowerlevel[idx1] << 24); } if (pHalData->EEPROMRegulatory == 0) { tmpval = (pHalData->MCSTxPowerLevelOriginalOffset[0][6]) + (pHalData->MCSTxPowerLevelOriginalOffset[0][7]<<8); TxAGC[RF_PATH_A] += tmpval; tmpval = (pHalData->MCSTxPowerLevelOriginalOffset[0][14]) + (pHalData->MCSTxPowerLevelOriginalOffset[0][15]<<24); TxAGC[RF_PATH_B] += tmpval; } } } for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) { ptr = (u8 *)(&TxAGC[idx1]); for (idx2 = 0; idx2 < 4; idx2++) { if (*ptr > RF6052_MAX_TX_PWR) *ptr = RF6052_MAX_TX_PWR; ptr++; } } /* rf-A cck tx power */ tmpval = TxAGC[RF_PATH_A] & 0xff; PHY_SetBBReg(Adapter, rTxAGC_A_CCK1_Mcs32, bMaskByte1, tmpval); tmpval = TxAGC[RF_PATH_A] >> 8; PHY_SetBBReg(Adapter, rTxAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval); /* rf-B cck tx power */ tmpval = TxAGC[RF_PATH_B] >> 24; PHY_SetBBReg(Adapter, rTxAGC_B_CCK11_A_CCK2_11, bMaskByte0, tmpval); tmpval = TxAGC[RF_PATH_B] & 0x00ffffff; PHY_SetBBReg(Adapter, rTxAGC_B_CCK1_55_Mcs32, 0xffffff00, tmpval); } /* PHY_RF6052SetCckTxPower */ /* powerbase0 for OFDM rates */ /* powerbase1 for HT MCS rates */ static void getPowerBase(struct rtw_adapter *Adapter, u8 *pPowerLevel, u8 Channel, u32 *OfdmBase, u32 *MCSBase) { struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); u32 ofdm, mcs; u8 Legacy_pwrdiff = 0; s8 HT20_pwrdiff = 0; u8 i, powerlevel[2]; for (i = 0; i < 2; i++) { powerlevel[i] = pPowerLevel[i]; Legacy_pwrdiff = pHalData->TxPwrLegacyHtDiff[i][Channel-1]; ofdm = powerlevel[i] + Legacy_pwrdiff; ofdm = ofdm << 24 | ofdm << 16 | ofdm << 8 | ofdm; *(OfdmBase + i) = ofdm; } for (i = 0; i < 2; i++) { /* Check HT20 to HT40 diff */ if (pHalData->CurrentChannelBW == HT_CHANNEL_WIDTH_20) { HT20_pwrdiff = pHalData->TxPwrHt20Diff[i][Channel-1]; powerlevel[i] += HT20_pwrdiff; } mcs = powerlevel[i]; mcs = mcs << 24 | mcs << 16 | mcs << 8 | mcs; *(MCSBase + i) = mcs; } } static void getTxPowerWriteValByRegulatory(struct rtw_adapter *Adapter, u8 Channel, u8 index, u32 *powerBase0, u32 *powerBase1, u32 *pOutWriteVal) { struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); struct dm_priv *pdmpriv = &pHalData->dmpriv; u8 i, chnlGroup = 0, pwr_diff_limit[4]; u32 writeVal, customer_limit, rf; /* Index 0 & 1 = legacy OFDM, 2-5 = HT_MCS rate */ for (rf = 0; rf < 2; rf++) { switch (pHalData->EEPROMRegulatory) { case 0: /* Realtek better performance */ /* increase power diff defined by Realtek for * large power */ chnlGroup = 0; writeVal = pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf?8:0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); break; case 1: /* Realtek regulatory */ /* increase power diff defined by Realtek for * regulatory */ if (pHalData->pwrGroupCnt == 1) chnlGroup = 0; if (pHalData->pwrGroupCnt >= 3) { if (Channel <= 3) chnlGroup = 0; else if (Channel >= 4 && Channel <= 9) chnlGroup = 1; else if (Channel > 9) chnlGroup = 2; if (pHalData->CurrentChannelBW == HT_CHANNEL_WIDTH_20) chnlGroup++; else chnlGroup += 4; } writeVal = pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf?8:0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); break; case 2: /* Better regulatory */ /* don't increase any power diff */ writeVal = (index < 2) ? powerBase0[rf] : powerBase1[rf]; break; case 3: /* Customer defined power diff. */ chnlGroup = 0; for (i = 0; i < 4; i++) { pwr_diff_limit[i] = (u8)((pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index + (rf ? 8 : 0)]&(0x7f << (i*8))) >> (i*8)); if (pHalData->CurrentChannelBW == HT_CHANNEL_WIDTH_40) { if (pwr_diff_limit[i] > pHalData->PwrGroupHT40[rf][Channel-1]) pwr_diff_limit[i] = pHalData->PwrGroupHT40[rf][Channel-1]; } else { if (pwr_diff_limit[i] > pHalData->PwrGroupHT20[rf][Channel-1]) pwr_diff_limit[i] = pHalData->PwrGroupHT20[rf][Channel-1]; } } customer_limit = (pwr_diff_limit[3]<<24) | (pwr_diff_limit[2]<<16) | (pwr_diff_limit[1]<<8) | (pwr_diff_limit[0]); writeVal = customer_limit + ((index<2)?powerBase0[rf]:powerBase1[rf]); break; default: chnlGroup = 0; writeVal = pHalData->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf?8:0)] + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); break; } /* 20100427 Joseph: Driver dynamic Tx power shall not affect Tx power. It shall be determined by power training mechanism. */ /* Currently, we cannot fully disable driver dynamic tx power mechanism because it is referenced by BT coexist mechanism. */ /* In the future, two mechanism shall be separated from each other and maintained independently. Thanks for Lanhsin's reminder. */ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1) writeVal = 0x14141414; else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2) writeVal = 0x00000000; /* 20100628 Joseph: High power mode for BT-Coexist mechanism. */ /* This mechanism is only applied when Driver-Highpower-Mechanism is OFF. */ if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_BT1) writeVal = writeVal - 0x06060606; else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_BT2) writeVal = writeVal; *(pOutWriteVal + rf) = writeVal; } } static void writeOFDMPowerReg(struct rtw_adapter *Adapter, u8 index, u32 *pValue) { struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); u16 RegOffset_A[6] = { rTxAGC_A_Rate18_06, rTxAGC_A_Rate54_24, rTxAGC_A_Mcs03_Mcs00, rTxAGC_A_Mcs07_Mcs04, rTxAGC_A_Mcs11_Mcs08, rTxAGC_A_Mcs15_Mcs12 }; u16 RegOffset_B[6] = { rTxAGC_B_Rate18_06, rTxAGC_B_Rate54_24, rTxAGC_B_Mcs03_Mcs00, rTxAGC_B_Mcs07_Mcs04, rTxAGC_B_Mcs11_Mcs08, rTxAGC_B_Mcs15_Mcs12 }; u8 i, rf, pwr_val[4]; u32 writeVal; u16 RegOffset; for (rf = 0; rf < 2; rf++) { writeVal = pValue[rf]; for (i = 0; i < 4; i++) { pwr_val[i] = (u8)((writeVal & (0x7f << (i * 8))) >> (i * 8)); if (pwr_val[i] > RF6052_MAX_TX_PWR) pwr_val[i] = RF6052_MAX_TX_PWR; } writeVal = pwr_val[3] << 24 | pwr_val[2] << 16 | pwr_val[1] << 8 | pwr_val[0]; if (rf == 0) RegOffset = RegOffset_A[index]; else RegOffset = RegOffset_B[index]; rtl8723au_write32(Adapter, RegOffset, writeVal); /* 201005115 Joseph: Set Tx Power diff for Tx power training mechanism. */ if (((pHalData->rf_type == RF_2T2R) && (RegOffset == rTxAGC_A_Mcs15_Mcs12 || RegOffset == rTxAGC_B_Mcs15_Mcs12)) || ((pHalData->rf_type != RF_2T2R) && (RegOffset == rTxAGC_A_Mcs07_Mcs04 || RegOffset == rTxAGC_B_Mcs07_Mcs04))) { writeVal = pwr_val[3]; if (RegOffset == rTxAGC_A_Mcs15_Mcs12 || RegOffset == rTxAGC_A_Mcs07_Mcs04) RegOffset = 0xc90; if (RegOffset == rTxAGC_B_Mcs15_Mcs12 || RegOffset == rTxAGC_B_Mcs07_Mcs04) RegOffset = 0xc98; for (i = 0; i < 3; i++) { if (i != 2) writeVal = (writeVal > 8) ? (writeVal - 8) : 0; else writeVal = (writeVal > 6) ? (writeVal - 6) : 0; rtl8723au_write8(Adapter, RegOffset + i, (u8)writeVal); } } } } /*----------------------------------------------------------------------------- * Function: PHY_RF6052SetOFDMTxPower * * Overview: For legacy and HY OFDM, we must read EEPROM TX power index for * different channel and read original value in TX power * register area from 0xe00. We increase offset and * original value to be correct tx pwr. * * Input: NONE * * Output: NONE * * Return: NONE * * Revised History: * When Remark * 11/05/2008 MHC Simulate 8192 series method. * 01/06/2009 MHC 1. Prevent Path B tx power overflow or * underflow dure to A/B pwr difference or * legacy/HT pwr diff. * 2. We concern with path B legacy/HT OFDM difference. * 01/22/2009 MHC Support new EPRO format from SD3. * *---------------------------------------------------------------------------*/ void rtl8723a_PHY_RF6052SetOFDMTxPower(struct rtw_adapter *Adapter, u8 *pPowerLevel, u8 Channel) { u32 writeVal[2], powerBase0[2], powerBase1[2]; u8 index = 0; getPowerBase(Adapter, pPowerLevel, Channel, &powerBase0[0], &powerBase1[0]); for (index = 0; index < 6; index++) { getTxPowerWriteValByRegulatory(Adapter, Channel, index, &powerBase0[0], &powerBase1[0], &writeVal[0]); writeOFDMPowerReg(Adapter, index, &writeVal[0]); } } static int phy_RF6052_Config_ParaFile(struct rtw_adapter *Adapter) { u32 u4RegValue = 0; u8 eRFPath; struct bb_reg_define *pPhyReg; int rtStatus = _SUCCESS; struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); /* 3----------------------------------------------------------------- */ /* 3 <2> Initialize RF */ /* 3----------------------------------------------------------------- */ for (eRFPath = 0; eRFPath < pHalData->NumTotalRFPath; eRFPath++) { pPhyReg = &pHalData->PHYRegDef[eRFPath]; /*----Store original RFENV control type----*/ switch (eRFPath) { case RF_PATH_A: u4RegValue = PHY_QueryBBReg(Adapter, pPhyReg->rfintfs, bRFSI_RFENV); break; case RF_PATH_B: u4RegValue = PHY_QueryBBReg(Adapter, pPhyReg->rfintfs, bRFSI_RFENV << 16); break; } /*----Set RF_ENV enable----*/ PHY_SetBBReg(Adapter, pPhyReg->rfintfe, bRFSI_RFENV << 16, 0x1); udelay(1);/* PlatformStallExecution(1); */ /*----Set RF_ENV output high----*/ PHY_SetBBReg(Adapter, pPhyReg->rfintfo, bRFSI_RFENV, 0x1); udelay(1);/* PlatformStallExecution(1); */ /* Set bit number of Address and Data for RF register */ PHY_SetBBReg(Adapter, pPhyReg->rfHSSIPara2, b3WireAddressLength, 0x0); /* Set 1 to 4 bits for 8255 */ udelay(1);/* PlatformStallExecution(1); */ PHY_SetBBReg(Adapter, pPhyReg->rfHSSIPara2, b3WireDataLength, 0x0); /* Set 0 to 12 bits for 8255 */ udelay(1);/* PlatformStallExecution(1); */ /*----Initialize RF fom connfiguration file----*/ switch (eRFPath) { case RF_PATH_A: ODM_ReadAndConfig_RadioA_1T_8723A(&pHalData->odmpriv); break; case RF_PATH_B: break; } /*----Restore RFENV control type----*/; switch (eRFPath) { case RF_PATH_A: PHY_SetBBReg(Adapter, pPhyReg->rfintfs, bRFSI_RFENV, u4RegValue); break; case RF_PATH_B: PHY_SetBBReg(Adapter, pPhyReg->rfintfs, bRFSI_RFENV << 16, u4RegValue); break; } if (rtStatus != _SUCCESS) { goto phy_RF6052_Config_ParaFile_Fail; } } phy_RF6052_Config_ParaFile_Fail: return rtStatus; } int PHY_RF6052_Config8723A(struct rtw_adapter *Adapter) { struct hal_data_8723a *pHalData = GET_HAL_DATA(Adapter); /* Initialize general global value */ /* TODO: Extend RF_PATH_C and RF_PATH_D in the future */ if (pHalData->rf_type == RF_1T1R) pHalData->NumTotalRFPath = 1; else pHalData->NumTotalRFPath = 2; /* Config BB and RF */ return phy_RF6052_Config_ParaFile(Adapter); } /* End of HalRf6052.c */