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