Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 142 kB image not shown  

Quelle  phy.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2009-2010  Realtek Corporation.*/

#include "../wifi.h"
#include "../pci.h"
#include "../ps.h"
#include "reg.h"
#include "def.h"
#include "phy.h"
#include "rf.h"
#include "dm.h"
#include "table.h"
#include "trx.h"
#include "../btcoexist/halbt_precomp.h"
#include "hw.h"
#include "../efuse.h"

#define READ_NEXT_PAIR(array_table, v1, v2, i) \
 do { \
  i += 2; \
  v1 = array_table[i]; \
  v2 = array_table[i+1]; \
 } while (0)

static u32 _rtl8821ae_phy_rf_serial_read(struct ieee80211_hw *hw,
      enum radio_path rfpath, u32 offset);
static void _rtl8821ae_phy_rf_serial_write(struct ieee80211_hw *hw,
        enum radio_path rfpath, u32 offset,
        u32 data);
static bool _rtl8821ae_phy_bb8821a_config_parafile(struct ieee80211_hw *hw);
/*static bool _rtl8812ae_phy_config_mac_with_headerfile(struct ieee80211_hw *hw);*/
static bool _rtl8821ae_phy_config_mac_with_headerfile(struct ieee80211_hw *hw);
static bool _rtl8821ae_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
           u8 configtype);
static bool _rtl8821ae_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
             u8 configtype);
static void phy_init_bb_rf_register_definition(struct ieee80211_hw *hw);

static long _rtl8821ae_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw,
         enum wireless_mode wirelessmode,
         u8 txpwridx);
static void rtl8821ae_phy_set_rf_on(struct ieee80211_hw *hw);
static void rtl8821ae_phy_set_io(struct ieee80211_hw *hw);

static void rtl8812ae_fixspur(struct ieee80211_hw *hw,
         enum ht_channel_width band_width, u8 channel)
{
 struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));

 /*C cut Item12 ADC FIFO CLOCK*/
 if (IS_VENDOR_8812A_C_CUT(rtlhal->version)) {
  if (band_width == HT_CHANNEL_WIDTH_20_40 && channel == 11)
   rtl_set_bbreg(hw, RRFMOD, 0xC00, 0x3);
   /* 0x8AC[11:10] = 2'b11*/
  else
   rtl_set_bbreg(hw, RRFMOD, 0xC00, 0x2);
   /* 0x8AC[11:10] = 2'b10*/

  /* <20120914, Kordan> A workaround to resolve
 * 2480Mhz spur by setting ADC clock as 160M. (Asked by Binson)
 */

  if (band_width == HT_CHANNEL_WIDTH_20 &&
      (channel == 13 || channel == 14)) {
   rtl_set_bbreg(hw, RRFMOD, 0x300, 0x3);
   /*0x8AC[9:8] = 2'b11*/
   rtl_set_bbreg(hw, RADC_BUF_CLK, BIT(30), 1);
   /* 0x8C4[30] = 1*/
  } else if (band_width == HT_CHANNEL_WIDTH_20_40 &&
      channel == 11) {
   rtl_set_bbreg(hw, RADC_BUF_CLK, BIT(30), 1);
   /*0x8C4[30] = 1*/
  } else if (band_width != HT_CHANNEL_WIDTH_80) {
   rtl_set_bbreg(hw, RRFMOD, 0x300, 0x2);
   /*0x8AC[9:8] = 2'b10*/
   rtl_set_bbreg(hw, RADC_BUF_CLK, BIT(30), 0);
   /*0x8C4[30] = 0*/
  }
 } else if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
  /* <20120914, Kordan> A workaround to resolve
 * 2480Mhz spur by setting ADC clock as 160M.
 */

  if (band_width == HT_CHANNEL_WIDTH_20 &&
      (channel == 13 || channel == 14))
   rtl_set_bbreg(hw, RRFMOD, 0x300, 0x3);
   /*0x8AC[9:8] = 11*/
  else if (channel  <= 14) /*2.4G only*/
   rtl_set_bbreg(hw, RRFMOD, 0x300, 0x2);
   /*0x8AC[9:8] = 10*/
 }
}

u32 rtl8821ae_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr,
          u32 bitmask)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 u32 returnvalue, originalvalue, bitshift;

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "regaddr(%#x), bitmask(%#x)\n",
  regaddr, bitmask);
 originalvalue = rtl_read_dword(rtlpriv, regaddr);
 bitshift = calculate_bit_shift(bitmask);
 returnvalue = (originalvalue & bitmask) >> bitshift;

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "BBR MASK=0x%x Addr[0x%x]=0x%x\n",
  bitmask, regaddr, originalvalue);
 return returnvalue;
}

void rtl8821ae_phy_set_bb_reg(struct ieee80211_hw *hw,
         u32 regaddr, u32 bitmask, u32 data)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 u32 originalvalue, bitshift;

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "regaddr(%#x), bitmask(%#x), data(%#x)\n",
  regaddr, bitmask, data);

 if (bitmask != MASKDWORD) {
  originalvalue = rtl_read_dword(rtlpriv, regaddr);
  bitshift = calculate_bit_shift(bitmask);
  data = ((originalvalue & (~bitmask)) |
   ((data << bitshift) & bitmask));
 }

 rtl_write_dword(rtlpriv, regaddr, data);

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "regaddr(%#x), bitmask(%#x), data(%#x)\n",
  regaddr, bitmask, data);
}

u32 rtl8821ae_phy_query_rf_reg(struct ieee80211_hw *hw,
          enum radio_path rfpath, u32 regaddr,
          u32 bitmask)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 u32 original_value, readback_value, bitshift;

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n",
  regaddr, rfpath, bitmask);

 spin_lock(&rtlpriv->locks.rf_lock);

 original_value = _rtl8821ae_phy_rf_serial_read(hw, rfpath, regaddr);
 bitshift = calculate_bit_shift(bitmask);
 readback_value = (original_value & bitmask) >> bitshift;

 spin_unlock(&rtlpriv->locks.rf_lock);

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "regaddr(%#x), rfpath(%#x), bitmask(%#x), original_value(%#x)\n",
  regaddr, rfpath, bitmask, original_value);

 return readback_value;
}

void rtl8821ae_phy_set_rf_reg(struct ieee80211_hw *hw,
      enum radio_path rfpath,
      u32 regaddr, u32 bitmask, u32 data)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 u32 original_value, bitshift;

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
  regaddr, bitmask, data, rfpath);

 spin_lock(&rtlpriv->locks.rf_lock);

 if (bitmask != RFREG_OFFSET_MASK) {
  original_value =
     _rtl8821ae_phy_rf_serial_read(hw, rfpath, regaddr);
  bitshift = calculate_bit_shift(bitmask);
  data = ((original_value & (~bitmask)) | (data << bitshift));
 }

 _rtl8821ae_phy_rf_serial_write(hw, rfpath, regaddr, data);

 spin_unlock(&rtlpriv->locks.rf_lock);

 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n",
   regaddr, bitmask, data, rfpath);
}

static u32 _rtl8821ae_phy_rf_serial_read(struct ieee80211_hw *hw,
      enum radio_path rfpath, u32 offset)
{
 struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 bool is_pi_mode = false;
 u32 retvalue = 0;

 /* 2009/06/17 MH We can not execute IO for power
save or other accident mode.*/

 if (RT_CANNOT_IO(hw)) {
  pr_err("return all one\n");
  return 0xFFFFFFFF;
 }
 /* <20120809, Kordan> CCA OFF(when entering),
asked by James to avoid reading the wrong value.
    <20120828, Kordan> Toggling CCA would affect RF 0x0, skip it!*/

 if (offset != 0x0 &&
     !((rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) ||
     (IS_VENDOR_8812A_C_CUT(rtlhal->version))))
  rtl_set_bbreg(hw, RCCAONSEC, 0x8, 1);
 offset &= 0xff;

 if (rfpath == RF90_PATH_A)
  is_pi_mode = (bool)rtl_get_bbreg(hw, 0xC00, 0x4);
 else if (rfpath == RF90_PATH_B)
  is_pi_mode = (bool)rtl_get_bbreg(hw, 0xE00, 0x4);

 rtl_set_bbreg(hw, RHSSIREAD_8821AE, 0xff, offset);

 if ((rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) ||
     (IS_VENDOR_8812A_C_CUT(rtlhal->version)))
  udelay(20);

 if (is_pi_mode) {
  if (rfpath == RF90_PATH_A)
   retvalue =
     rtl_get_bbreg(hw, RA_PIREAD_8821A, BLSSIREADBACKDATA);
  else if (rfpath == RF90_PATH_B)
   retvalue =
     rtl_get_bbreg(hw, RB_PIREAD_8821A, BLSSIREADBACKDATA);
 } else {
  if (rfpath == RF90_PATH_A)
   retvalue =
     rtl_get_bbreg(hw, RA_SIREAD_8821A, BLSSIREADBACKDATA);
  else if (rfpath == RF90_PATH_B)
   retvalue =
     rtl_get_bbreg(hw, RB_SIREAD_8821A, BLSSIREADBACKDATA);
 }

