1/****************************************************************************** 2 * 3 * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved. 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of version 2 of the GNU General Public License as 7 * published by the Free Software Foundation. 8 * 9 * This program is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 * more details. 13 * 14 * You should have received a copy of the GNU General Public License along with 15 * this program; if not, write to the Free Software Foundation, Inc., 16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 17 * 18 ******************************************************************************/ 19 20#include <osdep_service.h> 21#include <drv_types.h> 22#include <phy.h> 23#include <rf.h> 24#include <rtl8188e_hal.h> 25 26void rtl88eu_phy_rf6052_set_bandwidth(struct adapter *adapt, 27 enum ht_channel_width bandwidth) 28{ 29 struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt); 30 31 switch (bandwidth) { 32 case HT_CHANNEL_WIDTH_20: 33 hal_data->RfRegChnlVal[0] = ((hal_data->RfRegChnlVal[0] & 34 0xfffff3ff) | BIT(10) | BIT(11)); 35 phy_set_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask, 36 hal_data->RfRegChnlVal[0]); 37 break; 38 case HT_CHANNEL_WIDTH_40: 39 hal_data->RfRegChnlVal[0] = ((hal_data->RfRegChnlVal[0] & 40 0xfffff3ff) | BIT(10)); 41 phy_set_rf_reg(adapt, RF_PATH_A, RF_CHNLBW, bRFRegOffsetMask, 42 hal_data->RfRegChnlVal[0]); 43 break; 44 default: 45 break; 46 } 47} 48 49void rtl88eu_phy_rf6052_set_cck_txpower(struct adapter *adapt, u8 *powerlevel) 50{ 51 struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt); 52 struct dm_priv *pdmpriv = &hal_data->dmpriv; 53 struct mlme_ext_priv *pmlmeext = &adapt->mlmeextpriv; 54 u32 tx_agc[2] = {0, 0}, tmpval = 0, pwrtrac_value; 55 u8 idx1, idx2; 56 u8 *ptr; 57 u8 direction; 58 59 60 if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) { 61 tx_agc[RF_PATH_A] = 0x3f3f3f3f; 62 tx_agc[RF_PATH_B] = 0x3f3f3f3f; 63 for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) { 64 tx_agc[idx1] = powerlevel[idx1] | 65 (powerlevel[idx1]<<8) | 66 (powerlevel[idx1]<<16) | 67 (powerlevel[idx1]<<24); 68 if (tx_agc[idx1] > 0x20 && hal_data->ExternalPA) 69 tx_agc[idx1] = 0x20; 70 } 71 } else { 72 if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1) { 73 tx_agc[RF_PATH_A] = 0x10101010; 74 tx_agc[RF_PATH_B] = 0x10101010; 75 } else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2) { 76 tx_agc[RF_PATH_A] = 0x00000000; 77 tx_agc[RF_PATH_B] = 0x00000000; 78 } else { 79 for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) { 80 tx_agc[idx1] = powerlevel[idx1] | 81 (powerlevel[idx1]<<8) | 82 (powerlevel[idx1]<<16) | 83 (powerlevel[idx1]<<24); 84 } 85 if (hal_data->EEPROMRegulatory == 0) { 86 tmpval = hal_data->MCSTxPowerLevelOriginalOffset[0][6] + 87 (hal_data->MCSTxPowerLevelOriginalOffset[0][7]<<8); 88 tx_agc[RF_PATH_A] += tmpval; 89 90 tmpval = hal_data->MCSTxPowerLevelOriginalOffset[0][14] + 91 (hal_data->MCSTxPowerLevelOriginalOffset[0][15]<<24); 92 tx_agc[RF_PATH_B] += tmpval; 93 } 94 } 95 } 96 for (idx1 = RF_PATH_A; idx1 <= RF_PATH_B; idx1++) { 97 ptr = (u8 *)(&(tx_agc[idx1])); 98 for (idx2 = 0; idx2 < 4; idx2++) { 99 if (*ptr > RF6052_MAX_TX_PWR) 100 *ptr = RF6052_MAX_TX_PWR; 101 ptr++; 102 } 103 } 104 rtl88eu_dm_txpower_track_adjust(&hal_data->odmpriv, 1, &direction, 105 &pwrtrac_value); 106 107 if (direction == 1) { 108 /* Increase TX power */ 109 tx_agc[0] += pwrtrac_value; 110 tx_agc[1] += pwrtrac_value; 111 } else if (direction == 2) { 112 /* Decrease TX power */ 113 tx_agc[0] -= pwrtrac_value; 114 tx_agc[1] -= pwrtrac_value; 115 } 116 117 /* rf-A cck tx power */ 118 tmpval = tx_agc[RF_PATH_A]&0xff; 119 phy_set_bb_reg(adapt, rTxAGC_A_CCK1_Mcs32, bMaskByte1, tmpval); 120 tmpval = tx_agc[RF_PATH_A]>>8; 121 phy_set_bb_reg(adapt, rTxAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval); 122 123 /* rf-B cck tx power */ 124 tmpval = tx_agc[RF_PATH_B]>>24; 125 phy_set_bb_reg(adapt, rTxAGC_B_CCK11_A_CCK2_11, bMaskByte0, tmpval); 126 tmpval = tx_agc[RF_PATH_B]&0x00ffffff; 127 phy_set_bb_reg(adapt, rTxAGC_B_CCK1_55_Mcs32, 0xffffff00, tmpval); 128} 129 130/* powerbase0 for OFDM rates */ 131/* powerbase1 for HT MCS rates */ 132static void getpowerbase88e(struct adapter *adapt, u8 *pwr_level_ofdm, 133 u8 *pwr_level_bw20, u8 *pwr_level_bw40, 134 u8 channel, u32 *ofdmbase, u32 *mcs_base) 135{ 136 struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt); 137 u32 powerbase0, powerbase1; 138 u8 i, powerlevel[2]; 139 140 for (i = 0; i < 2; i++) { 141 powerbase0 = pwr_level_ofdm[i]; 142 143 powerbase0 = (powerbase0<<24) | (powerbase0<<16) | 144 (powerbase0<<8) | powerbase0; 145 *(ofdmbase+i) = powerbase0; 146 } 147 for (i = 0; i < hal_data->NumTotalRFPath; i++) { 148 /* Check HT20 to HT40 diff */ 149 if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_20) 150 powerlevel[i] = pwr_level_bw20[i]; 151 else 152 powerlevel[i] = pwr_level_bw40[i]; 153 powerbase1 = powerlevel[i]; 154 powerbase1 = (powerbase1<<24) | (powerbase1<<16) | 155 (powerbase1<<8) | powerbase1; 156 *(mcs_base+i) = powerbase1; 157 } 158} 159static void get_rx_power_val_by_reg(struct adapter *adapt, u8 channel, 160 u8 index, u32 *powerbase0, u32 *powerbase1, 161 u32 *out_val) 162{ 163 struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt); 164 struct dm_priv *pdmpriv = &hal_data->dmpriv; 165 u8 i, chnlGroup = 0, pwr_diff_limit[4], customer_pwr_limit; 166 s8 pwr_diff = 0; 167 u32 write_val, customer_limit, rf; 168 u8 regulatory = hal_data->EEPROMRegulatory; 169 170 /* Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */ 171 172 for (rf = 0; rf < 2; rf++) { 173 u8 j = index + (rf ? 8 : 0); 174 175 switch (regulatory) { 176 case 0: 177 chnlGroup = 0; 178 write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf ? 8 : 0)] + 179 ((index < 2) ? powerbase0[rf] : powerbase1[rf]); 180 break; 181 case 1: /* Realtek regulatory */ 182 /* increase power diff defined by Realtek for regulatory */ 183 if (hal_data->pwrGroupCnt == 1) 184 chnlGroup = 0; 185 if (hal_data->pwrGroupCnt >= hal_data->PGMaxGroup) { 186 if (channel < 3) 187 chnlGroup = 0; 188 else if (channel < 6) 189 chnlGroup = 1; 190 else if (channel < 9) 191 chnlGroup = 2; 192 else if (channel < 12) 193 chnlGroup = 3; 194 else if (channel < 14) 195 chnlGroup = 4; 196 else if (channel == 14) 197 chnlGroup = 5; 198 } 199 write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][index+(rf ? 8 : 0)] + 200 ((index < 2) ? powerbase0[rf] : powerbase1[rf]); 201 break; 202 case 2: /* Better regulatory */ 203 /* don't increase any power diff */ 204 write_val = (index < 2) ? powerbase0[rf] : powerbase1[rf]; 205 break; 206 case 3: /* Customer defined power diff. */ 207 /* increase power diff defined by customer. */ 208 chnlGroup = 0; 209 210 if (index < 2) 211 pwr_diff = hal_data->TxPwrLegacyHtDiff[rf][channel-1]; 212 else if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_20) 213 pwr_diff = hal_data->TxPwrHt20Diff[rf][channel-1]; 214 215 if (hal_data->CurrentChannelBW == HT_CHANNEL_WIDTH_40) 216 customer_pwr_limit = hal_data->PwrGroupHT40[rf][channel-1]; 217 else 218 customer_pwr_limit = hal_data->PwrGroupHT20[rf][channel-1]; 219 220 if (pwr_diff >= customer_pwr_limit) 221 pwr_diff = 0; 222 else 223 pwr_diff = customer_pwr_limit - pwr_diff; 224 225 for (i = 0; i < 4; i++) { 226 pwr_diff_limit[i] = (u8)((hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] & 227 (0x7f << (i * 8))) >> (i * 8)); 228 229 if (pwr_diff_limit[i] > pwr_diff) 230 pwr_diff_limit[i] = pwr_diff; 231 } 232 customer_limit = (pwr_diff_limit[3]<<24) | 233 (pwr_diff_limit[2]<<16) | 234 (pwr_diff_limit[1]<<8) | 235 (pwr_diff_limit[0]); 236 write_val = customer_limit + ((index < 2) ? powerbase0[rf] : powerbase1[rf]); 237 break; 238 default: 239 chnlGroup = 0; 240 write_val = hal_data->MCSTxPowerLevelOriginalOffset[chnlGroup][j] + 241 ((index < 2) ? powerbase0[rf] : powerbase1[rf]); 242 break; 243 } 244/* 20100427 Joseph: Driver dynamic Tx power shall not affect Tx power. It shall be determined by power training mechanism. */ 245/* Currently, we cannot fully disable driver dynamic tx power mechanism because it is referenced by BT coexist mechanism. */ 246/* In the future, two mechanism shall be separated from each other and maintained independently. Thanks for Lanhsin's reminder. */ 247 /* 92d do not need this */ 248 if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level1) 249 write_val = 0x14141414; 250 else if (pdmpriv->DynamicTxHighPowerLvl == TxHighPwrLevel_Level2) 251 write_val = 0x00000000; 252 253 *(out_val+rf) = write_val; 254 } 255} 256 257static void write_ofdm_pwr_reg(struct adapter *adapt, u8 index, u32 *pvalue) 258{ 259 u16 regoffset_a[6] = { rTxAGC_A_Rate18_06, rTxAGC_A_Rate54_24, 260 rTxAGC_A_Mcs03_Mcs00, rTxAGC_A_Mcs07_Mcs04, 261 rTxAGC_A_Mcs11_Mcs08, rTxAGC_A_Mcs15_Mcs12 }; 262 u16 regoffset_b[6] = { rTxAGC_B_Rate18_06, rTxAGC_B_Rate54_24, 263 rTxAGC_B_Mcs03_Mcs00, rTxAGC_B_Mcs07_Mcs04, 264 rTxAGC_B_Mcs11_Mcs08, rTxAGC_B_Mcs15_Mcs12 }; 265 u8 i, rf, pwr_val[4]; 266 u32 write_val; 267 u16 regoffset; 268 269 for (rf = 0; rf < 2; rf++) { 270 write_val = pvalue[rf]; 271 for (i = 0; i < 4; i++) { 272 pwr_val[i] = (u8)((write_val & (0x7f<<(i*8)))>>(i*8)); 273 if (pwr_val[i] > RF6052_MAX_TX_PWR) 274 pwr_val[i] = RF6052_MAX_TX_PWR; 275 } 276 write_val = (pwr_val[3]<<24) | (pwr_val[2]<<16) | 277 (pwr_val[1]<<8) | pwr_val[0]; 278 279 if (rf == 0) 280 regoffset = regoffset_a[index]; 281 else 282 regoffset = regoffset_b[index]; 283 284 phy_set_bb_reg(adapt, regoffset, bMaskDWord, write_val); 285 } 286} 287 288void rtl88eu_phy_rf6052_set_ofdm_txpower(struct adapter *adapt, 289 u8 *pwr_level_ofdm, 290 u8 *pwr_level_bw20, 291 u8 *pwr_level_bw40, u8 channel) 292{ 293 struct hal_data_8188e *hal_data = GET_HAL_DATA(adapt); 294 u32 write_val[2], powerbase0[2], powerbase1[2], pwrtrac_value; 295 u8 direction; 296 u8 index = 0; 297 298 getpowerbase88e(adapt, pwr_level_ofdm, pwr_level_bw20, pwr_level_bw40, 299 channel, &powerbase0[0], &powerbase1[0]); 300 301 rtl88eu_dm_txpower_track_adjust(&hal_data->odmpriv, 0, &direction, 302 &pwrtrac_value); 303 304 for (index = 0; index < 6; index++) { 305 get_rx_power_val_by_reg(adapt, channel, index, 306 &powerbase0[0], &powerbase1[0], 307 &write_val[0]); 308 309 if (direction == 1) { 310 write_val[0] += pwrtrac_value; 311 write_val[1] += pwrtrac_value; 312 } else if (direction == 2) { 313 write_val[0] -= pwrtrac_value; 314 write_val[1] -= pwrtrac_value; 315 } 316 write_ofdm_pwr_reg(adapt, index, &write_val[0]); 317 } 318} 319