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