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

Quelle  rtw88xxa.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2024  Realtek Corporation
 */


#include <linux/usb.h>
#include "main.h"
#include "coex.h"
#include "phy.h"
#include "rtw88xxa.h"
#include "mac.h"
#include "reg.h"
#include "sec.h"
#include "debug.h"
#include "bf.h"
#include "efuse.h"
#include "usb.h"

void rtw88xxa_efuse_grant(struct rtw_dev *rtwdev, bool on)
{
 if (on) {
  rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);

  rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR);
  rtw_write16_set(rtwdev, REG_SYS_CLKR,
    BIT_LOADER_CLK_EN | BIT_ANA8M);
 } else {
  rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
 }
}
EXPORT_SYMBOL(rtw88xxa_efuse_grant);

static void rtw8812a_read_amplifier_type(struct rtw_dev *rtwdev)
{
 struct rtw_efuse *efuse = &rtwdev->efuse;

 efuse->ext_pa_2g = (efuse->pa_type_2g & BIT(5)) &&
      (efuse->pa_type_2g & BIT(4));
 efuse->ext_lna_2g = (efuse->lna_type_2g & BIT(7)) &&
       (efuse->lna_type_2g & BIT(3));

 efuse->ext_pa_5g = (efuse->pa_type_5g & BIT(1)) &&
      (efuse->pa_type_5g & BIT(0));
 efuse->ext_lna_5g = (efuse->lna_type_5g & BIT(7)) &&
       (efuse->lna_type_5g & BIT(3));

 /* For rtw_phy_cond2: */
 if (efuse->ext_pa_2g) {
  u8 ext_type_pa_2g_a = u8_get_bits(efuse->lna_type_2g, BIT(2));
  u8 ext_type_pa_2g_b = u8_get_bits(efuse->lna_type_2g, BIT(6));

  efuse->gpa_type = (ext_type_pa_2g_b << 2) | ext_type_pa_2g_a;
 }

 if (efuse->ext_pa_5g) {
  u8 ext_type_pa_5g_a = u8_get_bits(efuse->lna_type_5g, BIT(2));
  u8 ext_type_pa_5g_b = u8_get_bits(efuse->lna_type_5g, BIT(6));

  efuse->apa_type = (ext_type_pa_5g_b << 2) | ext_type_pa_5g_a;
 }

 if (efuse->ext_lna_2g) {
  u8 ext_type_lna_2g_a = u8_get_bits(efuse->lna_type_2g,
         BIT(1) | BIT(0));
  u8 ext_type_lna_2g_b = u8_get_bits(efuse->lna_type_2g,
         BIT(5) | BIT(4));

  efuse->glna_type = (ext_type_lna_2g_b << 2) | ext_type_lna_2g_a;
 }

 if (efuse->ext_lna_5g) {
  u8 ext_type_lna_5g_a = u8_get_bits(efuse->lna_type_5g,
         BIT(1) | BIT(0));
  u8 ext_type_lna_5g_b = u8_get_bits(efuse->lna_type_5g,
         BIT(5) | BIT(4));

  efuse->alna_type = (ext_type_lna_5g_b << 2) | ext_type_lna_5g_a;
 }
}

static void rtw8812a_read_rfe_type(struct rtw_dev *rtwdev,
       struct rtw88xxa_efuse *map)
{
 struct rtw_efuse *efuse = &rtwdev->efuse;

 if (map->rfe_option == 0xff) {
  if (rtwdev->hci.type == RTW_HCI_TYPE_USB)
   efuse->rfe_option = 0;
  else if (rtwdev->hci.type == RTW_HCI_TYPE_PCIE)
   efuse->rfe_option = 2;
  else
   efuse->rfe_option = 4;
 } else if (map->rfe_option & BIT(7)) {
  if (efuse->ext_lna_5g) {
   if (efuse->ext_pa_5g) {
    if (efuse->ext_lna_2g && efuse->ext_pa_2g)
     efuse->rfe_option = 3;
    else
     efuse->rfe_option = 0;
   } else {
    efuse->rfe_option = 2;
   }
  } else {
   efuse->rfe_option = 4;
  }
 } else {
  efuse->rfe_option = map->rfe_option & 0x3f;

  /* Due to other customer already use incorrect EFUSE map for
 * their product. We need to add workaround to prevent to
 * modify spec and notify all customer to revise the IC 0xca
 * content.
 */

  if (efuse->rfe_option == 4 &&
      (efuse->ext_pa_5g || efuse->ext_pa_2g ||
       efuse->ext_lna_5g || efuse->ext_lna_2g)) {
   if (rtwdev->hci.type == RTW_HCI_TYPE_USB)
    efuse->rfe_option = 0;
   else if (rtwdev->hci.type == RTW_HCI_TYPE_PCIE)
    efuse->rfe_option = 2;
  }
 }
}

static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
{
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_hal *hal = &rtwdev->hal;
 u8 antenna = 0;
 u8 wmode = 0;
 u8 val8, i;

 efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) |
      BIT(RTW_CHANNEL_WIDTH_40) |
      BIT(RTW_CHANNEL_WIDTH_80);
 efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT;

 if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
  efuse->hw_cap.nss = 1;
 else
  efuse->hw_cap.nss = 2;

 if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
  goto print_hw_cap;

 for (i = 0; i < 2; i++) {
  rtw_read8_physical_efuse(rtwdev, 1019 - i, &val8);

  antenna = u8_get_bits(val8, GENMASK(7, 5));
  if (antenna)
   break;
  antenna = u8_get_bits(val8, GENMASK(3, 1));
  if (antenna)
   break;
 }

 for (i = 0; i < 2; i++) {
  rtw_read8_physical_efuse(rtwdev, 1021 - i, &val8);

  wmode = u8_get_bits(val8, GENMASK(3, 2));
  if (wmode)
   break;
 }

 if (antenna == 1) {
  rtw_info(rtwdev, "This RTL8812AU says it is 1T1R.\n");

  efuse->hw_cap.nss = 1;
  hal->rf_type = RF_1T1R;
  hal->rf_path_num = 1;
  hal->rf_phy_num = 1;
  hal->antenna_tx = BB_PATH_A;
  hal->antenna_rx = BB_PATH_A;
 } else {
  /* Override rtw_chip_parameter_setup(). It detects 8812au as 1T1R. */
  efuse->hw_cap.nss = 2;
  hal->rf_type = RF_2T2R;
  hal->rf_path_num = 2;
  hal->rf_phy_num = 2;
  hal->antenna_tx = BB_PATH_AB;
  hal->antenna_rx = BB_PATH_AB;

  if (antenna == 2 && wmode == 2) {
   rtw_info(rtwdev, "This RTL8812AU says it can't do VHT.\n");

   /* Can't be EFUSE_HW_CAP_IGNORE and can't be
 * EFUSE_HW_CAP_PTCL_VHT, so make it 1.
 */

   efuse->hw_cap.ptcl = 1;
   efuse->hw_cap.bw &= ~BIT(RTW_CHANNEL_WIDTH_80);
  }
 }

print_hw_cap:
 rtw_dbg(rtwdev, RTW_DBG_EFUSE,
  "hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n",
  efuse->hw_cap.hci, efuse->hw_cap.bw, efuse->hw_cap.ptcl,
  efuse->hw_cap.ant_num, efuse->hw_cap.nss);
}

int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw88xxa_efuse *map;
 int i;

 if (chip->id == RTW_CHIP_TYPE_8812A)
  rtwdev->hal.cut_version += 1;

 if (rtw_dbg_is_enabled(rtwdev, RTW_DBG_EFUSE))
  print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
          log_map, chip->log_efuse_size, true);

 map = (struct rtw88xxa_efuse *)log_map;

 efuse->rf_board_option = map->rf_board_option;
 efuse->crystal_cap = map->xtal_k;
 if (efuse->crystal_cap == 0xff)
  efuse->crystal_cap = 0x20;
 efuse->pa_type_2g = map->pa_type;
 efuse->pa_type_5g = map->pa_type;
 efuse->lna_type_2g = map->lna_type_2g;
 efuse->lna_type_5g = map->lna_type_5g;
 if (chip->id == RTW_CHIP_TYPE_8812A) {
  rtw8812a_read_amplifier_type(rtwdev);
  rtw8812a_read_rfe_type(rtwdev, map);

  efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(1));
 }
 efuse->channel_plan = map->channel_plan;
 efuse->country_code[0] = map->country_code[0];
 efuse->country_code[1] = map->country_code[1];
 efuse->bt_setting = map->rf_bt_setting;
 efuse->regd = map->rf_board_option & 0x7;
 efuse->thermal_meter[0] = map->thermal_meter;
 efuse->thermal_meter[1] = map->thermal_meter;
 efuse->thermal_meter_k = map->thermal_meter;
 efuse->tx_bb_swing_setting_2g = map->tx_bb_swing_setting_2g;
 efuse->tx_bb_swing_setting_5g = map->tx_bb_swing_setting_5g;

 rtw88xxa_read_usb_type(rtwdev);

 if (chip->id == RTW_CHIP_TYPE_8821A)
  efuse->btcoex = rtw_read32_mask(rtwdev, REG_WL_BT_PWR_CTRL,
      BIT_BT_FUNC_EN);
 else
  efuse->btcoex = (map->rf_board_option & 0xe0) == 0x20;
 efuse->share_ant = !!(efuse->bt_setting & BIT(0));

 /* No antenna diversity because it's disabled in the vendor driver */
 efuse->ant_div_cfg = 0;

 efuse->ant_div_type = map->rf_antenna_option;
 if (efuse->ant_div_type == 0xff)
  efuse->ant_div_type = 0x3;

 for (i = 0; i < 4; i++)
  efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i];

 switch (rtw_hci_type(rtwdev)) {
 case RTW_HCI_TYPE_USB:
  if (chip->id == RTW_CHIP_TYPE_8821A)
   ether_addr_copy(efuse->addr, map->rtw8821au.mac_addr);
  else
   ether_addr_copy(efuse->addr, map->rtw8812au.mac_addr);
  break;
 case RTW_HCI_TYPE_PCIE:
 case RTW_HCI_TYPE_SDIO:
 default:
  /* unsupported now */
  return -EOPNOTSUPP;
 }

 return 0;
}
EXPORT_SYMBOL(rtw88xxa_read_efuse);