 /*<20120809, Kordan> CCA ON(when exiting),
 * asked by James to avoid reading the wrong value.
 *   <20120828, Kordan> Toggling CCA would affect RF 0x0, skip it!
 */

 if (offset != 0x0 &&
     !((rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) ||
     (IS_VENDOR_8812A_C_CUT(rtlhal->version))))
  rtl_set_bbreg(hw, RCCAONSEC, 0x8, 0);
 return retvalue;
}

static void _rtl8821ae_phy_rf_serial_write(struct ieee80211_hw *hw,
        enum radio_path rfpath, u32 offset,
        u32 data)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath];
 u32 data_and_addr;
 u32 newoffset;

 if (RT_CANNOT_IO(hw)) {
  pr_err("stop\n");
  return;
 }
 offset &= 0xff;
 newoffset = offset;
 data_and_addr = ((newoffset << 20) |
    (data & 0x000fffff)) & 0x0fffffff;
 rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr);
 rtl_dbg(rtlpriv, COMP_RF, DBG_TRACE,
  "RFW-%d Addr[0x%x]=0x%x\n",
  rfpath, pphyreg->rf3wire_offset, data_and_addr);
}

bool rtl8821ae_phy_mac_config(struct ieee80211_hw *hw)
{
 bool rtstatus = 0;

 rtstatus = _rtl8821ae_phy_config_mac_with_headerfile(hw);

 return rtstatus;
}

bool rtl8821ae_phy_bb_config(struct ieee80211_hw *hw)
{
 bool rtstatus = true;
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 u8 regval;
 u8 crystal_cap;

 phy_init_bb_rf_register_definition(hw);

 regval = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN);
 regval |= FEN_PCIEA;
 rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, regval);
 rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN,
         regval | FEN_BB_GLB_RSTN | FEN_BBRSTB);

 rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x7);
 rtl_write_byte(rtlpriv, REG_OPT_CTRL + 2, 0x7);

 rtstatus = _rtl8821ae_phy_bb8821a_config_parafile(hw);

 if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
  crystal_cap = rtlefuse->crystalcap & 0x3F;
  rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0x7FF80000,
         (crystal_cap | (crystal_cap << 6)));
 } else {
  crystal_cap = rtlefuse->crystalcap & 0x3F;
  rtl_set_bbreg(hw, REG_MAC_PHY_CTRL, 0xFFF000,
         (crystal_cap | (crystal_cap << 6)));
 }
 rtlphy->reg_837 = rtl_read_byte(rtlpriv, 0x837);

 return rtstatus;
}

bool rtl8821ae_phy_rf_config(struct ieee80211_hw *hw)
{
 return rtl8821ae_phy_rf6052_config(hw);
}

static void _rtl8812ae_phy_set_rfe_reg_24g(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 u8 tmp;

 switch (rtlhal->rfe_type) {
 case 3:
  rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337770);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337770);
  rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
  rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1);
  break;
 case 4:
  rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
  rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x001);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x001);
  break;
 case 5:
  rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x77);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
  tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3);
  rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp & ~0x1);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
  break;
 case 1:
  if (rtlpriv->btcoexist.bt_coexistence) {
   rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x777777);
   rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
          0x77777777);
   rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000);
   rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
   break;
  }
  fallthrough;
 case 0:
 case 2:
 default:
  rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77777777);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77777777);
  rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
  break;
 }
}

static void _rtl8812ae_phy_set_rfe_reg_5g(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 u8 tmp;

 switch (rtlhal->rfe_type) {
 case 0:
  rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337717);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337717);
  rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
  break;
 case 1:
  if (rtlpriv->btcoexist.bt_coexistence) {
   rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xffffff, 0x337717);
   rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
          0x77337717);
   rtl_set_bbreg(hw, RA_RFE_INV, 0x33f00000, 0x000);
   rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
  } else {
   rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD,
          0x77337717);
   rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD,
          0x77337717);
   rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x000);
   rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x000);
  }
  break;
 case 3:
  rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x54337717);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x54337717);
  rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
  rtl_set_bbreg(hw, 0x900, 0x00000303, 0x1);
  break;
 case 5:
  rtl_write_byte(rtlpriv, RA_RFE_PINMUX + 2, 0x33);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777);
  tmp = rtl_read_byte(rtlpriv, RA_RFE_INV + 3);
  rtl_write_byte(rtlpriv, RA_RFE_INV + 3, tmp | 0x1);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
  break;
 case 2:
 case 4:
 default:
  rtl_set_bbreg(hw, RA_RFE_PINMUX, BMASKDWORD, 0x77337777);
  rtl_set_bbreg(hw, RB_RFE_PINMUX, BMASKDWORD, 0x77337777);
  rtl_set_bbreg(hw, RA_RFE_INV, BMASKRFEINV, 0x010);
  rtl_set_bbreg(hw, RB_RFE_INV, BMASKRFEINV, 0x010);
  break;
 }
}

u32 phy_get_tx_swing_8812A(struct ieee80211_hw *hw, u8 band,
      u8 rf_path)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 struct rtl_dm *rtldm = rtl_dm(rtlpriv);
 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
 s8 reg_swing_2g = -1;/* 0xff; */
 s8 reg_swing_5g = -1;/* 0xff; */
 s8 swing_2g = -1 * reg_swing_2g;
 s8 swing_5g = -1 * reg_swing_5g;
 u32  out = 0x200;
 const s8 auto_temp = -1;

 rtl_dbg(rtlpriv, COMP_SCAN, DBG_LOUD,
  "===> PHY_GetTXBBSwing_8812A, bbSwing_2G: %d, bbSwing_5G: %d,autoload_failflag=%d.\n",
  (int)swing_2g, (int)swing_5g,
  (int)rtlefuse->autoload_failflag);

 if (rtlefuse->autoload_failflag) {
  if (band == BAND_ON_2_4G) {
   rtldm->swing_diff_2g = swing_2g;
   if (swing_2g == 0) {
    out = 0x200; /* 0 dB */
   } else if (swing_2g == -3) {
    out = 0x16A; /* -3 dB */
   } else if (swing_2g == -6) {
    out = 0x101; /* -6 dB */
   } else if (swing_2g == -9) {
    out = 0x0B6; /* -9 dB */
   } else {
    rtldm->swing_diff_2g = 0;
    out = 0x200;
   }
  } else if (band == BAND_ON_5G) {
   rtldm->swing_diff_5g = swing_5g;
   if (swing_5g == 0) {
    out = 0x200; /* 0 dB */
   } else if (swing_5g == -3) {
    out = 0x16A; /* -3 dB */
   } else if (swing_5g == -6) {
    out = 0x101; /* -6 dB */
   } else if (swing_5g == -9) {
    out = 0x0B6; /* -9 dB */
   } else {
    if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) {
     rtldm->swing_diff_5g = -3;
     out = 0x16A;
    } else {
     rtldm->swing_diff_5g = 0;
     out = 0x200;
    }
   }
  } else {
   rtldm->swing_diff_2g = -3;
   rtldm->swing_diff_5g = -3;
   out = 0x16A; /* -3 dB */
  }
 } else {
  u32 swing = 0, swing_a = 0, swing_b = 0;

  if (band == BAND_ON_2_4G) {
   if (reg_swing_2g == auto_temp) {
    efuse_shadow_read(hw, 1, 0xC6, (u32 *)&swing);
    swing = (swing == 0xFF) ? 0x00 : swing;
   } else if (swing_2g ==  0) {
    swing = 0x00; /* 0 dB */
   } else if (swing_2g == -3) {
    swing = 0x05; /* -3 dB */
   } else if (swing_2g == -6) {
    swing = 0x0A; /* -6 dB */
   } else if (swing_2g == -9) {
    swing = 0xFF; /* -9 dB */
   } else {
    swing = 0x00;
   }
  } else {
   if (reg_swing_5g == auto_temp) {
    efuse_shadow_read(hw, 1, 0xC7, (u32 *)&swing);
    swing = (swing == 0xFF) ? 0x00 : swing;
   } else if (swing_5g ==  0) {
    swing = 0x00; /* 0 dB */
   } else if (swing_5g == -3) {
    swing = 0x05; /* -3 dB */
   } else if (swing_5g == -6) {
    swing = 0x0A; /* -6 dB */
   } else if (swing_5g == -9) {
    swing = 0xFF; /* -9 dB */
   } else {
    swing = 0x00;
   }
  }

  swing_a = (swing & 0x3) >> 0; /* 0xC6/C7[1:0] */
  swing_b = (swing & 0xC) >> 2; /* 0xC6/C7[3:2] */
  rtl_dbg(rtlpriv, COMP_SCAN, DBG_LOUD,
   "===> PHY_GetTXBBSwing_8812A, swingA: 0x%X, swingB: 0x%X\n",
   swing_a, swing_b);

  /* 3 Path-A */
  if (swing_a == 0x0) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = 0;
   else
    rtldm->swing_diff_5g = 0;
   out = 0x200; /* 0 dB */
  } else if (swing_a == 0x1) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = -3;
   else
    rtldm->swing_diff_5g = -3;
   out = 0x16A; /* -3 dB */
  } else if (swing_a == 0x2) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = -6;
   else
    rtldm->swing_diff_5g = -6;
   out = 0x101; /* -6 dB */
  } else if (swing_a == 0x3) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = -9;
   else
    rtldm->swing_diff_5g = -9;
   out = 0x0B6; /* -9 dB */
  }
  /* 3 Path-B */
  if (swing_b == 0x0) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = 0;
   else
    rtldm->swing_diff_5g = 0;
   out = 0x200; /* 0 dB */
  } else if (swing_b == 0x1) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = -3;
   else
    rtldm->swing_diff_5g = -3;
   out = 0x16A; /* -3 dB */
  } else if (swing_b == 0x2) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = -6;
   else
    rtldm->swing_diff_5g = -6;
   out = 0x101; /* -6 dB */
  } else if (swing_b == 0x3) {
   if (band == BAND_ON_2_4G)
    rtldm->swing_diff_2g = -9;
   else
    rtldm->swing_diff_5g = -9;
   out = 0x0B6; /* -9 dB */
  }
 }

 rtl_dbg(rtlpriv, COMP_SCAN, DBG_LOUD,
  "<=== PHY_GetTXBBSwing_8812A, out = 0x%X\n", out);
 return out;
}

