linux/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/rf.c
<<
>>
Prefs
   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 * 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
  33static bool _rtl8821ae_phy_rf6052_config_parafile(struct ieee80211_hw *hw);
  34
  35void rtl8821ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth)
  36{
  37        struct rtl_priv *rtlpriv = rtl_priv(hw);
  38
  39        switch (bandwidth) {
  40        case HT_CHANNEL_WIDTH_20:
  41                rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, BIT(11)|BIT(10), 3);
  42                rtl_set_rfreg(hw, RF90_PATH_B, RF_CHNLBW, BIT(11)|BIT(10), 3);
  43                break;
  44        case HT_CHANNEL_WIDTH_20_40:
  45                rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, BIT(11)|BIT(10), 1);
  46                rtl_set_rfreg(hw, RF90_PATH_B, RF_CHNLBW, BIT(11)|BIT(10), 1);
  47                break;
  48        case HT_CHANNEL_WIDTH_80:
  49                rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, BIT(11)|BIT(10), 0);
  50                rtl_set_rfreg(hw, RF90_PATH_B, RF_CHNLBW, BIT(11)|BIT(10), 0);
  51                break;
  52        default:
  53                RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
  54                         "unknown bandwidth: %#X\n", bandwidth);
  55                break;
  56        }
  57}
  58
  59void rtl8821ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw,
  60                                          u8 *ppowerlevel)
  61{
  62        struct rtl_priv *rtlpriv = rtl_priv(hw);
  63        struct rtl_phy *rtlphy = &rtlpriv->phy;
  64        struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
  65        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
  66        u32 tx_agc[2] = {0, 0}, tmpval;
  67        bool turbo_scanoff = false;
  68        u8 idx1, idx2;
  69        u8 *ptr;
  70        u8 direction;
  71        u32 pwrtrac_value;
  72
  73        if (rtlefuse->eeprom_regulatory != 0)
  74                turbo_scanoff = true;
  75
  76        if (mac->act_scanning) {
  77                tx_agc[RF90_PATH_A] = 0x3f3f3f3f;
  78                tx_agc[RF90_PATH_B] = 0x3f3f3f3f;
  79
  80                if (turbo_scanoff) {
  81                        for (idx1 = RF90_PATH_A;
  82                                idx1 <= RF90_PATH_B;
  83                                idx1++) {
  84                                tx_agc[idx1] = ppowerlevel[idx1] |
  85                                    (ppowerlevel[idx1] << 8) |
  86                                    (ppowerlevel[idx1] << 16) |
  87                                    (ppowerlevel[idx1] << 24);
  88                        }
  89                }
  90        } else {
  91                for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
  92                        tx_agc[idx1] = ppowerlevel[idx1] |
  93                            (ppowerlevel[idx1] << 8) |
  94                            (ppowerlevel[idx1] << 16) |
  95                            (ppowerlevel[idx1] << 24);
  96                }
  97
  98                if (rtlefuse->eeprom_regulatory == 0) {
  99                        tmpval =
 100                            (rtlphy->mcs_txpwrlevel_origoffset[0][6]) +
 101                            (rtlphy->mcs_txpwrlevel_origoffset[0][7] <<
 102                             8);
 103                        tx_agc[RF90_PATH_A] += tmpval;
 104
 105                        tmpval = (rtlphy->mcs_txpwrlevel_origoffset[0][14]) +
 106                            (rtlphy->mcs_txpwrlevel_origoffset[0][15] <<
 107                             24);
 108                        tx_agc[RF90_PATH_B] += tmpval;
 109                }
 110        }
 111
 112        for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) {
 113                ptr = (u8 *)(&tx_agc[idx1]);
 114                for (idx2 = 0; idx2 < 4; idx2++) {
 115                        if (*ptr > RF6052_MAX_TX_PWR)
 116                                *ptr = RF6052_MAX_TX_PWR;
 117                        ptr++;
 118                }
 119        }
 120        rtl8821ae_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value);
 121        if (direction == 1) {
 122                tx_agc[0] += pwrtrac_value;
 123                tx_agc[1] += pwrtrac_value;
 124        } else if (direction == 2) {
 125                tx_agc[0] -= pwrtrac_value;
 126                tx_agc[1] -= pwrtrac_value;
 127        }
 128        tmpval = tx_agc[RF90_PATH_A];
 129        rtl_set_bbreg(hw, RTXAGC_A_CCK11_CCK1, MASKDWORD, tmpval);
 130
 131        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 132                "CCK PWR 1~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval,
 133                 RTXAGC_A_CCK11_CCK1);
 134
 135        tmpval = tx_agc[RF90_PATH_B];
 136        rtl_set_bbreg(hw, RTXAGC_B_CCK11_CCK1, MASKDWORD, tmpval);
 137
 138        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 139                "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval,
 140                 RTXAGC_B_CCK11_CCK1);
 141}
 142
 143static void rtl8821ae_phy_get_power_base(struct ieee80211_hw *hw,
 144                                         u8 *ppowerlevel_ofdm,
 145                                         u8 *ppowerlevel_bw20,
 146                                         u8 *ppowerlevel_bw40, u8 channel,
 147                                         u32 *ofdmbase, u32 *mcsbase)
 148{
 149        struct rtl_priv *rtlpriv = rtl_priv(hw);
 150        struct rtl_phy *rtlphy = &rtlpriv->phy;
 151        u32 powerbase0, powerbase1;
 152        u8 i, powerlevel[2];
 153
 154        for (i = 0; i < 2; i++) {
 155                powerbase0 = ppowerlevel_ofdm[i];
 156
 157                powerbase0 = (powerbase0 << 24) | (powerbase0 << 16) |
 158                    (powerbase0 << 8) | powerbase0;
 159                *(ofdmbase + i) = powerbase0;
 160                RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 161                        " [OFDM power base index rf(%c) = 0x%x]\n",
 162                         ((i == 0) ? 'A' : 'B'), *(ofdmbase + i));
 163        }
 164
 165        for (i = 0; i < 2; i++) {
 166                if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20)
 167                        powerlevel[i] = ppowerlevel_bw20[i];
 168                else
 169                        powerlevel[i] = ppowerlevel_bw40[i];
 170
 171                powerbase1 = powerlevel[i];
 172                powerbase1 = (powerbase1 << 24) |
 173                    (powerbase1 << 16) | (powerbase1 << 8) | powerbase1;
 174
 175                *(mcsbase + i) = powerbase1;
 176
 177                RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 178                        " [MCS power base index rf(%c) = 0x%x]\n",
 179                         ((i == 0) ? 'A' : 'B'), *(mcsbase + i));
 180        }
 181}
 182
 183static void get_txpower_writeval_by_regulatory(struct ieee80211_hw *hw,
 184                                               u8 channel, u8 index,
 185                                               u32 *powerbase0,
 186                                               u32 *powerbase1,
 187                                               u32 *p_outwriteval)
 188{
 189        struct rtl_priv *rtlpriv = rtl_priv(hw);
 190        struct rtl_phy *rtlphy = &rtlpriv->phy;
 191        struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
 192        u8 i, chnlgroup = 0, pwr_diff_limit[4], pwr_diff = 0, customer_pwr_diff;
 193        u32 writeval, customer_limit, rf;
 194
 195        for (rf = 0; rf < 2; rf++) {
 196                switch (rtlefuse->eeprom_regulatory) {
 197                case 0:
 198                        chnlgroup = 0;
 199
 200                        writeval =
 201                            rtlphy->mcs_txpwrlevel_origoffset[chnlgroup][index +
 202                                                        (rf ? 8 : 0)]
 203                            + ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
 204
 205                        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 206                                "RTK better performance, writeval(%c) = 0x%x\n",
 207                                 ((rf == 0) ? 'A' : 'B'), writeval);
 208                        break;
 209                case 1:
 210                        if (rtlphy->pwrgroup_cnt == 1) {
 211                                chnlgroup = 0;
 212                        } else {
 213                                if (channel < 3)
 214                                        chnlgroup = 0;
 215                                else if (channel < 6)
 216                                        chnlgroup = 1;
 217                                else if (channel < 9)
 218                                        chnlgroup = 2;
 219                                else if (channel < 12)
 220                                        chnlgroup = 3;
 221                                else if (channel < 14)
 222                                        chnlgroup = 4;
 223                                else if (channel == 14)
 224                                        chnlgroup = 5;
 225                        }
 226
 227                        writeval =
 228                            rtlphy->mcs_txpwrlevel_origoffset[chnlgroup]
 229                            [index + (rf ? 8 : 0)] + ((index < 2) ?
 230                                                      powerbase0[rf] :
 231                                                      powerbase1[rf]);
 232
 233                        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 234                                "Realtek regulatory, 20MHz, writeval(%c) = 0x%x\n",
 235                                 ((rf == 0) ? 'A' : 'B'), writeval);
 236
 237                        break;
 238                case 2:
 239                        writeval =
 240                            ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
 241
 242                        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 243                                "Better regulatory, writeval(%c) = 0x%x\n",
 244                                 ((rf == 0) ? 'A' : 'B'), writeval);
 245                        break;
 246                case 3:
 247                        chnlgroup = 0;
 248
 249                        if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) {
 250                                RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 251                                        "customer's limit, 40MHz rf(%c) = 0x%x\n",
 252                                         ((rf == 0) ? 'A' : 'B'),
 253                                         rtlefuse->pwrgroup_ht40[rf][channel -
 254                                                                     1]);
 255                        } else {
 256                                RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 257                                        "customer's limit, 20MHz rf(%c) = 0x%x\n",
 258                                         ((rf == 0) ? 'A' : 'B'),
 259                                         rtlefuse->pwrgroup_ht20[rf][channel -
 260                                                                     1]);
 261                        }
 262
 263                        if (index < 2)
 264                                pwr_diff = rtlefuse->txpwr_legacyhtdiff[rf][channel-1];
 265                        else if (rtlphy->current_chan_bw ==  HT_CHANNEL_WIDTH_20)
 266                                pwr_diff =
 267                                  rtlefuse->txpwr_ht20diff[rf][channel-1];
 268
 269                        if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40)
 270                                customer_pwr_diff =
 271                                  rtlefuse->pwrgroup_ht40[rf][channel-1];
 272                        else
 273                                customer_pwr_diff =
 274                                  rtlefuse->pwrgroup_ht20[rf][channel-1];
 275
 276                        if (pwr_diff > customer_pwr_diff)
 277                                pwr_diff = 0;
 278                        else
 279                                pwr_diff = customer_pwr_diff - pwr_diff;
 280
 281                        for (i = 0; i < 4; i++) {
 282                                pwr_diff_limit[i] =
 283                                    (u8)((rtlphy->mcs_txpwrlevel_origoffset
 284                                    [chnlgroup][index + (rf ? 8 : 0)] &
 285                                    (0x7f << (i * 8))) >> (i * 8));
 286
 287                                if (pwr_diff_limit[i] > pwr_diff)
 288                                        pwr_diff_limit[i] = pwr_diff;
 289                        }
 290
 291                        customer_limit = (pwr_diff_limit[3] << 24) |
 292                            (pwr_diff_limit[2] << 16) |
 293                            (pwr_diff_limit[1] << 8) | (pwr_diff_limit[0]);
 294
 295                        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 296                                "Customer's limit rf(%c) = 0x%x\n",
 297                                 ((rf == 0) ? 'A' : 'B'), customer_limit);
 298
 299                        writeval = customer_limit +
 300                            ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
 301
 302                        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 303                                "Customer, writeval rf(%c)= 0x%x\n",
 304                                 ((rf == 0) ? 'A' : 'B'), writeval);
 305                        break;
 306                default:
 307                        chnlgroup = 0;
 308                        writeval =
 309                            rtlphy->mcs_txpwrlevel_origoffset[chnlgroup]
 310                            [index + (rf ? 8 : 0)]
 311                            + ((index < 2) ? powerbase0[rf] : powerbase1[rf]);
 312
 313                        RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 314                                "RTK better performance, writeval rf(%c) = 0x%x\n",
 315                                 ((rf == 0) ? 'A' : 'B'), writeval);
 316                        break;
 317                }
 318
 319                if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1)
 320                        writeval = writeval - 0x06060606;
 321                else if (rtlpriv->dm.dynamic_txhighpower_lvl ==
 322                         TXHIGHPWRLEVEL_BT2)
 323                        writeval = writeval - 0x0c0c0c0c;
 324                *(p_outwriteval + rf) = writeval;
 325        }
 326}
 327
 328static void _rtl8821ae_write_ofdm_power_reg(struct ieee80211_hw *hw,
 329                                            u8 index, u32 *pvalue)
 330{
 331        struct rtl_priv *rtlpriv = rtl_priv(hw);
 332        u16 regoffset_a[6] = {
 333                RTXAGC_A_OFDM18_OFDM6, RTXAGC_A_OFDM54_OFDM24,
 334                RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04,
 335                RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12
 336        };
 337        u16 regoffset_b[6] = {
 338                RTXAGC_B_OFDM18_OFDM6, RTXAGC_B_OFDM54_OFDM24,
 339                RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04,
 340                RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12
 341        };
 342        u8 i, rf, pwr_val[4];
 343        u32 writeval;
 344        u16 regoffset;
 345
 346        for (rf = 0; rf < 2; rf++) {
 347                writeval = pvalue[rf];
 348                for (i = 0; i < 4; i++) {
 349                        pwr_val[i] = (u8)((writeval & (0x7f <<
 350                                                        (i * 8))) >> (i * 8));
 351
 352                        if (pwr_val[i] > RF6052_MAX_TX_PWR)
 353                                pwr_val[i] = RF6052_MAX_TX_PWR;
 354                }
 355                writeval = (pwr_val[3] << 24) | (pwr_val[2] << 16) |
 356                    (pwr_val[1] << 8) | pwr_val[0];
 357
 358                if (rf == 0)
 359                        regoffset = regoffset_a[index];
 360                else
 361                        regoffset = regoffset_b[index];
 362                rtl_set_bbreg(hw, regoffset, MASKDWORD, writeval);
 363
 364                RTPRINT(rtlpriv, FPHY, PHY_TXPWR,
 365                        "Set 0x%x = %08x\n", regoffset, writeval);
 366        }
 367}
 368
 369void rtl8821ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw,
 370                                           u8 *ppowerlevel_ofdm,
 371                                           u8 *ppowerlevel_bw20,
 372                                           u8 *ppowerlevel_bw40,
 373                                           u8 channel)
 374{
 375        u32 writeval[2], powerbase0[2], powerbase1[2];
 376        u8 index;
 377        u8 direction;
 378        u32 pwrtrac_value;
 379
 380        rtl8821ae_phy_get_power_base(hw, ppowerlevel_ofdm,
 381                                     ppowerlevel_bw20,
 382                                     ppowerlevel_bw40,
 383                                     channel,
 384                                     &powerbase0[0],
 385                                     &powerbase1[0]);
 386
 387        rtl8821ae_dm_txpower_track_adjust(hw, 1, &direction, &pwrtrac_value);
 388
 389        for (index = 0; index < 6; index++) {
 390                get_txpower_writeval_by_regulatory(hw, channel, index,
 391                                                   &powerbase0[0],
 392                                                   &powerbase1[0],
 393                                                   &writeval[0]);
 394                if (direction == 1) {
 395                        writeval[0] += pwrtrac_value;
 396                        writeval[1] += pwrtrac_value;
 397                } else if (direction == 2) {
 398                        writeval[0] -= pwrtrac_value;
 399                        writeval[1] -= pwrtrac_value;
 400                }
 401                _rtl8821ae_write_ofdm_power_reg(hw, index, &writeval[0]);
 402        }
 403}
 404
 405bool rtl8821ae_phy_rf6052_config(struct ieee80211_hw *hw)
 406{
 407        struct rtl_priv *rtlpriv = rtl_priv(hw);
 408        struct rtl_phy *rtlphy = &rtlpriv->phy;
 409
 410        if (rtlphy->rf_type == RF_1T1R)
 411                rtlphy->num_total_rfpath = 1;
 412        else
 413                rtlphy->num_total_rfpath = 2;
 414
 415        return _rtl8821ae_phy_rf6052_config_parafile(hw);
 416}
 417
 418static bool _rtl8821ae_phy_rf6052_config_parafile(struct ieee80211_hw *hw)
 419{
 420        struct rtl_priv *rtlpriv = rtl_priv(hw);
 421        struct rtl_phy *rtlphy = &rtlpriv->phy;
 422        struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 423        u8 rfpath;
 424        bool rtstatus = true;
 425
 426        for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) {
 427                switch (rfpath) {
 428                case RF90_PATH_A: {
 429                        if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
 430                                rtstatus =
 431                                  rtl8812ae_phy_config_rf_with_headerfile(hw,
 432                                                        (enum radio_path)rfpath);
 433                        else
 434                                rtstatus =
 435                                  rtl8821ae_phy_config_rf_with_headerfile(hw,
 436                                                        (enum radio_path)rfpath);
 437                        break;
 438                        }
 439                case RF90_PATH_B:
 440                        if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
 441                                rtstatus =
 442                                  rtl8812ae_phy_config_rf_with_headerfile(hw,
 443                                                        (enum radio_path)rfpath);
 444                        else
 445                                rtstatus =
 446                                  rtl8821ae_phy_config_rf_with_headerfile(hw,
 447                                                        (enum radio_path)rfpath);
 448                        break;
 449                case RF90_PATH_C:
 450                        break;
 451                case RF90_PATH_D:
 452                        break;
 453                }
 454
 455                if (!rtstatus) {
 456                        RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE,
 457                                 "Radio[%d] Fail!!", rfpath);
 458                        return false;
 459                }
 460        }
 461
 462        /*put arrays in dm.c*/
 463        RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "\n");
 464        return rtstatus;
 465}
 466