static void rtw88xxa_reset_8051(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 u8 val8;

 /* Reset MCU IO Wrapper */
 rtw_write8_clr(rtwdev, REG_RSV_CTRL, BIT(1));
 if (chip->id == RTW_CHIP_TYPE_8812A)
  rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT(3));
 else
  rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT(0));

 val8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN + 1);
 rtw_write8(rtwdev, REG_SYS_FUNC_EN + 1, val8 & ~BIT(2));

 /* Enable MCU IO Wrapper */
 rtw_write8_clr(rtwdev, REG_RSV_CTRL, BIT(1));
 if (chip->id == RTW_CHIP_TYPE_8812A)
  rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT(3));
 else
  rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT(0));

 rtw_write8(rtwdev, REG_SYS_FUNC_EN + 1, val8 | BIT(2));
}

/* A lightweight deinit function */
static void rtw88xxau_hw_reset(struct rtw_dev *rtwdev)
{
 u8 val8;

 if (!(rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_RAM_DL_SEL))
  return;

 rtw88xxa_reset_8051(rtwdev);
 rtw_write8(rtwdev, REG_MCUFW_CTRL, 0x00);

 /* before BB reset should do clock gated */
 rtw_write32_set(rtwdev, REG_FPGA0_XCD_RF_PARA, BIT(6));

 /* reset BB */
 rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));

 /* reset RF */
 rtw_write8(rtwdev, REG_RF_CTRL, 0);

 /* reset TRX path */
 rtw_write16(rtwdev, REG_CR, 0);

 /* reset MAC, reg0x5[1], auto FSM off */
 rtw_write8_set(rtwdev, REG_APS_FSMCO + 1, APS_FSMCO_MAC_OFF >> 8);

 /* check if reg0x5[1] auto cleared */
 if (read_poll_timeout_atomic(rtw_read8, val8,
         !(val8 & (APS_FSMCO_MAC_OFF >> 8)),
         1, 5000, false,
         rtwdev, REG_APS_FSMCO + 1))
  rtw_err(rtwdev, "%s: timed out waiting for 0x5[1]\n", __func__);

 /* reg0x5[0], auto FSM on */
 val8 |= APS_FSMCO_MAC_ENABLE >> 8;
 rtw_write8(rtwdev, REG_APS_FSMCO + 1, val8);

 rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT(4) | BIT(7));
 rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT(4) | BIT(7));
}

static int rtw88xxau_init_power_on(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 u16 val16;
 int ret;

 ret = rtw_pwr_seq_parser(rtwdev, chip->pwr_on_seq);
 if (ret) {
  rtw_err(rtwdev, "power on flow failed\n");
  return ret;
 }

 rtw_write16(rtwdev, REG_CR, 0);
 val16 = BIT_HCI_TXDMA_EN | BIT_HCI_RXDMA_EN | BIT_TXDMA_EN |
  BIT_RXDMA_EN | BIT_PROTOCOL_EN | BIT_SCHEDULE_EN |
  BIT_MAC_SEC_EN | BIT_32K_CAL_TMR_EN;
 rtw_write16_set(rtwdev, REG_CR, val16);

 if (chip->id == RTW_CHIP_TYPE_8821A) {
  if (rtw_read8(rtwdev, REG_SYS_CFG1 + 3) & BIT(0))
   rtw_write8_set(rtwdev, REG_LDO_SWR_CTRL, BIT(6));
 }

 return ret;
}

static int rtw88xxa_llt_write(struct rtw_dev *rtwdev, u32 address, u32 data)
{
 u32 value = BIT_LLT_WRITE_ACCESS | (address << 8) | data;
 int count = 0;

 rtw_write32(rtwdev, REG_LLT_INIT, value);

 do {
  if (!rtw_read32_mask(rtwdev, REG_LLT_INIT, BIT(31) | BIT(30)))
   break;

  if (count > 20) {
   rtw_err(rtwdev, "Failed to poll write LLT done at %d!\n",
    address);
   return -EBUSY;
  }
 } while (++count);

 return 0;
}

static int rtw88xxa_llt_init(struct rtw_dev *rtwdev, u32 boundary)
{
 u32 last_entry = 255;
 int status = 0;
 u32 i;

 for (i = 0; i < boundary - 1; i++) {
  status = rtw88xxa_llt_write(rtwdev, i, i + 1);
  if (status)
   return status;
 }

 status = rtw88xxa_llt_write(rtwdev, boundary - 1, 0xFF);
 if (status)
  return status;

 for (i = boundary; i < last_entry; i++) {
  status = rtw88xxa_llt_write(rtwdev, i, i + 1);
  if (status)
   return status;
 }

 status = rtw88xxa_llt_write(rtwdev, last_entry, boundary);

 return status;
}

static void rtw88xxau_init_queue_reserved_page(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_fifo_conf *fifo = &rtwdev->fifo;
 const struct rtw_page_table *pg_tbl = NULL;
 u16 pubq_num;
 u32 val32;

 switch (rtw_hci_type(rtwdev)) {
 case RTW_HCI_TYPE_PCIE:
  pg_tbl = &chip->page_table[1];
  break;
 case RTW_HCI_TYPE_USB:
  if (rtwdev->hci.bulkout_num == 2)
   pg_tbl = &chip->page_table[2];
  else if (rtwdev->hci.bulkout_num == 3)
   pg_tbl = &chip->page_table[3];
  else if (rtwdev->hci.bulkout_num == 4)
   pg_tbl = &chip->page_table[4];
  break;
 case RTW_HCI_TYPE_SDIO:
  pg_tbl = &chip->page_table[0];
  break;
 default:
  break;
 }

 pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num -
     pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num;

 val32 = BIT_RQPN_NE(pg_tbl->nq_num, pg_tbl->exq_num);
 rtw_write32(rtwdev, REG_RQPN_NPQ, val32);

 val32 = BIT_RQPN_HLP(pg_tbl->hq_num, pg_tbl->lq_num, pubq_num);
 rtw_write32(rtwdev, REG_RQPN, val32);
}

static void rtw88xxau_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
{
 struct rtw_fifo_conf *fifo = &rtwdev->fifo;

 rtw_write8(rtwdev, REG_BCNQ_BDNY, fifo->rsvd_boundary);
 rtw_write8(rtwdev, REG_MGQ_BDNY, fifo->rsvd_boundary);
 rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, fifo->rsvd_boundary);
 rtw_write8(rtwdev, REG_TRXFF_BNDY, fifo->rsvd_boundary);
 rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, fifo->rsvd_boundary);
}