void rtl8821ae_phy_switch_wirelessband(struct ieee80211_hw *hw, u8 band)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
 struct rtl_dm *rtldm = rtl_dm(rtlpriv);
 u8 current_band = rtlhal->current_bandtype;
 s8 bb_diff_between_band;

 rtl8821ae_phy_query_bb_reg(hw, RTXPATH, 0xf0);
 rtl8821ae_phy_query_bb_reg(hw, RCCK_RX, 0x0f000000);
 rtlhal->current_bandtype = (enum band_type) band;
 /* reconfig BB/RF according to wireless mode */
 if (rtlhal->current_bandtype == BAND_ON_2_4G) {
  /* BB & RF Config */
  rtl_set_bbreg(hw, ROFDMCCKEN, BOFDMEN|BCCKEN, 0x03);

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) {
   /* 0xCB0[15:12] = 0x7 (LNA_On)*/
   rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xF000, 0x7);
   /* 0xCB0[7:4] = 0x7 (PAPE_A)*/
   rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xF0, 0x7);
  }

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
   /*0x834[1:0] = 0x1*/
   rtl_set_bbreg(hw, 0x834, 0x3, 0x1);
  }

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) {
   /* 0xC1C[11:8] = 0 */
   rtl_set_bbreg(hw, RA_TXSCALE, 0xF00, 0);
  } else {
   /* 0x82C[1:0] = 2b'00 */
   rtl_set_bbreg(hw, 0x82c, 0x3, 0);
  }

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
   _rtl8812ae_phy_set_rfe_reg_24g(hw);

  rtl_set_bbreg(hw, RTXPATH, 0xf0, 0x1);
  rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0x1);

  rtl_write_byte(rtlpriv, REG_CCK_CHECK, 0x0);
 } else {/* 5G band */
  u16 count, reg_41a;

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) {
   /*0xCB0[15:12] = 0x5 (LNA_On)*/
   rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xF000, 0x5);
   /*0xCB0[7:4] = 0x4 (PAPE_A)*/
   rtl_set_bbreg(hw, RA_RFE_PINMUX, 0xF0, 0x4);
  }
  /*CCK_CHECK_en*/
  rtl_write_byte(rtlpriv, REG_CCK_CHECK, 0x80);

  count = 0;
  reg_41a = rtl_read_word(rtlpriv, REG_TXPKT_EMPTY);
  rtl_dbg(rtlpriv, COMP_SCAN, DBG_LOUD,
   "Reg41A value %d\n", reg_41a);
  reg_41a &= 0x30;
  while ((reg_41a != 0x30) && (count < 50)) {
   udelay(50);
   rtl_dbg(rtlpriv, COMP_SCAN, DBG_LOUD, "Delay 50us\n");

   reg_41a = rtl_read_word(rtlpriv, REG_TXPKT_EMPTY);
   reg_41a &= 0x30;
   count++;
   rtl_dbg(rtlpriv, COMP_SCAN, DBG_LOUD,
    "Reg41A value %d\n", reg_41a);
  }
  if (count != 0)
   rtl_dbg(rtlpriv, COMP_MLME, DBG_LOUD,
    "PHY_SwitchWirelessBand8812(): Switch to 5G Band. Count = %d reg41A=0x%x\n",
    count, reg_41a);

  /* 2012/02/01, Sinda add registry to switch workaround
without long-run verification for scan issue. */

  rtl_set_bbreg(hw, ROFDMCCKEN, BOFDMEN|BCCKEN, 0x03);

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
   /*0x834[1:0] = 0x2*/
   rtl_set_bbreg(hw, 0x834, 0x3, 0x2);
  }

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) {
   /* AGC table select */
   /* 0xC1C[11:8] = 1*/
   rtl_set_bbreg(hw, RA_TXSCALE, 0xF00, 1);
  } else
   /* 0x82C[1:0] = 2'b00 */
   rtl_set_bbreg(hw, 0x82c, 0x3, 1);

  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)
   _rtl8812ae_phy_set_rfe_reg_5g(hw);

  rtl_set_bbreg(hw, RTXPATH, 0xf0, 0);
  rtl_set_bbreg(hw, RCCK_RX, 0x0f000000, 0xf);

  rtl_dbg(rtlpriv, COMP_SCAN, DBG_LOUD,
   "==>PHY_SwitchWirelessBand8812() BAND_ON_5G settings OFDM index 0x%x\n",
   rtlpriv->dm.ofdm_index[RF90_PATH_A]);
 }

 if ((rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) ||
     (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE)) {
  /* 0xC1C[31:21] */
  rtl_set_bbreg(hw, RA_TXSCALE, 0xFFE00000,
         phy_get_tx_swing_8812A(hw, band, RF90_PATH_A));
  /* 0xE1C[31:21] */
  rtl_set_bbreg(hw, RB_TXSCALE, 0xFFE00000,
         phy_get_tx_swing_8812A(hw, band, RF90_PATH_B));

  /* <20121005, Kordan> When TxPowerTrack is ON,
 * we should take care of the change of BB swing.
 *   That is, reset all info to trigger Tx power tracking.
 */

  if (band != current_band) {
   bb_diff_between_band =
    (rtldm->swing_diff_2g - rtldm->swing_diff_5g);
   bb_diff_between_band = (band == BAND_ON_2_4G) ?
      bb_diff_between_band :
      (-1 * bb_diff_between_band);
   rtldm->default_ofdm_index += bb_diff_between_band * 2;
  }
  rtl8821ae_dm_clear_txpower_tracking_state(hw);
 }

 rtl_dbg(rtlpriv, COMP_SCAN, DBG_TRACE,
  "<==%s():Switch Band OK.\n", __func__);
 return;
}

static bool _rtl8821ae_check_positive(struct ieee80211_hw *hw,
          const u32 condition1,
          const u32 condition2)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
 u32 cut_ver = ((rtlhal->version & CHIP_VER_RTL_MASK)
     >> CHIP_VER_RTL_SHIFT);
 u32 intf = (rtlhal->interface == INTF_USB ? BIT(1) : BIT(0));

 u8  board_type = ((rtlhal->board_type & BIT(4)) >> 4) << 0 | /* _GLNA */
    ((rtlhal->board_type & BIT(3)) >> 3) << 1 | /* _GPA  */
    ((rtlhal->board_type & BIT(7)) >> 7) << 2 | /* _ALNA */
    ((rtlhal->board_type & BIT(6)) >> 6) << 3 | /* _APA  */
    ((rtlhal->board_type & BIT(2)) >> 2) << 4;  /* _BT   */

 u32 cond1 = condition1, cond2 = condition2;
 u32 driver1 = cut_ver << 24 | /* CUT ver */
        0 << 20 |   /* interface 2/2 */
        0x04 << 16 |  /* platform */
        rtlhal->package_type << 12 |
        intf << 8 |   /* interface 1/2 */
        board_type;

 u32 driver2 = rtlhal->type_glna <<  0 |
        rtlhal->type_gpa  <<  8 |
        rtlhal->type_alna << 16 |
        rtlhal->type_apa  << 24;

 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  "===> [8812A] CheckPositive (cond1, cond2) = (0x%X 0x%X)\n",
  cond1, cond2);
 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  "===> [8812A] CheckPositive (driver1, driver2) = (0x%X 0x%X)\n",
  driver1, driver2);

 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  " (Platform, Interface) = (0x%X, 0x%X)\n", 0x04, intf);
 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  " (Board, Package) = (0x%X, 0x%X)\n",
  rtlhal->board_type, rtlhal->package_type);

 /*============== Value Defined Check ===============*/
 /*QFN Type [15:12] and Cut Version [27:24] need to do value check*/

 if (((cond1 & 0x0000F000) != 0) && ((cond1 & 0x0000F000) !=
  (driver1 & 0x0000F000)))
  return false;
 if (((cond1 & 0x0F000000) != 0) && ((cond1 & 0x0F000000) !=
  (driver1 & 0x0F000000)))
  return false;

 /*=============== Bit Defined Check ================*/
 /* We don't care [31:28] */

 cond1   &= 0x00FF0FFF;
 driver1 &= 0x00FF0FFF;

 if ((cond1 & driver1) == cond1) {
  u32 mask = 0;

  if ((cond1 & 0x0F) == 0) /* BoardType is DONTCARE*/
   return true;

  if ((cond1 & BIT(0)) != 0) /*GLNA*/
   mask |= 0x000000FF;
  if ((cond1 & BIT(1)) != 0) /*GPA*/
   mask |= 0x0000FF00;
  if ((cond1 & BIT(2)) != 0) /*ALNA*/
   mask |= 0x00FF0000;
  if ((cond1 & BIT(3)) != 0) /*APA*/
   mask |= 0xFF000000;

  /* BoardType of each RF path is matched*/
  if ((cond2 & mask) == (driver2 & mask))
   return true;
  else
   return false;
 } else
  return false;
}

