1/****************************************************************************** 2 * 3 * Copyright(c) 2009-2012 Realtek Corporation. 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 * The full GNU General Public License is included in this distribution in the 19 * file called LICENSE. 20 * 21 * Contact Information: 22 * wlanfae <wlanfae@realtek.com> 23 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, 24 * Hsinchu 300, Taiwan. 25 * 26 * Larry Finger <Larry.Finger@lwfinger.net> 27 * 28 *****************************************************************************/ 29 30#include "../wifi.h" 31#include "reg.h" 32#include "def.h" 33#include "phy.h" 34#include "rf.h" 35#include "dm.h" 36 37 38static void _rtl92s_get_powerbase(struct ieee80211_hw *hw, u8 *p_pwrlevel, 39 u8 chnl, u32 *ofdmbase, u32 *mcsbase, 40 u8 *p_final_pwridx) 41{ 42 struct rtl_priv *rtlpriv = rtl_priv(hw); 43 struct rtl_phy *rtlphy = &(rtlpriv->phy); 44 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); 45 u32 pwrbase0, pwrbase1; 46 u8 legacy_pwrdiff = 0, ht20_pwrdiff = 0; 47 u8 i, pwrlevel[4]; 48 49 for (i = 0; i < 2; i++) 50 pwrlevel[i] = p_pwrlevel[i]; 51 52 /* We only care about the path A for legacy. */ 53 if (rtlefuse->eeprom_version < 2) { 54 pwrbase0 = pwrlevel[0] + (rtlefuse->legacy_httxpowerdiff & 0xf); 55 } else { 56 legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff 57 [RF90_PATH_A][chnl - 1]; 58 59 /* For legacy OFDM, tx pwr always > HT OFDM pwr. 60 * We do not care Path B 61 * legacy OFDM pwr diff. NO BB register 62 * to notify HW. */ 63 pwrbase0 = pwrlevel[0] + legacy_pwrdiff; 64 } 65 66 pwrbase0 = (pwrbase0 << 24) | (pwrbase0 << 16) | (pwrbase0 << 8) | 67 pwrbase0; 68 *ofdmbase = pwrbase0; 69 70 /* MCS rates */ 71 if (rtlefuse->eeprom_version >= 2) { 72 /* Check HT20 to HT40 diff */ 73 if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) { 74 for (i = 0; i < 2; i++) { 75 /* rf-A, rf-B */ 76 /* HT 20<->40 pwr diff */ 77 ht20_pwrdiff = rtlefuse->txpwr_ht20diff 78 [i][chnl - 1]; 79 80 if (ht20_pwrdiff < 8) /* 0~+7 */ 81 pwrlevel[i] += ht20_pwrdiff; 82 else /* index8-15=-8~-1 */ 83 pwrlevel[i] -= (16 - ht20_pwrdiff); 84 } 85 } 86 } 87 88 /* use index of rf-A */ 89 pwrbase1 = pwrlevel[0]; 90 pwrbase1 = (pwrbase1 << 24) | (pwrbase1 << 16) | (pwrbase1 << 8) | 91 pwrbase1; 92 *mcsbase = pwrbase1; 93 94 /* The following is for Antenna 95 * diff from Ant-B to Ant-A */ 96 p_final_pwridx[0] = pwrlevel[0]; 97 p_final_pwridx[1] = pwrlevel[1]; 98 99 switch (rtlefuse->eeprom_regulatory) { 100 case 3: 101 /* The following is for calculation 102 * of the power diff for Ant-B to Ant-A. */ 103 if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { 104 p_final_pwridx[0] += rtlefuse->pwrgroup_ht40 105 [RF90_PATH_A][ 106 chnl - 1]; 107 p_final_pwridx[1] += rtlefuse->pwrgroup_ht40 108 [RF90_PATH_B][ 109 chnl - 1]; 110 } else { 111 p_final_pwridx[0] += rtlefuse->pwrgroup_ht20 112 [RF90_PATH_A][ 113 chnl - 1]; 114 p_final_pwridx[1] += rtlefuse->pwrgroup_ht20 115 [RF90_PATH_B][ 116 chnl - 1]; 117 } 118 break; 119 default: 120 break; 121 } 122 123 if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { 124 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 125 "40MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n", 126 p_final_pwridx[0], p_final_pwridx[1]); 127 } else { 128 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 129 "20MHz finalpwr_idx (A / B) = 0x%x / 0x%x\n", 130 p_final_pwridx[0], p_final_pwridx[1]); 131 } 132} 133 134static void _rtl92s_set_antennadiff(struct ieee80211_hw *hw, 135 u8 *p_final_pwridx) 136{ 137 struct rtl_priv *rtlpriv = rtl_priv(hw); 138 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); 139 struct rtl_phy *rtlphy = &(rtlpriv->phy); 140 char ant_pwr_diff = 0; 141 u32 u4reg_val = 0; 142 143 if (rtlphy->rf_type == RF_2T2R) { 144 ant_pwr_diff = p_final_pwridx[1] - p_final_pwridx[0]; 145 146 /* range is from 7~-8, 147 * index = 0x0~0xf */ 148 if (ant_pwr_diff > 7) 149 ant_pwr_diff = 7; 150 if (ant_pwr_diff < -8) 151 ant_pwr_diff = -8; 152 153 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 154 "Antenna Diff from RF-B to RF-A = %d (0x%x)\n", 155 ant_pwr_diff, ant_pwr_diff & 0xf); 156 157 ant_pwr_diff &= 0xf; 158 } 159 160 /* Antenna TX power difference */ 161 rtlefuse->antenna_txpwdiff[2] = 0;/* RF-D, don't care */ 162 rtlefuse->antenna_txpwdiff[1] = 0;/* RF-C, don't care */ 163 rtlefuse->antenna_txpwdiff[0] = (u8)(ant_pwr_diff); /* RF-B */ 164 165 u4reg_val = rtlefuse->antenna_txpwdiff[2] << 8 | 166 rtlefuse->antenna_txpwdiff[1] << 4 | 167 rtlefuse->antenna_txpwdiff[0]; 168 169 rtl_set_bbreg(hw, RFPGA0_TXGAINSTAGE, (BXBTXAGC | BXCTXAGC | BXDTXAGC), 170 u4reg_val); 171 172 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "Write BCD-Diff(0x%x) = 0x%x\n", 173 RFPGA0_TXGAINSTAGE, u4reg_val); 174} 175 176static void _rtl92s_get_txpower_writeval_byregulatory(struct ieee80211_hw *hw, 177 u8 chnl, u8 index, 178 u32 pwrbase0, 179 u32 pwrbase1, 180 u32 *p_outwrite_val) 181{ 182 struct rtl_priv *rtlpriv = rtl_priv(hw); 183 struct rtl_phy *rtlphy = &(rtlpriv->phy); 184 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); 185 u8 i, chnlgroup, pwrdiff_limit[4]; 186 u32 writeval, customer_limit; 187 188 /* Index 0 & 1= legacy OFDM, 2-5=HT_MCS rate */ 189 switch (rtlefuse->eeprom_regulatory) { 190 case 0: 191 /* Realtek better performance increase power diff 192 * defined by Realtek for large power */ 193 chnlgroup = 0; 194 195 writeval = rtlphy->mcs_offset[chnlgroup][index] + 196 ((index < 2) ? pwrbase0 : pwrbase1); 197 198 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 199 "RTK better performance, writeval = 0x%x\n", writeval); 200 break; 201 case 1: 202 /* Realtek regulatory increase power diff defined 203 * by Realtek for regulatory */ 204 if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { 205 writeval = ((index < 2) ? pwrbase0 : pwrbase1); 206 207 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 208 "Realtek regulatory, 40MHz, writeval = 0x%x\n", 209 writeval); 210 } else { 211 if (rtlphy->pwrgroup_cnt == 1) 212 chnlgroup = 0; 213 214 if (rtlphy->pwrgroup_cnt >= 3) { 215 if (chnl <= 3) 216 chnlgroup = 0; 217 else if (chnl >= 4 && chnl <= 8) 218 chnlgroup = 1; 219 else if (chnl > 8) 220 chnlgroup = 2; 221 if (rtlphy->pwrgroup_cnt == 4) 222 chnlgroup++; 223 } 224 225 writeval = rtlphy->mcs_offset[chnlgroup][index] 226 + ((index < 2) ? 227 pwrbase0 : pwrbase1); 228 229 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 230 "Realtek regulatory, 20MHz, writeval = 0x%x\n", 231 writeval); 232 } 233 break; 234 case 2: 235 /* Better regulatory don't increase any power diff */ 236 writeval = ((index < 2) ? pwrbase0 : pwrbase1); 237 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 238 "Better regulatory, writeval = 0x%x\n", writeval); 239 break; 240 case 3: 241 /* Customer defined power diff. increase power diff 242 defined by customer. */ 243 chnlgroup = 0; 244 245 if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { 246 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 247 "customer's limit, 40MHz = 0x%x\n", 248 rtlefuse->pwrgroup_ht40 249 [RF90_PATH_A][chnl - 1]); 250 } else { 251 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 252 "customer's limit, 20MHz = 0x%x\n", 253 rtlefuse->pwrgroup_ht20 254 [RF90_PATH_A][chnl - 1]); 255 } 256 257 for (i = 0; i < 4; i++) { 258 pwrdiff_limit[i] = (u8)((rtlphy->mcs_offset 259 [chnlgroup][index] & (0x7f << (i * 8))) 260 >> (i * 8)); 261 262 if (rtlphy->current_chan_bw == 263 HT_CHANNEL_WIDTH_20_40) { 264 if (pwrdiff_limit[i] > 265 rtlefuse->pwrgroup_ht40 266 [RF90_PATH_A][chnl - 1]) { 267 pwrdiff_limit[i] = 268 rtlefuse->pwrgroup_ht40 269 [RF90_PATH_A][chnl - 1]; 270 } 271 } else { 272 if (pwrdiff_limit[i] > 273 rtlefuse->pwrgroup_ht20 274 [RF90_PATH_A][chnl - 1]) { 275 pwrdiff_limit[i] = 276 rtlefuse->pwrgroup_ht20 277 [RF90_PATH_A][chnl - 1]; 278 } 279 } 280 } 281 282 customer_limit = (pwrdiff_limit[3] << 24) | 283 (pwrdiff_limit[2] << 16) | 284 (pwrdiff_limit[1] << 8) | 285 (pwrdiff_limit[0]); 286 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 287 "Customer's limit = 0x%x\n", customer_limit); 288 289 writeval = customer_limit + ((index < 2) ? 290 pwrbase0 : pwrbase1); 291 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 292 "Customer, writeval = 0x%x\n", writeval); 293 break; 294 default: 295 chnlgroup = 0; 296 writeval = rtlphy->mcs_offset[chnlgroup][index] + 297 ((index < 2) ? pwrbase0 : pwrbase1); 298 RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, 299 "RTK better performance, writeval = 0x%x\n", writeval); 300 break; 301 } 302 303 if (rtlpriv->dm.dynamic_txhighpower_lvl == TX_HIGH_PWR_LEVEL_LEVEL1) 304 writeval = 0x10101010; 305 else if (rtlpriv->dm.dynamic_txhighpower_lvl == 306 TX_HIGH_PWR_LEVEL_LEVEL2) 307 writeval = 0x0; 308 309 *p_outwrite_val = writeval; 310 311} 312 313static void _rtl92s_write_ofdm_powerreg(struct ieee80211_hw *hw, 314 u8 index, u32 val) 315{ 316 struct rtl_priv *rtlpriv = rtl_priv(hw); 317 struct rtl_phy *rtlphy = &(rtlpriv->phy); 318 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); 319 u16 regoffset[6] = {0xe00, 0xe04, 0xe10, 0xe14, 0xe18, 0xe1c}; 320 u8 i, rfa_pwr[4]; 321 u8 rfa_lower_bound = 0, rfa_upper_bound = 0, rf_pwr_diff = 0; 322 u32 writeval = val; 323 324 /* If path A and Path B coexist, we must limit Path A tx power. 325 * Protect Path B pwr over or under flow. We need to calculate 326 * upper and lower bound of path A tx power. */ 327 if (rtlphy->rf_type == RF_2T2R) { 328 rf_pwr_diff = rtlefuse->antenna_txpwdiff[0]; 329 330 /* Diff=-8~-1 */ 331 if (rf_pwr_diff >= 8) { 332 /* Prevent underflow!! */ 333 rfa_lower_bound = 0x10 - rf_pwr_diff; 334 /* if (rf_pwr_diff >= 0) Diff = 0-7 */ 335 } else { 336 rfa_upper_bound = RF6052_MAX_TX_PWR - rf_pwr_diff; 337 } 338 } 339 340 for (i = 0; i < 4; i++) { 341 rfa_pwr[i] = (u8)((writeval & (0x7f << (i * 8))) >> (i * 8)); 342 if (rfa_pwr[i] > RF6052_MAX_TX_PWR) 343 rfa_pwr[i] = RF6052_MAX_TX_PWR; 344 345 /* If path A and Path B coexist, we must limit Path A tx power. 346 * Protect Path B pwr over or under flow. We need to calculate 347 * upper and lower bound of path A tx power. */ 348 if (rtlphy->rf_type == RF_2T2R) { 349 /* Diff=-8~-1 */ 350 if (rf_pwr_diff >= 8) { 351 /* Prevent underflow!! */ 352 if (rfa_pwr[i] < rfa_lower_bound) 353 rfa_pwr[i] = rfa_lower_bound; 354 /* Diff = 0-7 */ 355 } else if (rf_pwr_diff >= 1) { 356 /* Prevent overflow */ 357 if (rfa_pwr[i] > rfa_upper_bound) 358 rfa_pwr[i] = rfa_upper_bound; 359 } 360 } 361 362 } 363 364 writeval = (rfa_pwr[3] << 24) | (rfa_pwr[2] << 16) | (rfa_pwr[1] << 8) | 365 rfa_pwr[0]; 366 367 rtl_set_bbreg(hw, regoffset[index], 0x7f7f7f7f, writeval); 368} 369 370void rtl92s_phy_rf6052_set_ofdmtxpower(struct ieee80211_hw *hw, 371 u8 *p_pwrlevel, u8 chnl) 372{ 373 u32 writeval, pwrbase0, pwrbase1; 374 u8 index = 0; 375 u8 finalpwr_idx[4]; 376 377 _rtl92s_get_powerbase(hw, p_pwrlevel, chnl, &pwrbase0, &pwrbase1, 378 &finalpwr_idx[0]); 379 _rtl92s_set_antennadiff(hw, &finalpwr_idx[0]); 380 381 for (index = 0; index < 6; index++) { 382 _rtl92s_get_txpower_writeval_byregulatory(hw, chnl, index, 383 pwrbase0, pwrbase1, &writeval); 384 385 _rtl92s_write_ofdm_powerreg(hw, index, writeval); 386 } 387} 388 389void rtl92s_phy_rf6052_set_ccktxpower(struct ieee80211_hw *hw, u8 pwrlevel) 390{ 391 struct rtl_priv *rtlpriv = rtl_priv(hw); 392 struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); 393 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); 394 u32 txagc = 0; 395 bool dont_inc_cck_or_turboscanoff = false; 396 397 if (((rtlefuse->eeprom_version >= 2) && 398 (rtlefuse->txpwr_safetyflag == 1)) || 399 ((rtlefuse->eeprom_version >= 2) && 400 (rtlefuse->eeprom_regulatory != 0))) 401 dont_inc_cck_or_turboscanoff = true; 402 403 if (mac->act_scanning) { 404 txagc = 0x3f; 405 if (dont_inc_cck_or_turboscanoff) 406 txagc = pwrlevel; 407 } else { 408 txagc = pwrlevel; 409 410 if (rtlpriv->dm.dynamic_txhighpower_lvl == 411 TX_HIGH_PWR_LEVEL_LEVEL1) 412 txagc = 0x10; 413 else if (rtlpriv->dm.dynamic_txhighpower_lvl == 414 TX_HIGH_PWR_LEVEL_LEVEL2) 415 txagc = 0x0; 416 } 417 418 if (txagc > RF6052_MAX_TX_PWR) 419 txagc = RF6052_MAX_TX_PWR; 420 421 rtl_set_bbreg(hw, RTXAGC_CCK_MCS32, BTX_AGCRATECCK, txagc); 422 423} 424 425bool rtl92s_phy_rf6052_config(struct ieee80211_hw *hw) 426{ 427 struct rtl_priv *rtlpriv = rtl_priv(hw); 428 struct rtl_phy *rtlphy = &(rtlpriv->phy); 429 u32 u4reg_val = 0; 430 u8 rfpath; 431 bool rtstatus = true; 432 struct bb_reg_def *pphyreg; 433 434 /* Initialize RF */ 435 for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { 436 437 pphyreg = &rtlphy->phyreg_def[rfpath]; 438 439 /* Store original RFENV control type */ 440 switch (rfpath) { 441 case RF90_PATH_A: 442 case RF90_PATH_C: 443 u4reg_val = rtl92s_phy_query_bb_reg(hw, 444 pphyreg->rfintfs, 445 BRFSI_RFENV); 446 break; 447 case RF90_PATH_B: 448 case RF90_PATH_D: 449 u4reg_val = rtl92s_phy_query_bb_reg(hw, 450 pphyreg->rfintfs, 451 BRFSI_RFENV << 16); 452 break; 453 } 454 455 /* Set RF_ENV enable */ 456 rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfe, 457 BRFSI_RFENV << 16, 0x1); 458 459 /* Set RF_ENV output high */ 460 rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); 461 462 /* Set bit number of Address and Data for RF register */ 463 rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2, 464 B3WIRE_ADDRESSLENGTH, 0x0); 465 rtl92s_phy_set_bb_reg(hw, pphyreg->rfhssi_para2, 466 B3WIRE_DATALENGTH, 0x0); 467 468 /* Initialize RF fom connfiguration file */ 469 switch (rfpath) { 470 case RF90_PATH_A: 471 rtstatus = rtl92s_phy_config_rf(hw, 472 (enum radio_path)rfpath); 473 break; 474 case RF90_PATH_B: 475 rtstatus = rtl92s_phy_config_rf(hw, 476 (enum radio_path)rfpath); 477 break; 478 case RF90_PATH_C: 479 break; 480 case RF90_PATH_D: 481 break; 482 } 483 484 /* Restore RFENV control type */ 485 switch (rfpath) { 486 case RF90_PATH_A: 487 case RF90_PATH_C: 488 rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs, BRFSI_RFENV, 489 u4reg_val); 490 break; 491 case RF90_PATH_B: 492 case RF90_PATH_D: 493 rtl92s_phy_set_bb_reg(hw, pphyreg->rfintfs, 494 BRFSI_RFENV << 16, 495 u4reg_val); 496 break; 497 } 498 499 if (!rtstatus) { 500 pr_err("Radio[%d] Fail!!\n", rfpath); 501 goto fail; 502 } 503 504 } 505 506 return rtstatus; 507 508fail: 509 return rtstatus; 510} 511 512void rtl92s_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) 513{ 514 struct rtl_priv *rtlpriv = rtl_priv(hw); 515 struct rtl_phy *rtlphy = &(rtlpriv->phy); 516 517 switch (bandwidth) { 518 case HT_CHANNEL_WIDTH_20: 519 rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 520 0xfffff3ff) | 0x0400); 521 rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, 522 rtlphy->rfreg_chnlval[0]); 523 break; 524 case HT_CHANNEL_WIDTH_20_40: 525 rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & 526 0xfffff3ff)); 527 rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, 528 rtlphy->rfreg_chnlval[0]); 529 break; 530 default: 531 RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, 532 "unknown bandwidth: %#X\n", bandwidth); 533 break; 534 } 535} 536