static int rtw88xxau_init_queue_priority(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 u8 bulkout_num = rtwdev->hci.bulkout_num;
 const struct rtw_rqpn *rqpn = NULL;
 u16 txdma_pq_map;

 switch (rtw_hci_type(rtwdev)) {
 case RTW_HCI_TYPE_PCIE:
  rqpn = &chip->rqpn_table[1];
  break;
 case RTW_HCI_TYPE_USB:
  if (bulkout_num == 2)
   rqpn = &chip->rqpn_table[2];
  else if (bulkout_num == 3)
   rqpn = &chip->rqpn_table[3];
  else if (bulkout_num == 4)
   rqpn = &chip->rqpn_table[4];
  else
   return -EINVAL;
  break;
 case RTW_HCI_TYPE_SDIO:
  rqpn = &chip->rqpn_table[0];
  break;
 default:
  return -EINVAL;
 }

 rtwdev->fifo.rqpn = rqpn;

 txdma_pq_map = rtw_read16(rtwdev, REG_TXDMA_PQ_MAP) & 0x7;
 txdma_pq_map |= BIT_TXDMA_HIQ_MAP(rqpn->dma_map_hi);
 txdma_pq_map |= BIT_TXDMA_MGQ_MAP(rqpn->dma_map_mg);
 txdma_pq_map |= BIT_TXDMA_BKQ_MAP(rqpn->dma_map_bk);
 txdma_pq_map |= BIT_TXDMA_BEQ_MAP(rqpn->dma_map_be);
 txdma_pq_map |= BIT_TXDMA_VIQ_MAP(rqpn->dma_map_vi);
 txdma_pq_map |= BIT_TXDMA_VOQ_MAP(rqpn->dma_map_vo);
 rtw_write16(rtwdev, REG_TXDMA_PQ_MAP, txdma_pq_map);

 /* Packet in Hi Queue Tx immediately (No constraint for ATIM Period). */
 if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && bulkout_num == 4)
  rtw_write8(rtwdev, REG_HIQ_NO_LMT_EN, 0xff);

 return 0;
}

static void rtw88xxa_init_wmac_setting(struct rtw_dev *rtwdev)
{
 rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff);
 rtw_write16(rtwdev, REG_RXFLTMAP1, 0x0400);
 rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff);

 rtw_write32(rtwdev, REG_MAR, 0xffffffff);
 rtw_write32(rtwdev, REG_MAR + 4, 0xffffffff);
}

static void rtw88xxa_init_adaptive_ctrl(struct rtw_dev *rtwdev)
{
 rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xffff1);
 rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030);
}

static void rtw88xxa_init_edca(struct rtw_dev *rtwdev)
{
 rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a);
 rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a);

 rtw_write16(rtwdev, REG_SIFS, 0x100a);
 rtw_write16(rtwdev, REG_SIFS + 2, 0x100a);

 rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B);
 rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F);
 rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324);
 rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226);

 rtw_write8(rtwdev, REG_USTIME_TSF, 0x50);
 rtw_write8(rtwdev, REG_USTIME_EDCA, 0x50);
}

static void rtw88xxau_tx_aggregation(struct rtw_dev *rtwdev)
{
 const struct rtw_chip_info *chip = rtwdev->chip;

 rtw_write32_mask(rtwdev, REG_DWBCN0_CTRL, 0xf0,
    chip->usb_tx_agg_desc_num);

 if (chip->id == RTW_CHIP_TYPE_8821A)
  rtw_write8(rtwdev, REG_DWBCN1_CTRL,
      chip->usb_tx_agg_desc_num << 1);
}

static void rtw88xxa_init_beacon_parameters(struct rtw_dev *rtwdev)
{
 u16 val16;

 val16 = (BIT_DIS_TSF_UDT << 8) | BIT_DIS_TSF_UDT;
 if (rtwdev->efuse.btcoex)
  val16 |= BIT_EN_BCN_FUNCTION;
 rtw_write16(rtwdev, REG_BCN_CTRL, val16);

 rtw_write32_mask(rtwdev, REG_TBTT_PROHIBIT, 0xfffff, WLAN_TBTT_TIME);
 rtw_write8(rtwdev, REG_DRVERLYINT, 0x05);
 rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME);
 rtw_write16(rtwdev, REG_BCNTCFG, 0x4413);
}

static void rtw88xxa_phy_bb_config(struct rtw_dev *rtwdev)
{
 u8 val8, crystal_cap;

 /* power on BB/RF domain */
 val8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
 val8 |= BIT_FEN_USBA;
 rtw_write8(rtwdev, REG_SYS_FUNC_EN, val8);

 /* toggle BB reset */
 val8 |= BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST;
 rtw_write8(rtwdev, REG_SYS_FUNC_EN, val8);

 rtw_write8(rtwdev, REG_RF_CTRL,
     BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB);
 rtw_write8(rtwdev, REG_RF_B_CTRL,
     BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB);

 rtw_load_table(rtwdev, rtwdev->chip->bb_tbl);
 rtw_load_table(rtwdev, rtwdev->chip->agc_tbl);

 crystal_cap = rtwdev->efuse.crystal_cap & 0x3F;
 if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
  rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x7FF80000,
     crystal_cap | (crystal_cap << 6));
 else
  rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x00FFF000,
     crystal_cap | (crystal_cap << 6));
}

static void rtw88xxa_phy_rf_config(struct rtw_dev *rtwdev)
{
 u8 rf_path;

 for (rf_path = 0; rf_path < rtwdev->hal.rf_path_num; rf_path++)
  rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[rf_path]);
}

static void rtw8812a_config_1t(struct rtw_dev *rtwdev)
{
 /* BB OFDM RX Path_A */
 rtw_write32_mask(rtwdev, REG_RXPSEL, 0xff, 0x11);

 /* BB OFDM TX Path_A */
 rtw_write32_mask(rtwdev, REG_TXPSEL, MASKLWORD, 0x1111);

 /* BB CCK R/Rx Path_A */
 rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0c000000, 0x0);

 /* MCS support */
 rtw_write32_mask(rtwdev, REG_RX_MCS_LIMIT, 0xc0000060, 0x4);

 /* RF Path_B HSSI OFF */
 rtw_write32_mask(rtwdev, REG_3WIRE_SWB, 0xf, 0x4);

 /* RF Path_B Power Down */
 rtw_write32_mask(rtwdev, REG_LSSI_WRITE_B, MASKDWORD, 0);

 /* ADDA Path_B OFF */
 rtw_write32_mask(rtwdev, REG_AFE_PWR1_B, MASKDWORD, 0);
 rtw_write32_mask(rtwdev, REG_AFE_PWR2_B, MASKDWORD, 0);
}

static const u32 rtw88xxa_txscale_tbl[] = {
 0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8,
 0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180,
 0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab,
 0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe
};

static u32 rtw88xxa_get_bb_swing(struct rtw_dev *rtwdev, u8 band, u8 path)
{
 static const u32 swing2setting[4] = {0x200, 0x16a, 0x101, 0x0b6};
 struct rtw_efuse *efuse = &rtwdev->efuse;
 u8 tx_bb_swing;

 if (band == RTW_BAND_2G)
  tx_bb_swing = efuse->tx_bb_swing_setting_2g;
 else
  tx_bb_swing = efuse->tx_bb_swing_setting_5g;

 if (path == RF_PATH_B)
  tx_bb_swing >>= 2;
 tx_bb_swing &= 0x3;

 return swing2setting[tx_bb_swing];
}

static u8 rtw88xxa_get_swing_index(struct rtw_dev *rtwdev)
{
 u32 swing, table_value;
 u8 i;

 swing = rtw88xxa_get_bb_swing(rtwdev, rtwdev->hal.current_band_type,
          RF_PATH_A);

 for (i = 0; i < ARRAY_SIZE(rtw88xxa_txscale_tbl); i++) {
  table_value = rtw88xxa_txscale_tbl[i];
  if (swing == table_value)
   return i;
 }

 return 24;
}

static void rtw88xxa_pwrtrack_init(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u8 path;

 dm_info->default_ofdm_index = rtw88xxa_get_swing_index(rtwdev);

 if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
  dm_info->default_cck_index = 0;
 else
  dm_info->default_cck_index = 24;

 for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
  ewma_thermal_init(&dm_info->avg_thermal[path]);
  dm_info->delta_power_index[path] = 0;
  dm_info->delta_power_index_last[path] = 0;
 }

 dm_info->pwr_trk_triggered = false;
 dm_info->pwr_trk_init_trigger = true;
 dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
}