static bool _rtl8821ae_check_condition(struct ieee80211_hw *hw,
           const u32 condition)
{
 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
 u32 _board = rtlefuse->board_type; /*need efuse define*/
 u32 _interface = 0x01; /* ODM_ITRF_PCIE */
 u32 _platform = 0x08;/* ODM_WIN */
 u32 cond = condition;

 if (condition == 0xCDCDCDCD)
  return true;

 cond = condition & 0xFF;
 if ((_board != cond) && cond != 0xFF)
  return false;

 cond = condition & 0xFF00;
 cond = cond >> 8;
 if ((_interface & cond) == 0 && cond != 0x07)
  return false;

 cond = condition & 0xFF0000;
 cond = cond >> 16;
 if ((_platform & cond) == 0 && cond != 0x0F)
  return false;
 return true;
}

static void _rtl8821ae_config_rf_reg(struct ieee80211_hw *hw,
         u32 addr, u32 data,
         enum radio_path rfpath, u32 regaddr)
{
 if (addr == 0xfe || addr == 0xffe) {
  /* In order not to disturb BT music when
 * wifi init.(1ant NIC only)
 */

  mdelay(50);
 } else {
  rtl_set_rfreg(hw, rfpath, regaddr, RFREG_OFFSET_MASK, data);
  udelay(1);
 }
}

static void _rtl8821ae_config_rf_radio_a(struct ieee80211_hw *hw,
      u32 addr, u32 data)
{
 u32 content = 0x1000; /*RF Content: radio_a_txt*/
 u32 maskforphyset = (u32)(content & 0xE000);

 _rtl8821ae_config_rf_reg(hw, addr, data,
     RF90_PATH_A, addr | maskforphyset);
}

static void _rtl8821ae_config_rf_radio_b(struct ieee80211_hw *hw,
      u32 addr, u32 data)
{
 u32 content = 0x1001; /*RF Content: radio_b_txt*/
 u32 maskforphyset = (u32)(content & 0xE000);

 _rtl8821ae_config_rf_reg(hw, addr, data,
     RF90_PATH_B, addr | maskforphyset);
}

static void _rtl8821ae_config_bb_reg(struct ieee80211_hw *hw,
         u32 addr, u32 data)
{
 if (addr == 0xfe)
  mdelay(50);
 else if (addr == 0xfd)
  mdelay(5);
 else if (addr == 0xfc)
  mdelay(1);
 else if (addr == 0xfb)
  udelay(50);
 else if (addr == 0xfa)
  udelay(5);
 else if (addr == 0xf9)
  udelay(1);
 else
  rtl_set_bbreg(hw, addr, MASKDWORD, data);

 udelay(1);
}

static void _rtl8821ae_phy_init_tx_power_by_rate(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 band, rfpath, txnum, rate_section;

 for (band = BAND_ON_2_4G; band <= BAND_ON_5G; ++band)
  for (rfpath = 0; rfpath < TX_PWR_BY_RATE_NUM_RF; ++rfpath)
   for (txnum = 0; txnum < TX_PWR_BY_RATE_NUM_RF; ++txnum)
    for (rate_section = 0;
         rate_section < TX_PWR_BY_RATE_NUM_SECTION;
         ++rate_section)
     rtlphy->tx_power_by_rate_offset[band]
         [rfpath][txnum][rate_section] = 0;
}

static void _rtl8821ae_phy_set_txpower_by_rate_base(struct ieee80211_hw *hw,
       u8 band, u8 path,
       u8 rate_section,
       u8 txnum, u8 value)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;

 if (path > RF90_PATH_D) {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
   "Invalid Rf Path %d in phy_SetTxPowerByRatBase()\n", path);
  return;
 }

 if (band == BAND_ON_2_4G) {
  switch (rate_section) {
  case CCK:
   rtlphy->txpwr_by_rate_base_24g[path][txnum][0] = value;
   break;
  case OFDM:
   rtlphy->txpwr_by_rate_base_24g[path][txnum][1] = value;
   break;
  case HT_MCS0_MCS7:
   rtlphy->txpwr_by_rate_base_24g[path][txnum][2] = value;
   break;
  case HT_MCS8_MCS15:
   rtlphy->txpwr_by_rate_base_24g[path][txnum][3] = value;
   break;
  case VHT_1SSMCS0_1SSMCS9:
   rtlphy->txpwr_by_rate_base_24g[path][txnum][4] = value;
   break;
  case VHT_2SSMCS0_2SSMCS9:
   rtlphy->txpwr_by_rate_base_24g[path][txnum][5] = value;
   break;
  default:
   rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
    "Invalid RateSection %d in Band 2.4G,Rf Path %d, %dTx in PHY_SetTxPowerByRateBase()\n",
    rate_section, path, txnum);
   break;
  }
 } else if (band == BAND_ON_5G) {
  switch (rate_section) {
  case OFDM:
   rtlphy->txpwr_by_rate_base_5g[path][txnum][0] = value;
   break;
  case HT_MCS0_MCS7:
   rtlphy->txpwr_by_rate_base_5g[path][txnum][1] = value;
   break;
  case HT_MCS8_MCS15:
   rtlphy->txpwr_by_rate_base_5g[path][txnum][2] = value;
   break;
  case VHT_1SSMCS0_1SSMCS9:
   rtlphy->txpwr_by_rate_base_5g[path][txnum][3] = value;
   break;
  case VHT_2SSMCS0_2SSMCS9:
   rtlphy->txpwr_by_rate_base_5g[path][txnum][4] = value;
   break;
  default:
   rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
    "Invalid RateSection %d in Band 5G, Rf Path %d, %dTx in PHY_SetTxPowerByRateBase()\n",
    rate_section, path, txnum);
   break;
  }
 } else {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
   "Invalid Band %d in PHY_SetTxPowerByRateBase()\n", band);
 }
}

static u8 _rtl8821ae_phy_get_txpower_by_rate_base(struct ieee80211_hw *hw,
        u8 band, u8 path,
        u8 txnum, u8 rate_section)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 value = 0;

 if (path > RF90_PATH_D) {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
   "Invalid Rf Path %d in PHY_GetTxPowerByRateBase()\n",
   path);
  return 0;
 }

 if (band == BAND_ON_2_4G) {
  switch (rate_section) {
  case CCK:
   value = rtlphy->txpwr_by_rate_base_24g[path][txnum][0];
   break;
  case OFDM:
   value = rtlphy->txpwr_by_rate_base_24g[path][txnum][1];
   break;
  case HT_MCS0_MCS7:
   value = rtlphy->txpwr_by_rate_base_24g[path][txnum][2];
   break;
  case HT_MCS8_MCS15:
   value = rtlphy->txpwr_by_rate_base_24g[path][txnum][3];
   break;
  case VHT_1SSMCS0_1SSMCS9:
   value = rtlphy->txpwr_by_rate_base_24g[path][txnum][4];
   break;
  case VHT_2SSMCS0_2SSMCS9:
   value = rtlphy->txpwr_by_rate_base_24g[path][txnum][5];
   break;
  default:
   rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
    "Invalid RateSection %d in Band 2.4G, Rf Path %d, %dTx in PHY_GetTxPowerByRateBase()\n",
    rate_section, path, txnum);
   break;
  }
 } else if (band == BAND_ON_5G) {
  switch (rate_section) {
  case OFDM:
   value = rtlphy->txpwr_by_rate_base_5g[path][txnum][0];
   break;
  case HT_MCS0_MCS7:
   value = rtlphy->txpwr_by_rate_base_5g[path][txnum][1];
   break;
  case HT_MCS8_MCS15:
   value = rtlphy->txpwr_by_rate_base_5g[path][txnum][2];
   break;
  case VHT_1SSMCS0_1SSMCS9:
   value = rtlphy->txpwr_by_rate_base_5g[path][txnum][3];
   break;
  case VHT_2SSMCS0_2SSMCS9:
   value = rtlphy->txpwr_by_rate_base_5g[path][txnum][4];
   break;
  default:
   rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
    "Invalid RateSection %d in Band 5G, Rf Path %d, %dTx in PHY_GetTxPowerByRateBase()\n",
    rate_section, path, txnum);
   break;
  }
 } else {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
   "Invalid Band %d in PHY_GetTxPowerByRateBase()\n", band);
 }

 return value;
}

