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