void rtw88xxa_power_off(struct rtw_dev *rtwdev,
   const struct rtw_pwr_seq_cmd *const *enter_lps_flow)
{
 struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
 enum usb_device_speed speed = rtwusb->udev->speed;
 u16 ori_fsmc0;
 u8 reg_cr;

 reg_cr = rtw_read8(rtwdev, REG_CR);

 /* Already powered off */
 if (reg_cr == 0 || reg_cr == 0xEA)
  return;

 rtw_hci_stop(rtwdev);

 if (!rtwdev->efuse.btcoex)
  rtw_write16_clr(rtwdev, REG_GPIO_MUXCFG, BIT_EN_SIC);

 /* set Reg 0xf008[3:4] to 2'11 to enable U1/U2 Mode in USB3.0. */
 if (speed == USB_SPEED_SUPER)
  rtw_write8_set(rtwdev, REG_USB_MOD, 0x18);

 rtw_write32(rtwdev, REG_HISR0, 0xffffffff);
 rtw_write32(rtwdev, REG_HISR1, 0xffffffff);
 rtw_write32(rtwdev, REG_HIMR0, 0);
 rtw_write32(rtwdev, REG_HIMR1, 0);

 if (rtwdev->efuse.btcoex)
  rtw_coex_power_off_setting(rtwdev);

 ori_fsmc0 = rtw_read16(rtwdev, REG_APS_FSMCO);
 rtw_write16(rtwdev, REG_APS_FSMCO, ori_fsmc0 & ~APS_FSMCO_HW_POWERDOWN);

 /* Stop Tx Report Timer. */
 rtw_write8_clr(rtwdev, REG_TX_RPT_CTRL, BIT(1));

 /* Stop Rx */
 rtw_write8(rtwdev, REG_CR, 0);

 rtw_pwr_seq_parser(rtwdev, enter_lps_flow);

 if (rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_RAM_DL_SEL)
  rtw88xxa_reset_8051(rtwdev);

 rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT(2));
 rtw_write8(rtwdev, REG_MCUFW_CTRL, 0);

 rtw_pwr_seq_parser(rtwdev, rtwdev->chip->pwr_off_seq);

 if (ori_fsmc0 & APS_FSMCO_HW_POWERDOWN)
  rtw_write16_set(rtwdev, REG_APS_FSMCO, APS_FSMCO_HW_POWERDOWN);

 clear_bit(RTW_FLAG_POWERON, rtwdev->flags);
}
EXPORT_SYMBOL(rtw88xxa_power_off);

static void rtw88xxa_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 band)
{
 rtw_write32_mask(rtwdev, REG_TXSCALE_A, BB_SWING_MASK,
    rtw88xxa_get_bb_swing(rtwdev, band, RF_PATH_A));
 rtw_write32_mask(rtwdev, REG_TXSCALE_B, BB_SWING_MASK,
    rtw88xxa_get_bb_swing(rtwdev, band, RF_PATH_B));
 rtw88xxa_pwrtrack_init(rtwdev);
}

static void rtw8821a_set_ext_band_switch(struct rtw_dev *rtwdev, u8 band)
{
 rtw_write32_mask(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN, 0);
 rtw_write32_mask(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL, 1);
 rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0xf, 7);
 rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0xf0, 7);

 if (band == RTW_BAND_2G)
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(29) | BIT(28), 1);
 else
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(29) | BIT(28), 2);
}

static void rtw8821a_phy_set_rfe_reg_24g(struct rtw_dev *rtwdev)
{
 struct rtw_efuse *efuse = &rtwdev->efuse;

 /* Turn off RF PA and LNA */

 /* 0xCB0[15:12] = 0x7 (LNA_On)*/
 rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF000, 0x7);
 /* 0xCB0[7:4] = 0x7 (PAPE_A)*/
 rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF0, 0x7);

 if (efuse->ext_lna_2g) {
  /* Turn on 2.4G External LNA */
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(20), 1);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(22), 0);
  rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(2, 0), 0x2);
  rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(10, 8), 0x2);
 } else {
  /* Bypass 2.4G External LNA */
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(20), 0);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(22), 0);
  rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(2, 0), 0x7);
  rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(10, 8), 0x7);
 }
}

static void rtw8821a_phy_set_rfe_reg_5g(struct rtw_dev *rtwdev)
{
 /* Turn ON RF PA and LNA */

 /* 0xCB0[15:12] = 0x7 (LNA_On)*/
 rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF000, 0x5);
 /* 0xCB0[7:4] = 0x7 (PAPE_A)*/
 rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF0, 0x4);

 /* Bypass 2.4G External LNA */
 rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(20), 0);
 rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(22), 0);
 rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(2, 0), 0x7);
 rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(10, 8), 0x7);
}

static void rtw8812a_phy_set_rfe_reg_24g(struct rtw_dev *rtwdev)
{
 switch (rtwdev->efuse.rfe_option) {
 case 0:
 case 2:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x000);
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
  break;
 case 1:
  if (rtwdev->efuse.btcoex) {
   rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xffffff, 0x777777);
   rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
   rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0x33f00000, 0x000);
   rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
  } else {
   rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
   rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
   rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x000);
   rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
  }
  break;
 case 3:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x54337770);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x54337770);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
  rtw_write32_mask(rtwdev, REG_ANTSEL_SW, 0x00000303, 0x1);
  break;
 case 4:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x001);
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x001);
  break;
 case 5:
  rtw_write8(rtwdev, REG_RFE_PINMUX_A + 2, 0x77);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
  rtw_write8_clr(rtwdev, REG_RFE_INV_A + 3, BIT(0));
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
  break;
 case 6:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x07772770);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x07772770);
  rtw_write32(rtwdev, REG_RFE_INV_A, 0x00000077);
  rtw_write32(rtwdev, REG_RFE_INV_B, 0x00000077);
  break;
 default:
  break;
 }
}

static void rtw8812a_phy_set_rfe_reg_5g(struct rtw_dev *rtwdev)
{
 switch (rtwdev->efuse.rfe_option) {
 case 0:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77337717);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337717);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
  break;
 case 1:
  if (rtwdev->efuse.btcoex) {
   rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xffffff, 0x337717);
   rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337717);
   rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0x33f00000, 0x000);
   rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
  } else {
   rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77337717);
   rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337717);
   rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x000);
   rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
  }
  break;
 case 2:
 case 4:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77337777);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337777);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
  break;
 case 3:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x54337717);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x54337717);
  rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
  rtw_write32_mask(rtwdev, REG_ANTSEL_SW, 0x00000303, 0x1);
  break;
 case 5:
  rtw_write8(rtwdev, REG_RFE_PINMUX_A + 2, 0x33);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337777);
  rtw_write8_set(rtwdev, REG_RFE_INV_A + 3, BIT(0));
  rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
  break;
 case 6:
  rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x07737717);
  rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x07737717);
  rtw_write32(rtwdev, REG_RFE_INV_A, 0x00000077);
  rtw_write32(rtwdev, REG_RFE_INV_B, 0x00000077);
  break;
 default:
  break;
 }
}

static void rtw88xxa_switch_band(struct rtw_dev *rtwdev, u8 new_band, u8 bw)
{
 const struct rtw_chip_info *chip = rtwdev->chip;
 u16 basic_rates, reg_41a;

 /* 8811au one antenna module doesn't support antenna div, so driver must
 * control antenna band, otherwise one of the band will have issue
 */

 if (chip->id == RTW_CHIP_TYPE_8821A && !rtwdev->efuse.btcoex &&
     rtwdev->efuse.ant_div_cfg == 0)
  rtw8821a_set_ext_band_switch(rtwdev, new_band);

 if (new_band == RTW_BAND_2G) {
  rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST);

  if (chip->id == RTW_CHIP_TYPE_8821A) {
   rtw8821a_phy_set_rfe_reg_24g(rtwdev);

   rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 0);
  } else {
   rtw_write32_mask(rtwdev, REG_BWINDICATION, 0x3, 0x1);
   rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(17, 13), 0x17);

   if (bw == RTW_CHANNEL_WIDTH_20 &&
       rtwdev->hal.rf_type == RF_1T1R &&
       !rtwdev->efuse.ext_lna_2g)
    rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(3, 1), 0x02);
   else
    rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(3, 1), 0x04);

   rtw_write32_mask(rtwdev, REG_CCASEL, 0x3, 0);

   rtw8812a_phy_set_rfe_reg_24g(rtwdev);
  }

  rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x1);
  rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x1);

  basic_rates = BIT(DESC_RATE1M) | BIT(DESC_RATE2M) |
         BIT(DESC_RATE5_5M) | BIT(DESC_RATE11M) |
         BIT(DESC_RATE6M) | BIT(DESC_RATE12M) |
         BIT(DESC_RATE24M);
  rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, basic_rates);

  rtw_write8_clr(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN);
 } else { /* RTW_BAND_5G */
  if (chip->id == RTW_CHIP_TYPE_8821A)
   rtw8821a_phy_set_rfe_reg_5g(rtwdev);

  rtw_write8_set(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN);

  read_poll_timeout_atomic(rtw_read16, reg_41a, (reg_41a & 0x30) == 0x30,
      50, 2500, false, rtwdev, REG_TXPKT_EMPTY);

  rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST);

  if (chip->id == RTW_CHIP_TYPE_8821A) {
   rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 1);
  } else {
   rtw_write32_mask(rtwdev, REG_BWINDICATION, 0x3, 0x2);
   rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(17, 13), 0x15);
   rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(3, 1), 0x04);

   rtw_write32_mask(rtwdev, REG_CCASEL, 0x3, 1);

   rtw8812a_phy_set_rfe_reg_5g(rtwdev);
  }

  rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0);
  rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0xf);

  basic_rates = BIT(DESC_RATE6M) | BIT(DESC_RATE12M) |
         BIT(DESC_RATE24M);
  rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, basic_rates);
 }

 rtw88xxa_set_channel_bb_swing(rtwdev, new_band);
}