static void _rtl8821ae_phy_store_txpower_by_rate_base(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u16 rawvalue = 0;
 u8 base = 0, path = 0;

 for (path = RF90_PATH_A; path <= RF90_PATH_B; ++path) {
  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][path][RF_1TX][0] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_2_4G, path, CCK, RF_1TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][path][RF_1TX][2] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_2_4G, path, OFDM, RF_1TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][path][RF_1TX][4] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_2_4G, path, HT_MCS0_MCS7, RF_1TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][path][RF_2TX][6] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_2_4G, path, HT_MCS8_MCS15, RF_2TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][path][RF_1TX][8] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_2_4G, path, VHT_1SSMCS0_1SSMCS9, RF_1TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][path][RF_2TX][11] >> 8) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_2_4G, path, VHT_2SSMCS0_2SSMCS9, RF_2TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_5G][path][RF_1TX][2] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_5G, path, OFDM, RF_1TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_5G][path][RF_1TX][4] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_5G, path, HT_MCS0_MCS7, RF_1TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_5G][path][RF_2TX][6] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_5G, path, HT_MCS8_MCS15, RF_2TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_5G][path][RF_1TX][8] >> 24) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_5G, path, VHT_1SSMCS0_1SSMCS9, RF_1TX, base);

  rawvalue = (u16)(rtlphy->tx_power_by_rate_offset[BAND_ON_5G][path][RF_2TX][11] >> 8) &&nbsp;0xFF;
  base = (rawvalue >> 4) * 10 + (rawvalue & 0xF);
  _rtl8821ae_phy_set_txpower_by_rate_base(hw, BAND_ON_5G, path, VHT_2SSMCS0_2SSMCS9, RF_2TX, base);
 }
}

static void _phy_convert_txpower_dbm_to_relative_value(u32 *data, u8 start,
      u8 end, u8 base_val)
{
 int i;
 u8 temp_value = 0;
 u32 temp_data = 0;

 for (i = 3; i >= 0; --i) {
  if (i >= start && i <= end) {
   /* Get the exact value */
   temp_value = (u8)(*data >> (i * 8)) & 0xF;
   temp_value += ((u8)((*data >> (i * 8 + 4)) & 0xF)) * 10;

   /* Change the value to a relative value */
   temp_value = (temp_value > base_val) ? temp_value -
     base_val : base_val - temp_value;
  } else {
   temp_value = (u8)(*data >> (i * 8)) & 0xFF;
  }
  temp_data <<= 8;
  temp_data |= temp_value;
 }
 *data = temp_data;
}

static void _rtl8812ae_phy_cross_reference_ht_and_vht_txpower_limit(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 regulation, bw, channel, rate_section;
 s8 temp_pwrlmt = 0;

 for (regulation = 0; regulation < MAX_REGULATION_NUM; ++regulation) {
  for (bw = 0; bw < MAX_5G_BANDWIDTH_NUM; ++bw) {
   for (channel = 0; channel < CHANNEL_MAX_NUMBER_5G; ++channel) {
    for (rate_section = 0; rate_section < MAX_RATE_SECTION_NUM; ++rate_section) {
     temp_pwrlmt = rtlphy->txpwr_limit_5g[regulation]
      [bw][rate_section][channel][RF90_PATH_A];
     if (temp_pwrlmt == MAX_POWER_INDEX) {
      if (bw == 0 || bw == 1) { /*5G 20M 40M VHT and HT can cross reference*/
       rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
        "No power limit table of the specified band %d, bandwidth %d, ratesection %d, channel %d, rf path %d\n",
        1, bw, rate_section, channel, RF90_PATH_A);
       if (rate_section == 2) {
        rtlphy->txpwr_limit_5g[regulation][bw][2][channel][RF90_PATH_A] =
         rtlphy->txpwr_limit_5g[regulation][bw][4][channel][RF90_PATH_A];
       } else if (rate_section == 4) {
        rtlphy->txpwr_limit_5g[regulation][bw][4][channel][RF90_PATH_A] =
         rtlphy->txpwr_limit_5g[regulation][bw][2][channel][RF90_PATH_A];
       } else if (rate_section == 3) {
        rtlphy->txpwr_limit_5g[regulation][bw][3][channel][RF90_PATH_A] =
         rtlphy->txpwr_limit_5g[regulation][bw][5][channel][RF90_PATH_A];
       } else if (rate_section == 5) {
        rtlphy->txpwr_limit_5g[regulation][bw][5][channel][RF90_PATH_A] =
         rtlphy->txpwr_limit_5g[regulation][bw][3][channel][RF90_PATH_A];
       }

       rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
        "use other value %d\n",
        temp_pwrlmt);
      }
     }
    }
   }
  }
 }
}

static u8 _rtl8812ae_phy_get_txpower_by_rate_base_index(struct ieee80211_hw *hw,
         enum band_type band, u8 rate)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 u8 index = 0;
 if (band == BAND_ON_2_4G) {
  switch (rate) {
  case MGN_1M:
  case MGN_2M:
  case MGN_5_5M:
  case MGN_11M:
   index = 0;
   break;

  case MGN_6M:
  case MGN_9M:
  case MGN_12M:
  case MGN_18M:
  case MGN_24M:
  case MGN_36M:
  case MGN_48M:
  case MGN_54M:
   index = 1;
   break;

  case MGN_MCS0:
  case MGN_MCS1:
  case MGN_MCS2:
  case MGN_MCS3:
  case MGN_MCS4:
  case MGN_MCS5:
  case MGN_MCS6:
  case MGN_MCS7:
   index = 2;
   break;

  case MGN_MCS8:
  case MGN_MCS9:
  case MGN_MCS10:
  case MGN_MCS11:
  case MGN_MCS12:
  case MGN_MCS13:
  case MGN_MCS14:
  case MGN_MCS15:
   index = 3;
   break;

  default:
   rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
    "Wrong rate 0x%x to obtain index in 2.4G in PHY_GetTxPowerByRateBaseIndex()\n",
    rate);
   break;
  }
 } else if (band == BAND_ON_5G) {
  switch (rate) {
  case MGN_6M:
  case MGN_9M:
  case MGN_12M:
  case MGN_18M:
  case MGN_24M:
  case MGN_36M:
  case MGN_48M:
  case MGN_54M:
   index = 0;
   break;

  case MGN_MCS0:
  case MGN_MCS1:
  case MGN_MCS2:
  case MGN_MCS3:
  case MGN_MCS4:
  case MGN_MCS5:
  case MGN_MCS6:
  case MGN_MCS7:
   index = 1;
   break;

  case MGN_MCS8:
  case MGN_MCS9:
  case MGN_MCS10:
  case MGN_MCS11:
  case MGN_MCS12:
  case MGN_MCS13:
  case MGN_MCS14:
  case MGN_MCS15:
   index = 2;
   break;

  case MGN_VHT1SS_MCS0:
  case MGN_VHT1SS_MCS1:
  case MGN_VHT1SS_MCS2:
  case MGN_VHT1SS_MCS3:
  case MGN_VHT1SS_MCS4:
  case MGN_VHT1SS_MCS5:
  case MGN_VHT1SS_MCS6:
  case MGN_VHT1SS_MCS7:
  case MGN_VHT1SS_MCS8:
  case MGN_VHT1SS_MCS9:
   index = 3;
   break;

  case MGN_VHT2SS_MCS0:
  case MGN_VHT2SS_MCS1:
  case MGN_VHT2SS_MCS2:
  case MGN_VHT2SS_MCS3:
  case MGN_VHT2SS_MCS4:
  case MGN_VHT2SS_MCS5:
  case MGN_VHT2SS_MCS6:
  case MGN_VHT2SS_MCS7:
  case MGN_VHT2SS_MCS8:
  case MGN_VHT2SS_MCS9:
   index = 4;
   break;

  default:
   rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
    "Wrong rate 0x%x to obtain index in 5G in PHY_GetTxPowerByRateBaseIndex()\n",
    rate);
   break;
  }
 }

 return index;
}

static void _rtl8812ae_phy_convert_txpower_limit_to_power_index(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 bw40_pwr_base_dbm2_4G, bw40_pwr_base_dbm5G;
 u8 regulation, bw, channel, rate_section;
 u8 base_index2_4G = 0;
 u8 base_index5G = 0;
 s8 temp_value = 0, temp_pwrlmt = 0;
 u8 rf_path = 0;

 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  "=====> _rtl8812ae_phy_convert_txpower_limit_to_power_index()\n");

 _rtl8812ae_phy_cross_reference_ht_and_vht_txpower_limit(hw);

 for (regulation = 0; regulation < MAX_REGULATION_NUM; ++regulation) {
  for (bw = 0; bw < MAX_2_4G_BANDWIDTH_NUM; ++bw) {
   for (channel = 0; channel < CHANNEL_MAX_NUMBER_2G; ++channel) {
    for (rate_section = 0; rate_section < MAX_RATE_SECTION_NUM; ++rate_section) {
     /* obtain the base dBm values in 2.4G band
 CCK => 11M, OFDM => 54M, HT 1T => MCS7, HT 2T => MCS15*/

     if (rate_section == 0) { /*CCK*/
      base_index2_4G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_2_4G, MGN_11M);
     } else if (rate_section == 1) { /*OFDM*/
      base_index2_4G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_2_4G, MGN_54M);
     } else if (rate_section == 2) { /*HT IT*/
      base_index2_4G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_2_4G, MGN_MCS7);
     } else if (rate_section == 3) { /*HT 2T*/
      base_index2_4G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_2_4G, MGN_MCS15);
     }

     temp_pwrlmt = rtlphy->txpwr_limit_2_4g[regulation]
      [bw][rate_section][channel][RF90_PATH_A];

     for (rf_path = RF90_PATH_A;
      rf_path < MAX_RF_PATH_NUM;
      ++rf_path) {
      if (rate_section == 3)
       bw40_pwr_base_dbm2_4G =
       rtlphy->txpwr_by_rate_base_24g[rf_path][RF_2TX][base_index2_4G];
      else
       bw40_pwr_base_dbm2_4G =
       rtlphy->txpwr_by_rate_base_24g[rf_path][RF_1TX][base_index2_4G];

      if (temp_pwrlmt != MAX_POWER_INDEX) {
       temp_value = temp_pwrlmt - bw40_pwr_base_dbm2_4G;
       rtlphy->txpwr_limit_2_4g[regulation]
        [bw][rate_section][channel][rf_path] =
        temp_value;
      }

      rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
       "TxPwrLimit_2_4G[regulation %d][bw %d][rateSection %d][channel %d] = %d\n(TxPwrLimit in dBm %d - BW40PwrLmt2_4G[channel %d][rfpath %d] %d)\n",
       regulation, bw, rate_section, channel,
       rtlphy->txpwr_limit_2_4g[regulation][bw]
       [rate_section][channel][rf_path], (temp_pwrlmt == 63)
       ? 0 : temp_pwrlmt/2, channel, rf_path,
       bw40_pwr_base_dbm2_4G);
     }
    }
   }
  }
 }
 for (regulation = 0; regulation < MAX_REGULATION_NUM; ++regulation) {
  for (bw = 0; bw < MAX_5G_BANDWIDTH_NUM; ++bw) {
   for (channel = 0; channel < CHANNEL_MAX_NUMBER_5G; ++channel) {
    for (rate_section = 0; rate_section < MAX_RATE_SECTION_NUM; ++rate_section) {
     /* obtain the base dBm values in 5G band
 OFDM => 54M, HT 1T => MCS7, HT 2T => MCS15,
VHT => 1SSMCS7, VHT 2T => 2SSMCS7*/

     if (rate_section == 1) { /*OFDM*/
      base_index5G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_5G, MGN_54M);
     } else if (rate_section == 2) { /*HT 1T*/
      base_index5G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_5G, MGN_MCS7);
     } else if (rate_section == 3) { /*HT 2T*/
      base_index5G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_5G, MGN_MCS15);
     } else if (rate_section == 4) { /*VHT 1T*/
      base_index5G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_5G, MGN_VHT1SS_MCS7);
     } else if (rate_section == 5) { /*VHT 2T*/
      base_index5G =
       _rtl8812ae_phy_get_txpower_by_rate_base_index(hw,
       BAND_ON_5G, MGN_VHT2SS_MCS7);
     }

     temp_pwrlmt = rtlphy->txpwr_limit_5g[regulation]
      [bw][rate_section][channel]
      [RF90_PATH_A];

     for (rf_path = RF90_PATH_A;
          rf_path < MAX_RF_PATH_NUM;
          ++rf_path) {
      if (rate_section == 3 || rate_section == 5)
       bw40_pwr_base_dbm5G =
       rtlphy->txpwr_by_rate_base_5g[rf_path]
       [RF_2TX][base_index5G];
      else
       bw40_pwr_base_dbm5G =
       rtlphy->txpwr_by_rate_base_5g[rf_path]
       [RF_1TX][base_index5G];

      if (temp_pwrlmt != MAX_POWER_INDEX) {
       temp_value =
        temp_pwrlmt - bw40_pwr_base_dbm5G;
       rtlphy->txpwr_limit_5g[regulation]
        [bw][rate_section][channel]
        [rf_path] = temp_value;
      }

      rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
       "TxPwrLimit_5G[regulation %d][bw %d][rateSection %d][channel %d] =%d\n(TxPwrLimit in dBm %d - BW40PwrLmt5G[chnl group %d][rfpath %d] %d)\n",
       regulation, bw, rate_section,
       channel, rtlphy->txpwr_limit_5g[regulation]
       [bw][rate_section][channel][rf_path],
       temp_pwrlmt, channel, rf_path, bw40_pwr_base_dbm5G);
     }
    }
   }
  }
 }
 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  "<===== %s()\n", __func__);
}

static void _rtl8821ae_phy_init_txpower_limit(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 i, j, k, l, m;

 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
  "=====>`%s()!\n", __func__);

 for (i = 0; i < MAX_REGULATION_NUM; ++i) {
  for (j = 0; j < MAX_2_4G_BANDWIDTH_NUM; ++j)
   for (k = 0; k < MAX_RATE_SECTION_NUM; ++k)
    for (m = 0; m < CHANNEL_MAX_NUMBER_2G; ++m)
     for (l = 0; l < MAX_RF_PATH_NUM; ++l)
      rtlphy->txpwr_limit_2_4g
        [i][j][k][m][l]
       = MAX_POWER_INDEX;
 }
 for (i = 0; i < MAX_REGULATION_NUM; ++i) {
  for (j = 0; j < MAX_5G_BANDWIDTH_NUM; ++j)
   for (k = 0; k < MAX_RATE_SECTION_NUM; ++k)
    for (m = 0; m < CHANNEL_MAX_NUMBER_5G; ++m)
     for (l = 0; l < MAX_RF_PATH_NUM; ++l)
      rtlphy->txpwr_limit_5g
        [i][j][k][m][l]
       = MAX_POWER_INDEX;
 }

 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
  "<===== %s()!\n", __func__);
}

static void _rtl8821ae_phy_convert_txpower_dbm_to_relative_value(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 base = 0, rfpath = 0;

 for (rfpath = RF90_PATH_A; rfpath <= RF90_PATH_B; ++rfpath) {
  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_2_4G, rfpath, RF_1TX, CCK);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][0],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_2_4G, rfpath, RF_1TX, OFDM);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][1],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][2],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_2_4G, rfpath, RF_1TX, HT_MCS0_MCS7);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][3],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][4],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_2_4G, rfpath, RF_2TX, HT_MCS8_MCS15);

  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_2TX][5],
   0, 3, base);

  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_2TX][6],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_2_4G, rfpath, RF_1TX, VHT_1SSMCS0_1SSMCS9);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][7],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][8],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][9],
   0, 1, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_2_4G, rfpath, RF_2TX, VHT_2SSMCS0_2SSMCS9);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_1TX][9],
   2, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_2TX][10],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_2_4G][rfpath][RF_2TX][11],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_5G, rfpath, RF_1TX, OFDM);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][1],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][2],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_5G, rfpath, RF_1TX, HT_MCS0_MCS7);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][3],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][4],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_5G, rfpath, RF_2TX, HT_MCS8_MCS15);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_2TX][5],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_2TX][6],
   0, 3, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_5G, rfpath, RF_1TX, VHT_1SSMCS0_1SSMCS9);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][7],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][8],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][9],
   0, 1, base);

  base = _rtl8821ae_phy_get_txpower_by_rate_base(hw, BAND_ON_5G, rfpath, RF_2TX, VHT_2SSMCS0_2SSMCS9);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_1TX][9],
   2, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_2TX][10],
   0, 3, base);
  _phy_convert_txpower_dbm_to_relative_value(
   &rtlphy->tx_power_by_rate_offset[BAND_ON_5G][rfpath][RF_2TX][11],
   0, 3, base);
 }

 rtl_dbg(rtlpriv, COMP_POWER, DBG_TRACE,
  "<===_rtl8821ae_phy_convert_txpower_dbm_to_relative_value()\n");
}

static void _rtl8821ae_phy_txpower_by_rate_configuration(struct ieee80211_hw *hw)
{
 _rtl8821ae_phy_store_txpower_by_rate_base(hw);
 _rtl8821ae_phy_convert_txpower_dbm_to_relative_value(hw);
}

/* string is in decimal */
static bool _rtl8812ae_get_integer_from_string(const char *str, u8 *pint)
{
 u16 i = 0;
 *pint = 0;

 while (str[i] != '\0') {
  if (str[i] >= '0' && str[i] <= '9') {
   *pint *= 10;
   *pint += (str[i] - '0');
  } else {
   return false;
  }
  ++i;
 }

 return true;
}

static s8 _rtl8812ae_phy_get_chnl_idx_of_txpwr_lmt(struct ieee80211_hw *hw,
           u8 band, u8 channel)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 s8 channel_index = -1;
 u8  i = 0;

 if (band == BAND_ON_2_4G)
  channel_index = channel - 1;
 else if (band == BAND_ON_5G) {
  for (i = 0; i < sizeof(channel5g)/sizeof(u8); ++i) {
   if (channel5g[i] == channel)
    channel_index = i;
  }
 } else
  rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD, "Invalid Band %d in %s\n",
   band,  __func__);

 if (channel_index == -1)
  rtl_dbg(rtlpriv, COMP_POWER, DBG_LOUD,
   "Invalid Channel %d of Band %d in %s\n", channel,
   band, __func__);

 return channel_index;
}