int rtw88xxa_power_on(struct rtw_dev *rtwdev)
{
 struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
 const struct rtw_chip_info *chip = rtwdev->chip;
 struct rtw_efuse *efuse = &rtwdev->efuse;
 struct rtw_hal *hal = &rtwdev->hal;
 int ret;

 if (test_bit(RTW_FLAG_POWERON, rtwdev->flags))
  return 0;

 /* Override rtw_chip_efuse_info_setup() */
 if (chip->id == RTW_CHIP_TYPE_8821A)
  efuse->btcoex = rtw_read32_mask(rtwdev, REG_WL_BT_PWR_CTRL,
      BIT_BT_FUNC_EN);

 /* Override rtw_chip_efuse_info_setup() */
 if (chip->id == RTW_CHIP_TYPE_8812A)
  rtw8812a_read_amplifier_type(rtwdev);

 ret = rtw_hci_setup(rtwdev);
 if (ret) {
  rtw_err(rtwdev, "failed to setup hci\n");
  goto err;
 }

 /* Revise for U2/U3 switch we can not update RF-A/B reset.
 * Reset after MAC power on to prevent RF R/W error.
 * Is it a right method?
 */

 if (chip->id == RTW_CHIP_TYPE_8812A) {
  rtw_write8(rtwdev, REG_RF_CTRL, 5);
  rtw_write8(rtwdev, REG_RF_CTRL, 7);
  rtw_write8(rtwdev, REG_RF_B_CTRL, 5);
  rtw_write8(rtwdev, REG_RF_B_CTRL, 7);
 }

 /* If HW didn't go through a complete de-initial procedure,
 * it probably occurs some problem for double initial
 * procedure.
 */

 rtw88xxau_hw_reset(rtwdev);

 ret = rtw88xxau_init_power_on(rtwdev);
 if (ret) {
  rtw_err(rtwdev, "failed to power on\n");
  goto err;
 }

 ret = rtw_set_trx_fifo_info(rtwdev);
 if (ret) {
  rtw_err(rtwdev, "failed to set trx fifo info\n");
  goto err;
 }

 ret = rtw88xxa_llt_init(rtwdev, rtwdev->fifo.rsvd_boundary);
 if (ret) {
  rtw_err(rtwdev, "failed to init llt\n");
  goto err;
 }

 rtw_write32_set(rtwdev, REG_TXDMA_OFFSET_CHK, BIT_DROP_DATA_EN);

 ret = rtw_wait_firmware_completion(rtwdev);
 if (ret) {
  rtw_err(rtwdev, "failed to wait firmware completion\n");
  goto err_off;
 }

 ret = rtw_download_firmware(rtwdev, &rtwdev->fw);
 if (ret) {
  rtw_err(rtwdev, "failed to download firmware\n");
  goto err_off;
 }

 rtw_write8(rtwdev, REG_HMETFR, 0xf);

 rtw_load_table(rtwdev, chip->mac_tbl);

 rtw88xxau_init_queue_reserved_page(rtwdev);
 rtw88xxau_init_tx_buffer_boundary(rtwdev);
 rtw88xxau_init_queue_priority(rtwdev);

 rtw_write16(rtwdev, REG_TRXFF_BNDY + 2,
      chip->rxff_size - REPORT_BUF - 1);

 if (chip->id == RTW_CHIP_TYPE_8812A)
  rtw_write8(rtwdev, REG_PBP,
      u8_encode_bits(PBP_512, PBP_TX_MASK) |
      u8_encode_bits(PBP_64, PBP_RX_MASK));

 rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE);

 rtw_write32(rtwdev, REG_HIMR0, 0);
 rtw_write32(rtwdev, REG_HIMR1, 0);

 rtw_write32_mask(rtwdev, REG_CR, 0x30000, 0x2);

 rtw88xxa_init_wmac_setting(rtwdev);
 rtw88xxa_init_adaptive_ctrl(rtwdev);
 rtw88xxa_init_edca(rtwdev);

 rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
 rtw_write8(rtwdev, REG_ACKTO, 0x80);

 rtw88xxau_tx_aggregation(rtwdev);

 rtw88xxa_init_beacon_parameters(rtwdev);
 rtw_write8(rtwdev, REG_BCN_MAX_ERR, 0xff);

 rtw_hci_interface_cfg(rtwdev);

 /* usb3 rx interval */
 rtw_write8(rtwdev, REG_USB3_RXITV, 0x01);

 /* burst length=4, set 0x3400 for burst length=2 */
 rtw_write16(rtwdev, REG_RXDMA_STATUS, 0x7400);
 rtw_write8(rtwdev, REG_RXDMA_STATUS + 1, 0xf5);

 /* 0x456 = 0x70, sugguested by Zhilin */
 if (chip->id == RTW_CHIP_TYPE_8821A)
  rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, 0x5e);
 else
  rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, 0x70);

 rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, 0xffffffff);
 rtw_write8(rtwdev, REG_USTIME_TSF, 0x50);
 rtw_write8(rtwdev, REG_USTIME_EDCA, 0x50);

 if (rtwusb->udev->speed == USB_SPEED_SUPER)
  /* Disable U1/U2 Mode to avoid 2.5G spur in USB3.0. */
  rtw_write8_clr(rtwdev, REG_USB_MOD, BIT(4) | BIT(3));

 rtw_write8_set(rtwdev, REG_SINGLE_AMPDU_CTRL, BIT_EN_SINGLE_APMDU);

 /* for VHT packet length 11K */
 rtw_write8(rtwdev, REG_RX_PKT_LIMIT, 0x18);

 rtw_write8(rtwdev, REG_PIFS, 0x00);

 if (chip->id == RTW_CHIP_TYPE_8821A) {
  /* 0x0a0a too small, it can't pass AC logo. change to 0x1f1f */
  rtw_write16(rtwdev, REG_MAX_AGGR_NUM, 0x1f1f);
  rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL, 0x80);
  rtw_write32(rtwdev, REG_FAST_EDCA_CTRL, 0x03087777);
 } else {
  rtw_write16(rtwdev, REG_MAX_AGGR_NUM, 0x1f1f);
  rtw_write8_clr(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
 }

  /* to prevent mac is reseted by bus. */
 rtw_write8_set(rtwdev, REG_RSV_CTRL, BIT(5) | BIT(6));

 /* ARFB table 9 for 11ac 5G 2SS */
 rtw_write32(rtwdev, REG_ARFR0, 0x00000010);
 rtw_write32(rtwdev, REG_ARFRH0, 0xfffff000);

 /* ARFB table 10 for 11ac 5G 1SS */
 rtw_write32(rtwdev, REG_ARFR1_V1, 0x00000010);
 rtw_write32(rtwdev, REG_ARFRH1_V1, 0x003ff000);

 /* ARFB table 11 for 11ac 24G 1SS */
 rtw_write32(rtwdev, REG_ARFR2_V1, 0x00000015);
 rtw_write32(rtwdev, REG_ARFRH2_V1, 0x003ff000);

 /* ARFB table 12 for 11ac 24G 2SS */
 rtw_write32(rtwdev, REG_ARFR3_V1, 0x00000015);
 rtw_write32(rtwdev, REG_ARFRH3_V1, 0xffcff000);

 rtw_write8_set(rtwdev, REG_CR, BIT_MACTXEN | BIT_MACRXEN);

 rtw88xxa_phy_bb_config(rtwdev);
 rtw88xxa_phy_rf_config(rtwdev);

 if (chip->id == RTW_CHIP_TYPE_8812A && hal->rf_path_num == 1)
  rtw8812a_config_1t(rtwdev);

 rtw88xxa_switch_band(rtwdev, RTW_BAND_2G, RTW_CHANNEL_WIDTH_20);

 rtw_write32(rtwdev, RTW_SEC_CMD_REG, BIT(31) | BIT(30));

 rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xff);
 rtw_write32(rtwdev, REG_BAR_MODE_CTRL, 0x0201ffff);
 rtw_write8(rtwdev, REG_NAV_CTRL + 2, 0);

 rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(5));

 rtw_phy_init(rtwdev);

 rtw88xxa_pwrtrack_init(rtwdev);

 /* 0x4c6[3] 1: RTS BW = Data BW
 * 0: RTS BW depends on CCA / secondary CCA result.
 */

 rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT(3));

 /* enable Tx report. */
 rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, 0x0f);

 /* Pretx_en, for WEP/TKIP SEC */
 rtw_write8(rtwdev, REG_EARLY_MODE_CONTROL + 3, 0x01);

 rtw_write16(rtwdev, REG_TX_RPT_TIME, 0x3df0);

 /* Reset USB mode switch setting */
 rtw_write8(rtwdev, REG_SYS_SDIO_CTRL, 0x0);
 rtw_write8(rtwdev, REG_ACLK_MON, 0x0);

 rtw_write8(rtwdev, REG_USB_HRPWM, 0);

 /* ack for xmit mgmt frames. */
 rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(12));

 hal->cck_high_power = rtw_read32_mask(rtwdev, REG_CCK_RPT_FORMAT,
           BIT_CCK_RPT_FORMAT);

 ret = rtw_hci_start(rtwdev);
 if (ret) {
  rtw_err(rtwdev, "failed to start hci\n");
  goto err_off;
 }

 if (efuse->btcoex) {
  rtw_coex_power_on_setting(rtwdev);
  rtw_coex_init_hw_config(rtwdev, false);
 }

 set_bit(RTW_FLAG_POWERON, rtwdev->flags);

 return 0;