static void _rtl8812ae_phy_set_txpower_limit(struct ieee80211_hw *hw,
          const char *pregulation,
          const char *pband, const char *pbandwidth,
          const char *prate_section, const char *prf_path,
          const char *pchannel, const char *ppower_limit)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 regulation = 0, bandwidth = 0, rate_section = 0, channel;
 u8 channel_index;
 s8 power_limit = 0, prev_power_limit, ret;

 if (!_rtl8812ae_get_integer_from_string(pchannel, &channel) ||
     !_rtl8812ae_get_integer_from_string(ppower_limit,
      &power_limit)) {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
   "Illegal index of pwr_lmt table [chnl %d][val %d]\n",
   channel, power_limit);
 }

 power_limit = power_limit > MAX_POWER_INDEX ?
        MAX_POWER_INDEX : power_limit;

 if (strcmp(pregulation, "FCC") == 0)
  regulation = 0;
 else if (strcmp(pregulation, "MKK") == 0)
  regulation = 1;
 else if (strcmp(pregulation, "ETSI") == 0)
  regulation = 2;
 else if (strcmp(pregulation, "WW13") == 0)
  regulation = 3;

 if (strcmp(prate_section, "CCK") == 0)
  rate_section = 0;
 else if (strcmp(prate_section, "OFDM") == 0)
  rate_section = 1;
 else if (strcmp(prate_section, "HT") == 0 &&
   strcmp(prf_path, "1T") == 0)
  rate_section = 2;
 else if (strcmp(prate_section, "HT") == 0 &&
   strcmp(prf_path, "2T") == 0)
  rate_section = 3;
 else if (strcmp(prate_section, "VHT") == 0 &&
   strcmp(prf_path, "1T") == 0)
  rate_section = 4;
 else if (strcmp(prate_section, "VHT") == 0 &&
   strcmp(prf_path, "2T") == 0)
  rate_section = 5;

 if (strcmp(pbandwidth, "20M") == 0)
  bandwidth = 0;
 else if (strcmp(pbandwidth, "40M") == 0)
  bandwidth = 1;
 else if (strcmp(pbandwidth, "80M") == 0)
  bandwidth = 2;
 else if (strcmp(pbandwidth, "160M") == 0)
  bandwidth = 3;

 if (strcmp(pband, "2.4G") == 0) {
  ret = _rtl8812ae_phy_get_chnl_idx_of_txpwr_lmt(hw,
              BAND_ON_2_4G,
              channel);

  if (ret == -1)
   return;

  channel_index = ret;

  prev_power_limit = rtlphy->txpwr_limit_2_4g[regulation]
      [bandwidth][rate_section]
      [channel_index][RF90_PATH_A];

  if (power_limit < prev_power_limit)
   rtlphy->txpwr_limit_2_4g[regulation][bandwidth]
    [rate_section][channel_index][RF90_PATH_A] =
           power_limit;

  rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
   "2.4G [regula %d][bw %d][sec %d][chnl %d][val %d]\n",
   regulation, bandwidth, rate_section, channel_index,
   rtlphy->txpwr_limit_2_4g[regulation][bandwidth]
    [rate_section][channel_index][RF90_PATH_A]);
 } else if (strcmp(pband, "5G") == 0) {
  ret = _rtl8812ae_phy_get_chnl_idx_of_txpwr_lmt(hw,
              BAND_ON_5G,
              channel);

  if (ret == -1)
   return;

  channel_index = ret;

  prev_power_limit = rtlphy->txpwr_limit_5g[regulation][bandwidth]
      [rate_section][channel_index]
      [RF90_PATH_A];

  if (power_limit < prev_power_limit)
   rtlphy->txpwr_limit_5g[regulation][bandwidth]
   [rate_section][channel_index][RF90_PATH_A] = power_limit;

  rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
   "5G: [regul %d][bw %d][sec %d][chnl %d][val %d]\n",
   regulation, bandwidth, rate_section, channel,
   rtlphy->txpwr_limit_5g[regulation][bandwidth]
    [rate_section][channel_index][RF90_PATH_A]);
 } else {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
   "Cannot recognize the band info in %s\n", pband);
  return;
 }
}

static void _rtl8812ae_phy_config_bb_txpwr_lmt(struct ieee80211_hw *hw,
       const char *regulation, const char *band,
       const char *bandwidth, const char *rate_section,
       const char *rf_path, const char *channel,
       const char *power_limit)
{
 _rtl8812ae_phy_set_txpower_limit(hw, regulation, band, bandwidth,
      rate_section, rf_path, channel,
      power_limit);
}

static void _rtl8821ae_phy_read_and_config_txpwr_lmt(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
 u32 i = 0;
 u32 array_len;
 const char **array;

 if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
  array_len = RTL8812AE_TXPWR_LMT_ARRAY_LEN;
  array = RTL8812AE_TXPWR_LMT;
 } else {
  array_len = RTL8821AE_TXPWR_LMT_ARRAY_LEN;
  array = RTL8821AE_TXPWR_LMT;
 }

 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE, "\n");

 for (i = 0; i < array_len; i += 7) {
  const char *regulation = array[i];
  const char *band = array[i+1];
  const char *bandwidth = array[i+2];
  const char *rate = array[i+3];
  const char *rf_path = array[i+4];
  const char *chnl = array[i+5];
  const char *val = array[i+6];

  _rtl8812ae_phy_config_bb_txpwr_lmt(hw, regulation, band,
         bandwidth, rate, rf_path,
         chnl, val);
 }
}

static bool _rtl8821ae_phy_bb8821a_config_parafile(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw));
 bool rtstatus;

 _rtl8821ae_phy_init_txpower_limit(hw);

 /* RegEnableTxPowerLimit == 1 for 8812a & 8821a */
 if (rtlefuse->eeprom_regulatory != 2)
  _rtl8821ae_phy_read_and_config_txpwr_lmt(hw);

 rtstatus = _rtl8821ae_phy_config_bb_with_headerfile(hw,
             BASEBAND_CONFIG_PHY_REG);
 if (!rtstatus) {
  pr_err("Write BB Reg Fail!!\n");
  return false;
 }
 _rtl8821ae_phy_init_tx_power_by_rate(hw);
 if (!rtlefuse->autoload_failflag) {
  rtstatus = _rtl8821ae_phy_config_bb_with_pgheaderfile(hw,
          BASEBAND_CONFIG_PHY_REG);
 }
 if (!rtstatus) {
  pr_err("BB_PG Reg Fail!!\n");
  return false;
 }

 _rtl8821ae_phy_txpower_by_rate_configuration(hw);

 /* RegEnableTxPowerLimit == 1 for 8812a & 8821a */
 if (rtlefuse->eeprom_regulatory != 2)
  _rtl8812ae_phy_convert_txpower_limit_to_power_index(hw);

 rtstatus = _rtl8821ae_phy_config_bb_with_headerfile(hw,
      BASEBAND_CONFIG_AGC_TAB);

 if (!rtstatus) {
  pr_err("AGC Table Fail\n");
  return false;
 }
 rtlphy->cck_high_power = (bool)(rtl_get_bbreg(hw,
   RFPGA0_XA_HSSIPARAMETER2, 0x200));
 return true;
}

static bool
__rtl8821ae_phy_config_with_headerfile(struct ieee80211_hw *hw,
           u32 *array_table, u16 arraylen,
           void (*set_reg)(struct ieee80211_hw *hw,
             u32 regaddr, u32 data))
{
 #define COND_ELSE  2
 #define COND_ENDIF 3

 int i = 0;
 u8 cond;
 bool matched = true, skipped = false;

 while ((i + 1) < arraylen) {
  u32 v1 = array_table[i];
  u32 v2 = array_table[i + 1];

  if (v1 & (BIT(31) | BIT(30))) {/*positive & negative condition*/
   if (v1 & BIT(31)) {/* positive condition*/
    cond  = (u8)((v1 & (BIT(29) | BIT(28))) >> 28);
    if (cond == COND_ENDIF) {/*end*/
     matched = true;
     skipped = false;
    } else if (cond == COND_ELSE) /*else*/
     matched = skipped ? false : true;
    else {/*if , else if*/
     if (skipped) {
      matched = false;
     } else {
      if (_rtl8821ae_check_positive(
        hw, v1, v2)) {
       matched = true;
       skipped = true;
      } else {
       matched = false;
       skipped = false;
      }
     }
    }
   } else if (v1 & BIT(30)) { /*negative condition*/
   /*do nothing*/
   }
  } else {
   if (matched)
    set_reg(hw, v1, v2);
  }
  i = i + 2;
 }

 return true;
}

static bool _rtl8821ae_phy_config_mac_with_headerfile(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
 u32 arraylength;
 u32 *ptrarray;

 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE, "Read MAC_REG_Array\n");
 if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) {
  arraylength = RTL8821AE_MAC_1T_ARRAYLEN;
  ptrarray = RTL8821AE_MAC_REG_ARRAY;
 } else {
  arraylength = RTL8812AE_MAC_1T_ARRAYLEN;
  ptrarray = RTL8812AE_MAC_REG_ARRAY;
 }
 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
  "Img: MAC_REG_ARRAY LEN %d\n", arraylength);

 return __rtl8821ae_phy_config_with_headerfile(hw,
   ptrarray, arraylength, rtl_write_byte_with_val32);
}

static bool _rtl8821ae_phy_config_bb_with_headerfile(struct ieee80211_hw *hw,
           u8 configtype)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
 u32 *array_table;
 u16 arraylen;

 if (configtype == BASEBAND_CONFIG_PHY_REG) {
  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
   arraylen = RTL8812AE_PHY_REG_1TARRAYLEN;
   array_table = RTL8812AE_PHY_REG_ARRAY;
  } else {
   arraylen = RTL8821AE_PHY_REG_1TARRAYLEN;
   array_table = RTL8821AE_PHY_REG_ARRAY;
  }

  return __rtl8821ae_phy_config_with_headerfile(hw,
    array_table, arraylen,
    _rtl8821ae_config_bb_reg);
 } else if (configtype == BASEBAND_CONFIG_AGC_TAB) {
  if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
   arraylen = RTL8812AE_AGC_TAB_1TARRAYLEN;
   array_table = RTL8812AE_AGC_TAB_ARRAY;
  } else {
   arraylen = RTL8821AE_AGC_TAB_1TARRAYLEN;
   array_table = RTL8821AE_AGC_TAB_ARRAY;
  }

  return __rtl8821ae_phy_config_with_headerfile(hw,
    array_table, arraylen,
    rtl_set_bbreg_with_dwmask);
 }
 return true;
}

static u8 _rtl8821ae_get_rate_section_index(u32 regaddr)
{
 u8 index = 0;
 regaddr &= 0xFFF;
 if (regaddr >= 0xC20 && regaddr <= 0xC4C)
  index = (u8)((regaddr - 0xC20) / 4);
 else if (regaddr >= 0xE20 && regaddr <= 0xE4C)
  index = (u8)((regaddr - 0xE20) / 4);
 else
  WARN_ONCE(true,
     "rtl8821ae: Invalid RegAddr 0x%x\n", regaddr);
 return index;
}

static void _rtl8821ae_store_tx_power_by_rate(struct ieee80211_hw *hw,
           u32 band, u32 rfpath,
           u32 txnum, u32 regaddr,
           u32 bitmask, u32 data)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;
 u8 rate_section = _rtl8821ae_get_rate_section_index(regaddr);

 if (band != BAND_ON_2_4G && band != BAND_ON_5G) {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid Band %d\n", band);
  band = BAND_ON_2_4G;
 }
 if (rfpath >= MAX_RF_PATH) {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid RfPath %d\n", rfpath);
  rfpath = MAX_RF_PATH - 1;
 }
 if (txnum >= MAX_RF_PATH) {
  rtl_dbg(rtlpriv, COMP_INIT, DBG_WARNING, "Invalid TxNum %d\n", txnum);
  txnum = MAX_RF_PATH - 1;
 }
 rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section] = data;
 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
  "TxPwrByRateOffset[Band %d][RfPath %d][TxNum %d][RateSection %d] = 0x%x\n",
  band, rfpath, txnum, rate_section,
  rtlphy->tx_power_by_rate_offset[band][rfpath][txnum][rate_section]);
}

static bool _rtl8821ae_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw,
       u8 configtype)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
 int i;
 u32 *array;
 u16 arraylen;
 u32 v1, v2, v3, v4, v5, v6;

 if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) {
  arraylen = RTL8812AE_PHY_REG_ARRAY_PGLEN;
  array = RTL8812AE_PHY_REG_ARRAY_PG;
 } else {
  arraylen = RTL8821AE_PHY_REG_ARRAY_PGLEN;
  array = RTL8821AE_PHY_REG_ARRAY_PG;
 }

 if (configtype != BASEBAND_CONFIG_PHY_REG) {
  rtl_dbg(rtlpriv, COMP_SEND, DBG_TRACE,
   "configtype != BaseBand_Config_PHY_REG\n");
  return true;
 }
 for (i = 0; i < arraylen; i += 6) {
  v1 = array[i];
  v2 = array[i+1];
  v3 = array[i+2];
  v4 = array[i+3];
  v5 = array[i+4];
  v6 = array[i+5];

  if (v1 < 0xCDCDCDCD) {
   if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE &&
    (v4 == 0xfe || v4 == 0xffe)) {
    msleep(50);
    continue;
   }

   if (rtlhal->hw_type == HARDWARE_TYPE_RTL8821AE) {
    if (v4 == 0xfe)
     msleep(50);
    else if (v4 == 0xfd)
     mdelay(5);
    else if (v4 == 0xfc)
     mdelay(1);
    else if (v4 == 0xfb)
     udelay(50);
    else if (v4 == 0xfa)
     udelay(5);
    else if (v4 == 0xf9)
     udelay(1);
   }
   _rtl8821ae_store_tx_power_by_rate(hw, v1, v2, v3,
         v4, v5, v6);
   continue;
  } else {
    /*don't need the hw_body*/
   if (!_rtl8821ae_check_condition(hw, v1)) {
    i += 2; /* skip the pair of expression*/
    v2 = array[i+1];
    while (v2 != 0xDEAD) {
     i += 3;
     v2 = array[i + 1];
    }
   }
  }
 }

 return true;
}

bool rtl8812ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
          enum radio_path rfpath)
{
 u32 *radioa_array_table_a, *radioa_array_table_b;
 u16 radioa_arraylen_a, radioa_arraylen_b;
 struct rtl_priv *rtlpriv = rtl_priv(hw);

 radioa_arraylen_a = RTL8812AE_RADIOA_1TARRAYLEN;
 radioa_array_table_a = RTL8812AE_RADIOA_ARRAY;
 radioa_arraylen_b = RTL8812AE_RADIOB_1TARRAYLEN;
 radioa_array_table_b = RTL8812AE_RADIOB_ARRAY;
 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
  "Radio_A:RTL8821AE_RADIOA_ARRAY %d\n", radioa_arraylen_a);
 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD, "Radio No %x\n", rfpath);
 switch (rfpath) {
 case RF90_PATH_A:
  return __rtl8821ae_phy_config_with_headerfile(hw,
    radioa_array_table_a, radioa_arraylen_a,
    _rtl8821ae_config_rf_radio_a);
 case RF90_PATH_B:
  return __rtl8821ae_phy_config_with_headerfile(hw,
    radioa_array_table_b, radioa_arraylen_b,
    _rtl8821ae_config_rf_radio_b);
 case RF90_PATH_C:
 case RF90_PATH_D:
  pr_err("switch case %#x not processed\n", rfpath);
  break;
 }
 return true;
}

bool rtl8821ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw,
      enum radio_path rfpath)
{
 u32 *radioa_array_table;
 u16 radioa_arraylen;
 struct rtl_priv *rtlpriv = rtl_priv(hw);

 radioa_arraylen = RTL8821AE_RADIOA_1TARRAYLEN;
 radioa_array_table = RTL8821AE_RADIOA_ARRAY;
 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
  "Radio_A:RTL8821AE_RADIOA_ARRAY %d\n", radioa_arraylen);
 rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD, "Radio No %x\n", rfpath);
 switch (rfpath) {
 case RF90_PATH_A:
  return __rtl8821ae_phy_config_with_headerfile(hw,
   radioa_array_table, radioa_arraylen,
   _rtl8821ae_config_rf_radio_a);

 case RF90_PATH_B:
 case RF90_PATH_C:
 case RF90_PATH_D:
  pr_err("switch case %#x not processed\n", rfpath);
  break;
 }
 return true;
}

void rtl8821ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;

 rtlphy->default_initialgain[0] =
     (u8)rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0);
 rtlphy->default_initialgain[1] =
     (u8)rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0);
 rtlphy->default_initialgain[2] =
     (u8)rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0);
 rtlphy->default_initialgain[3] =
     (u8)rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0);

 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x\n",
  rtlphy->default_initialgain[0],
  rtlphy->default_initialgain[1],
  rtlphy->default_initialgain[2],
  rtlphy->default_initialgain[3]);

 rtlphy->framesync = (u8)rtl_get_bbreg(hw,
            ROFDM0_RXDETECTOR3, MASKBYTE0);
 rtlphy->framesync_c34 = rtl_get_bbreg(hw,
           ROFDM0_RXDETECTOR2, MASKDWORD);

 rtl_dbg(rtlpriv, COMP_INIT, DBG_TRACE,
  "Default framesync (0x%x) = 0x%x\n",
  ROFDM0_RXDETECTOR3, rtlphy->framesync);
}

static void phy_init_bb_rf_register_definition(struct ieee80211_hw *hw)
{
 struct rtl_priv *rtlpriv = rtl_priv(hw);
 struct rtl_phy *rtlphy = &rtlpriv->phy;

 rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW;
 rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW;

 rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE;
 rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE;

 rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE;
 rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE;

 rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset = RA_LSSIWRITE_8821A;
 rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset = RB_LSSIWRITE_8821A;

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=92 G=93

¤ Dauer der Verarbeitung: 0.13 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.