err_off:
 chip->ops->power_off(rtwdev);

err:
 return ret;
}
EXPORT_SYMBOL(rtw88xxa_power_on);

u32 rtw88xxa_phy_read_rf(struct rtw_dev *rtwdev,
    enum rtw_rf_path rf_path, u32 addr, u32 mask)
{
 static const u32 pi_addr[2] = { REG_3WIRE_SWA, REG_3WIRE_SWB };
 static const u32 read_addr[2][2] = {
  { REG_SI_READ_A, REG_SI_READ_B },
  { REG_PI_READ_A, REG_PI_READ_B }
 };
 const struct rtw_chip_info *chip = rtwdev->chip;
 const struct rtw_hal *hal = &rtwdev->hal;
 bool set_cca, pi_mode;
 u32 val;

 if (rf_path >= hal->rf_phy_num) {
  rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path);
  return INV_RF_DATA;
 }

 /* CCA off to avoid reading the wrong value.
 * Toggling CCA would affect RF 0x0, skip it.
 */

 set_cca = addr != 0x0 && chip->id == RTW_CHIP_TYPE_8812A &&
    hal->cut_version != RTW_CHIP_VER_CUT_C;

 if (set_cca)
  rtw_write32_set(rtwdev, REG_CCA2ND, BIT(3));

 addr &= 0xff;

 pi_mode = rtw_read32_mask(rtwdev, pi_addr[rf_path], 0x4);

 rtw_write32_mask(rtwdev, REG_HSSI_READ, MASKBYTE0, addr);

 if (chip->id == RTW_CHIP_TYPE_8821A ||
     hal->cut_version == RTW_CHIP_VER_CUT_C)
  udelay(20);

 val = rtw_read32_mask(rtwdev, read_addr[pi_mode][rf_path], mask);

 /* CCA on */
 if (set_cca)
  rtw_write32_clr(rtwdev, REG_CCA2ND, BIT(3));

 return val;
}
EXPORT_SYMBOL(rtw88xxa_phy_read_rf);

static void rtw8812a_phy_fix_spur(struct rtw_dev *rtwdev, u8 channel, u8 bw)
{
 /* C cut Item12 ADC FIFO CLOCK */
 if (rtwdev->hal.cut_version == RTW_CHIP_VER_CUT_C) {
  if (bw == RTW_CHANNEL_WIDTH_40 && channel == 11)
   rtw_write32_mask(rtwdev, REG_ADCCLK, 0xC00, 0x3);
  else
   rtw_write32_mask(rtwdev, REG_ADCCLK, 0xC00, 0x2);

  /* A workaround to resolve 2480Mhz spur by setting ADC clock
 * as 160M.
 */

  if (bw == RTW_CHANNEL_WIDTH_20 && (channel == 13 || channel == 14)) {
   rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x3);
   rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 1);
  } else if (bw == RTW_CHANNEL_WIDTH_40 && channel == 11) {
   rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 1);
  } else if (bw != RTW_CHANNEL_WIDTH_80) {
   rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x2);
   rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0);
  }
 } else {
  /* A workaround to resolve 2480Mhz spur by setting ADC clock
 * as 160M.
 */

  if (bw == RTW_CHANNEL_WIDTH_20 && (channel == 13 || channel == 14))
   rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x3);
  else if (channel <= 14) /* 2.4G only */
   rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x2);
 }
}

static void rtw88xxa_switch_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw)
{
 struct rtw_hal *hal = &rtwdev->hal;
 u32 fc_area, rf_mod_ag;
 u8 path;

 switch (channel) {
 case 36 ... 48:
  fc_area = 0x494;
  break;
 case 50 ... 64:
  fc_area = 0x453;
  break;
 case 100 ... 116:
  fc_area = 0x452;
  break;
 default:
  if (channel >= 118)
   fc_area = 0x412;
  else
   fc_area = 0x96a;
  break;
 }

 rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, fc_area);

 for (path = 0; path < hal->rf_path_num; path++) {
  switch (channel) {
  case 36 ... 64:
   rf_mod_ag = 0x101;
   break;
  case 100 ... 140:
   rf_mod_ag = 0x301;
   break;
  default:
   if (channel > 140)
    rf_mod_ag = 0x501;
   else
    rf_mod_ag = 0x000;
   break;
  }

  rtw_write_rf(rtwdev, path, RF_CFGCH,
        RF18_RFSI_MASK | RF18_BAND_MASK, rf_mod_ag);

  if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
   rtw8812a_phy_fix_spur(rtwdev, channel, bw);

  rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_CHANNEL_MASK, channel);
 }
}

static void rtw88xxa_set_reg_bw(struct rtw_dev *rtwdev, u8 bw)
{
 u16 val16 = rtw_read16(rtwdev, REG_WMAC_TRXPTCL_CTL);

 val16 &= ~BIT_RFMOD;
 if (bw == RTW_CHANNEL_WIDTH_80)
  val16 |= BIT_RFMOD_80M;
 else if (bw == RTW_CHANNEL_WIDTH_40)
  val16 |= BIT_RFMOD_40M;

 rtw_write16(rtwdev, REG_WMAC_TRXPTCL_CTL, val16);
}

static void rtw88xxa_post_set_bw_mode(struct rtw_dev *rtwdev, u8 channel,
          u8 bw, u8 primary_chan_idx)
{
 struct rtw_hal *hal = &rtwdev->hal;
 u8 txsc40 = 0, txsc20, txsc;
 u8 reg_837, l1pkval;

 rtw88xxa_set_reg_bw(rtwdev, bw);

 txsc20 = primary_chan_idx;
 if (bw == RTW_CHANNEL_WIDTH_80) {
  if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST)
   txsc40 = RTW_SC_40_UPPER;
  else
   txsc40 = RTW_SC_40_LOWER;
 }

 txsc = BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40);
 rtw_write8(rtwdev, REG_DATA_SC, txsc);

 reg_837 = rtw_read8(rtwdev, REG_BWINDICATION + 3);

 switch (bw) {
 default:
 case RTW_CHANNEL_WIDTH_20:
  rtw_write32_mask(rtwdev, REG_ADCCLK, 0x003003C3, 0x00300200);
  rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0);

  if (hal->rf_type == RF_2T2R)
   rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, 7);
  else
   rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, 8);

  break;
 case RTW_CHANNEL_WIDTH_40:
  rtw_write32_mask(rtwdev, REG_ADCCLK, 0x003003C3, 0x00300201);
  rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0);
  rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3C, txsc);
  rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf0000000, txsc);

  if (reg_837 & BIT(2)) {
   l1pkval = 6;
  } else {
   if (hal->rf_type == RF_2T2R)
    l1pkval = 7;
   else
    l1pkval = 8;
  }

  rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, l1pkval);

  if (txsc == RTW_SC_20_UPPER)
   rtw_write32_set(rtwdev, REG_RXSB, BIT(4));
  else
   rtw_write32_clr(rtwdev, REG_RXSB, BIT(4));

  break;
 case RTW_CHANNEL_WIDTH_80:
  rtw_write32_mask(rtwdev, REG_ADCCLK, 0x003003C3, 0x00300202);
  rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 1);
  rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3C, txsc);
  rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf0000000, txsc);

  if (reg_837 & BIT(2)) {
   l1pkval = 5;
  } else {
   if (hal->rf_type == RF_2T2R)
    l1pkval = 6;
   else
    l1pkval = 7;
  }

  rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, l1pkval);

  break;
 }
}

static void rtw88xxa_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
{
 u8 path;

 for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
  switch (bw) {
  case RTW_CHANNEL_WIDTH_5:
  case RTW_CHANNEL_WIDTH_10:
  case RTW_CHANNEL_WIDTH_20:
  default:
   rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 3);
   break;
  case RTW_CHANNEL_WIDTH_40:
   rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 1);
   break;
  case RTW_CHANNEL_WIDTH_80:
   rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 0);
   break;
  }
 }
}

void rtw88xxa_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw,
     u8 primary_chan_idx)
{
 u8 old_band, new_band;

 if (rtw_read8(rtwdev, REG_CCK_CHECK) & BIT_CHECK_CCK_EN)
  old_band = RTW_BAND_5G;
 else
  old_band = RTW_BAND_2G;

 if (channel > 14)
  new_band = RTW_BAND_5G;
 else
  new_band = RTW_BAND_2G;

 if (new_band != old_band)
  rtw88xxa_switch_band(rtwdev, new_band, bw);

 rtw88xxa_switch_channel(rtwdev, channel, bw);

 rtw88xxa_post_set_bw_mode(rtwdev, channel, bw, primary_chan_idx);

 if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
  rtw8812a_phy_fix_spur(rtwdev, channel, bw);

 rtw88xxa_set_channel_rf(rtwdev, channel, bw);
}
EXPORT_SYMBOL(rtw88xxa_set_channel);

void rtw88xxa_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
          struct rtw_rx_pkt_stat *pkt_stat,
          s8 (*cck_rx_pwr)(u8 lna_idx, u8 vga_idx))
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 struct rtw_jaguar_phy_status_rpt *rpt;
 u8 gain[RTW_RF_PATH_MAX], rssi, i;
 s8 rx_pwr_db, power_a, power_b;
 const s8 min_rx_power = -120;
 u8 lna_idx, vga_idx;

 rpt = (struct rtw_jaguar_phy_status_rpt *)phy_status;

 if (pkt_stat->rate <= DESC_RATE11M) {
  lna_idx = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_LNA_IDX);
  vga_idx = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_VGA_IDX);

  rx_pwr_db = cck_rx_pwr(lna_idx, vga_idx);

  pkt_stat->rx_power[RF_PATH_A] = rx_pwr_db;
  pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
  dm_info->rssi[RF_PATH_A] = pkt_stat->rssi;
  pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
  pkt_stat->signal_power = rx_pwr_db;
 } else { /* OFDM rate */
  gain[RF_PATH_A] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_A);
  gain[RF_PATH_B] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_B);

  for (i = RF_PATH_A; i < rtwdev->hal.rf_path_num; i++) {
   pkt_stat->rx_power[i] = gain[i] - 110;
   rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[i], 1);
   dm_info->rssi[i] = rssi;
  }

  pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power,
        rtwdev->hal.rf_path_num);

  power_a = pkt_stat->rx_power[RF_PATH_A];
  power_b = pkt_stat->rx_power[RF_PATH_B];
  if (rtwdev->hal.rf_path_num == 1)
   power_b = power_a;

  pkt_stat->signal_power = max3(power_a, power_b, min_rx_power);
 }
}
EXPORT_SYMBOL(rtw88xxa_query_phy_status);

static void
rtw88xxa_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path,
        u8 rs, u32 *phy_pwr_idx)
{
 static const u32 offset_txagc[2] = {
  REG_TX_AGC_A_CCK_11_CCK_1, REG_TX_AGC_B_CCK_11_CCK_1
 };
 u8 rate, rate_idx, pwr_index, shift;
 struct rtw_hal *hal = &rtwdev->hal;
 bool write_1ss_mcs9;
 u32 mask;
 int j;

 for (j = 0; j < rtw_rate_size[rs]; j++) {
  rate = rtw_rate_section[rs][j];

  pwr_index = hal->tx_pwr_tbl[path][rate];

  shift = rate & 0x3;
  *phy_pwr_idx |= ((u32)pwr_index << (shift * 8));

  write_1ss_mcs9 = rate == DESC_RATEVHT1SS_MCS9 &&
     hal->rf_path_num == 1;

  if (write_1ss_mcs9)
   mask = MASKLWORD;
  else
   mask = MASKDWORD;

  if (shift == 0x3 || write_1ss_mcs9) {
   rate_idx = rate & 0xfc;
   if (rate >= DESC_RATEVHT1SS_MCS0)
    rate_idx -= 0x10;

   rtw_write32_mask(rtwdev, offset_txagc[path] + rate_idx,
      mask, *phy_pwr_idx);

   *phy_pwr_idx = 0;
  }
 }
}

static void rtw88xxa_tx_power_training(struct rtw_dev *rtwdev, u8 bw,
           u8 channel, u8 path)
{
 static const u32 write_offset[] = {
  REG_TX_PWR_TRAINING_A, REG_TX_PWR_TRAINING_B,
 };
 u32 power_level, write_data;
 u8 i;

 power_level = rtwdev->hal.tx_pwr_tbl[path][DESC_RATEMCS7];
 write_data = 0;

 for (i = 0; i < 3; i++) {
  if (i == 0)
   power_level -= 10;
  else if (i == 1)
   power_level -= 8;
  else
   power_level -= 6;

  write_data |= max_t(u32, power_level, 2) << (i * 8);
 }

 rtw_write32_mask(rtwdev, write_offset[path], 0xffffff, write_data);
}

void rtw88xxa_set_tx_power_index(struct rtw_dev *rtwdev)
{
 struct rtw_hal *hal = &rtwdev->hal;
 u32 phy_pwr_idx = 0;
 int rs, path;

 for (path = 0; path < hal->rf_path_num; path++) {
  for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) {
   if (hal->rf_path_num == 1 &&
       (rs == RTW_RATE_SECTION_HT_2S ||
        rs == RTW_RATE_SECTION_VHT_2S))
    continue;

   if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags) &&
       rs > RTW_RATE_SECTION_OFDM)
    continue;

   if (hal->current_band_type == RTW_BAND_5G &&
       rs == RTW_RATE_SECTION_CCK)
    continue;

   rtw88xxa_set_tx_power_index_by_rate(rtwdev, path, rs,
           &phy_pwr_idx);
  }

  rtw88xxa_tx_power_training(rtwdev, hal->current_band_width,
        hal->current_channel, path);
 }
}
EXPORT_SYMBOL(rtw88xxa_set_tx_power_index);

void rtw88xxa_false_alarm_statistics(struct rtw_dev *rtwdev)
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u32 cck_fa_cnt, ofdm_fa_cnt;
 u32 crc32_cnt, cca32_cnt;
 u32 cck_enable;

 cck_enable = rtw_read32(rtwdev, REG_RXPSEL) & BIT(28);
 cck_fa_cnt = rtw_read16(rtwdev, REG_FA_CCK);
 ofdm_fa_cnt = rtw_read16(rtwdev, REG_FA_OFDM);

 dm_info->cck_fa_cnt = cck_fa_cnt;
 dm_info->ofdm_fa_cnt = ofdm_fa_cnt;
 dm_info->total_fa_cnt = ofdm_fa_cnt;
 if (cck_enable)
  dm_info->total_fa_cnt += cck_fa_cnt;

 crc32_cnt = rtw_read32(rtwdev, REG_CRC_CCK);
 dm_info->cck_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
 dm_info->cck_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);

 crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM);
 dm_info->ofdm_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
 dm_info->ofdm_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);

 crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT);
 dm_info->ht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
 dm_info->ht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);

 crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT);
 dm_info->vht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
 dm_info->vht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);

 cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM);
 dm_info->ofdm_cca_cnt = u32_get_bits(cca32_cnt, MASKHWORD);
 dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt;
 if (cck_enable) {
  cca32_cnt = rtw_read32(rtwdev, REG_CCA_CCK);
  dm_info->cck_cca_cnt = u32_get_bits(cca32_cnt, MASKLWORD);
  dm_info->total_cca_cnt += dm_info->cck_cca_cnt;
 }

 rtw_write32_set(rtwdev, REG_FAS, BIT(17));
 rtw_write32_clr(rtwdev, REG_FAS, BIT(17));
 rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT, BIT(15));
 rtw_write32_set(rtwdev, REG_CCK0_FAREPORT, BIT(15));
 rtw_write32_set(rtwdev, REG_CNTRST, BIT(0));
 rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0));
}
EXPORT_SYMBOL(rtw88xxa_false_alarm_statistics);

void rtw88xxa_iqk_backup_mac_bb(struct rtw_dev *rtwdev,
    u32 *macbb_backup,
    const u32 *backup_macbb_reg,
    u32 macbb_num)
{
 u32 i;

 /* [31] = 0 --> Page C */
 rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);

 /* save MACBB default value */
 for (i = 0; i < macbb_num; i++)
  macbb_backup[i] = rtw_read32(rtwdev, backup_macbb_reg[i]);
}
EXPORT_SYMBOL(rtw88xxa_iqk_backup_mac_bb);

void rtw88xxa_iqk_backup_afe(struct rtw_dev *rtwdev, u32 *afe_backup,
        const u32 *backup_afe_reg, u32 afe_num)
{
 u32 i;

 /* [31] = 0 --> Page C */
 rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);

 /* Save AFE Parameters */
 for (i = 0; i < afe_num; i++)
  afe_backup[i] = rtw_read32(rtwdev, backup_afe_reg[i]);
}
EXPORT_SYMBOL(rtw88xxa_iqk_backup_afe);

void rtw88xxa_iqk_restore_mac_bb(struct rtw_dev *rtwdev,
     u32 *macbb_backup,
     const u32 *backup_macbb_reg,
     u32 macbb_num)
{
 u32 i;

 /* [31] = 0 --> Page C */
 rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);

 /* Reload MacBB Parameters */
 for (i = 0; i < macbb_num; i++)
  rtw_write32(rtwdev, backup_macbb_reg[i], macbb_backup[i]);
}
EXPORT_SYMBOL(rtw88xxa_iqk_restore_mac_bb);

void rtw88xxa_iqk_configure_mac(struct rtw_dev *rtwdev)
{
 /* [31] = 0 --> Page C */
 rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);

 rtw_write8(rtwdev, REG_TXPAUSE, 0x3f);
 rtw_write32_mask(rtwdev, REG_BCN_CTRL,
    (BIT_EN_BCN_FUNCTION << 8) | BIT_EN_BCN_FUNCTION, 0x0);

 /* RX ante off */
 rtw_write8(rtwdev, REG_RXPSEL, 0x00);

 /* CCA off */
 rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf, 0xc);

 /* CCK RX path off */
 rtw_write8(rtwdev, REG_CCK_RX + 3, 0xf);
}
EXPORT_SYMBOL(rtw88xxa_iqk_configure_mac);

bool rtw88xxa_iqk_finish(int average, int threshold,
    int *x_temp, int *y_temp, int *x, int *y,
    bool break_inner, bool break_outer)
{
 bool finish = false;
 int i, ii, dx, dy;

 for (i = 0; i < average; i++) {
  for (ii = i + 1; ii < average; ii++) {
   dx = abs_diff(x_temp[i] >> 21, x_temp[ii] >> 21);
   dy = abs_diff(y_temp[i] >> 21, y_temp[ii] >> 21);

   if (dx < threshold && dy < threshold) {
    *x = ((x_temp[i] >> 21) + (x_temp[ii] >> 21));
    *y = ((y_temp[i] >> 21) + (y_temp[ii] >> 21));

    *x /= 2;
    *y /= 2;

    finish = true;

    if (break_inner)
     break;
   }
  }

  if (finish && break_outer)
   break;
 }

 return finish;
}
EXPORT_SYMBOL(rtw88xxa_iqk_finish);

static void rtw88xxa_pwrtrack_set(struct rtw_dev *rtwdev, u8 tx_rate, u8 path)
{
 static const u32 reg_txscale[2] = { REG_TXSCALE_A, REG_TXSCALE_B };
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 u8 cck_swing_idx, ofdm_swing_idx;
 u8 pwr_tracking_limit;

 switch (tx_rate) {
 case DESC_RATE1M ... DESC_RATE11M:
  pwr_tracking_limit = 32;
  break;
 case DESC_RATE6M ... DESC_RATE48M:
 case DESC_RATEMCS3 ... DESC_RATEMCS4:
 case DESC_RATEMCS11 ... DESC_RATEMCS12:
 case DESC_RATEVHT1SS_MCS3 ... DESC_RATEVHT1SS_MCS4:
 case DESC_RATEVHT2SS_MCS3 ... DESC_RATEVHT2SS_MCS4:
  pwr_tracking_limit = 30;
  break;
 case DESC_RATE54M:
 case DESC_RATEMCS5 ... DESC_RATEMCS7:
 case DESC_RATEMCS13 ... DESC_RATEMCS15:
 case DESC_RATEVHT1SS_MCS5 ... DESC_RATEVHT1SS_MCS6:
 case DESC_RATEVHT2SS_MCS5 ... DESC_RATEVHT2SS_MCS6:
  pwr_tracking_limit = 28;
  break;
 case DESC_RATEMCS0 ... DESC_RATEMCS2:
 case DESC_RATEMCS8 ... DESC_RATEMCS10:
 case DESC_RATEVHT1SS_MCS0 ... DESC_RATEVHT1SS_MCS2:
 case DESC_RATEVHT2SS_MCS0 ... DESC_RATEVHT2SS_MCS2:
  pwr_tracking_limit = 34;
  break;
 case DESC_RATEVHT1SS_MCS7:
 case DESC_RATEVHT2SS_MCS7:
  pwr_tracking_limit = 26;
  break;
 default:
 case DESC_RATEVHT1SS_MCS8:
 case DESC_RATEVHT2SS_MCS8:
  pwr_tracking_limit = 24;
  break;
 case DESC_RATEVHT1SS_MCS9:
 case DESC_RATEVHT2SS_MCS9:
  pwr_tracking_limit = 22;
  break;
 }

 cck_swing_idx = dm_info->delta_power_index[path] + dm_info->default_cck_index;
 ofdm_swing_idx = dm_info->delta_power_index[path] + dm_info->default_ofdm_index;

 if (ofdm_swing_idx > pwr_tracking_limit) {
  if (path == RF_PATH_A)
   dm_info->txagc_remnant_cck = cck_swing_idx - pwr_tracking_limit;
  dm_info->txagc_remnant_ofdm[path] = ofdm_swing_idx - pwr_tracking_limit;

  ofdm_swing_idx = pwr_tracking_limit;
 } else if (ofdm_swing_idx == 0) {
  if (path == RF_PATH_A)
   dm_info->txagc_remnant_cck = cck_swing_idx;
  dm_info->txagc_remnant_ofdm[path] = ofdm_swing_idx;
 } else {
  if (path == RF_PATH_A)
   dm_info->txagc_remnant_cck = 0;
  dm_info->txagc_remnant_ofdm[path] = 0;
 }

 rtw_write32_mask(rtwdev, reg_txscale[path], GENMASK(31, 21),
    rtw88xxa_txscale_tbl[ofdm_swing_idx]);
}

void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
      void (*do_lck)(struct rtw_dev *rtwdev),
      void (*do_iqk)(struct rtw_dev *rtwdev))
{
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;
 struct rtw_hal *hal = &rtwdev->hal;
 struct rtw_swing_table swing_table;
 s8 remnant_pre[RTW_RF_PATH_MAX];
 u8 thermal_value, delta, path;
 bool need_iqk;

 rtw_phy_config_swing_table(rtwdev, &swing_table);

 if (rtwdev->efuse.thermal_meter[0] == 0xff) {
  pr_err_once("efuse thermal meter is 0xff\n");
  return;
 }

 thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00);

 rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);

 need_iqk = rtw_phy_pwrtrack_need_iqk(rtwdev);

 if (need_iqk && do_lck)
  do_lck(rtwdev);

 if (dm_info->pwr_trk_init_trigger)
  dm_info->pwr_trk_init_trigger = false;
 else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value,
         RF_PATH_A))
  goto iqk;

 delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);

 for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
  remnant_pre[path] = dm_info->txagc_remnant_ofdm[path];

  dm_info->delta_power_index[path] =
   rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table, path,
          RF_PATH_A, delta);

  if (dm_info->delta_power_index[path] !=
      dm_info->delta_power_index_last[path]) {
   dm_info->delta_power_index_last[path] =
    dm_info->delta_power_index[path];

   rtw88xxa_pwrtrack_set(rtwdev, dm_info->tx_rate, path);
  }
 }

 for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
  if (remnant_pre[path] != dm_info->txagc_remnant_ofdm[path]) {
   rtw_phy_set_tx_power_level(rtwdev,
         hal->current_channel);
   break;
  }
 }

iqk:
 if (need_iqk)
  do_iqk(rtwdev);
}
EXPORT_SYMBOL(rtw88xxa_phy_pwrtrack);

void rtw88xxa_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl)
{
 static const u8 pd[CCK_PD_LV_MAX] = {0x40, 0x83, 0xcd, 0xdd, 0xed};
 struct rtw_dm_info *dm_info = &rtwdev->dm_info;

 /* Override rtw_phy_cck_pd_lv_link(). It implements something
 * like type 2/3/4. We need type 1 here.
 */

 if (rtw_is_assoc(rtwdev)) {
  if (dm_info->min_rssi > 60) {
   new_lvl = CCK_PD_LV3;
  } else if (dm_info->min_rssi > 35) {
   new_lvl = CCK_PD_LV2;
  } else if (dm_info->min_rssi > 20) {
   if (dm_info->cck_fa_avg > 500)
    new_lvl = CCK_PD_LV2;
   else if (dm_info->cck_fa_avg < 250)
    new_lvl = CCK_PD_LV1;
   else
    return;
  } else {
   new_lvl = CCK_PD_LV1;
  }
 }

 rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d)\n",
  dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A], new_lvl);

 if (dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] == new_lvl)
  return;

 dm_info->cck_fa_avg = CCK_FA_AVG_RESET;
 dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] = new_lvl;

 rtw_write8(rtwdev, REG_CCK_PD_TH, pd[new_lvl]);
}
EXPORT_SYMBOL(rtw88xxa_phy_cck_pd_set);

MODULE_AUTHOR("Realtek Corporation");
MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a common code");
MODULE_LICENSE("Dual BSD/GPL");

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

¤ Dauer der Verarbeitung: 0.36 Sekunden  ¤

*© 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.