diff mbox series

[18/20] wifi: rtw88: Add rtw8821a.{c,h}

Message ID f25d00ab-4481-4540-956b-bc1028a501e1@gmail.com
State New
Headers show
Series wifi: rtw88: Add support for RTL8821AU and RTL8812AU | expand

Commit Message

Bitterblue Smith Aug. 11, 2024, 9:07 p.m. UTC
These contain all the logic for the RTL8821AU and RTL8812AU chips.

Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/rtw8821a.c | 4139 +++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw8821a.h |  385 ++
 2 files changed, 4524 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821a.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821a.h

Comments

Ping-Ke Shih Aug. 16, 2024, 6:06 a.m. UTC | #1
Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> These contain all the logic for the RTL8821AU and RTL8812AU chips.
> 
> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
> ---
>  drivers/net/wireless/realtek/rtw88/rtw8821a.c | 4139 +++++++++++++++++
>  drivers/net/wireless/realtek/rtw88/rtw8821a.h |  385 ++
>  2 files changed, 4524 insertions(+)
>  create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821a.c
>  create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821a.h
> 
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> new file mode 100644
> index 000000000000..e72599db74ed
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
> @@ -0,0 +1,4139 @@
> +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
> +/* Copyright(c) 2018-2019  Realtek Corporation

Year 2024

[...]

> +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, 0x8bc, 0xc0000060, 0x4);

#define REG_RX_MCS_LIMIT 0x8bc

The setting value is weird to me though. 

> +
> +       /* RF Path_B HSSI OFF */
> +       rtw_write32_mask(rtwdev, 0xe00, 0xf, 0x4);

#define REG_HSSI_WRITE_B 	0xe00

> +
> +       /* 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 u8 rtw8821a_get_swing_index(struct rtw_dev *rtwdev)
> +{
> +       u32 swing, table_value;
> +       u8 i = 0;

no need '= 0'

> +
> +       swing = rtw8821a_get_bb_swing(rtwdev, rtwdev->hal.current_band_type,
> +                                     RF_PATH_A);
> +
> +       for (i = 0; i < ARRAY_SIZE(rtw8821a_txscale_tbl); i++) {
> +               table_value = rtw8821a_txscale_tbl[i];
> +               if (swing == table_value)
> +                       break;

return i;

> +       }
> +
> +       return i;

return ARRAY_SIZE(rtw8821a_txscale_tbl)?  (but I don't like it so much)

or

return 24;

Move checking statement from callers to here. 

> +}
> +
> +static void rtw8821a_pwrtrack_init(struct rtw_dev *rtwdev)
> +{
> +       struct rtw_dm_info *dm_info = &rtwdev->dm_info;
> +       u8 ofdm_swing_idx;
> +       u8 path;
> +
> +       ofdm_swing_idx = rtw8821a_get_swing_index(rtwdev);
> +
> +       if (ofdm_swing_idx >= ARRAY_SIZE(rtw8821a_txscale_tbl))
> +               dm_info->default_ofdm_index = 24;
> +       else
> +               dm_info->default_ofdm_index = ofdm_swing_idx;

If rtw8821a_get_swing_index(rtwdev) returns 24 for the case we can't find a 
match from rtw8821a_txscale_tbl[], here can be simply

    dm_info->default_ofdm_index = rtw8821a_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;
> +}
> +

[...]

> +static int rtw8821a_power_on(struct rtw_dev *rtwdev)
> +{

Will the coming RTL8814AU share this flow? If so, we can move this power on
to main.c/mac.c as rtw_power_on_v1() or something else. 

If we decide moving power on flow into chip specific files, the duplicate code
will become more and more. Please think about this. 


> +       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.
> +        */
> +       rtw8812au_hw_reset(rtwdev);
> +
> +       ret = rtw8812au_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 = rtw8821a_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);
> +
> +       rtw8821au_init_queue_reserved_page(rtwdev);
> +       rtw8821au_init_tx_buffer_boundary(rtwdev);
> +       rtw8821au_init_queue_priority(rtwdev);
> +

Seemingly above flow looks common. Can it share with other chips?

[...]

> +}
> +
> +static u32 rtw8821a_phy_read_rf(struct rtw_dev *rtwdev,
> +                               enum rtw_rf_path rf_path, u32 addr, u32 mask)
> +{

read/write RF functions are also common for chips. Can it share with coming RTL8814A?
Move this to phy.c as v1?

> +       static const u32 pi_addr[2] = { 0xc00, 0xe00 };
> +       static const u32 read_addr[2][2] = {
> +               { REG_SI_READ_A, REG_SI_READ_B },
> +               { REG_PI_READ_A, REG_PI_READ_B }
> +       };

[...]

> +
> +static void rtw8821a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
> +                                     struct rtw_rx_pkt_stat *pkt_stat)
> +{
> +       struct rtw_dm_info *dm_info = &rtwdev->dm_info;
> +       struct rtw8821a_phy_status_rpt *phy_sts;
> +       u8 lna_idx, vga_idx, cck_agc_rpt;
> +       s8 rx_pwr_db, power_a, power_b;
> +       const s8 min_rx_power = -120;
> +       u8 rssi, val, i;
> +
> +       phy_sts = (struct rtw8821a_phy_status_rpt *)phy_status;
> +
> +       if (pkt_stat->rate <= DESC_RATE11M) {
> +               cck_agc_rpt = phy_sts->cfosho[0];
> +               lna_idx = (cck_agc_rpt & 0xE0) >> 5;
> +               vga_idx = cck_agc_rpt & 0x1F;

If we remove "#ifdef __LITTLE_ENDIAN" from rtw8821a_phy_status_rpt and define
bit mask there, additionally define these bit masks and then use u8_get_bits().

By the way, shouldn't the field of 'cfosho[2]' be 'u8' instead of 's8'?

> +
> +               if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
> +                       rx_pwr_db = rtw8821a_cck_rx_pwr(rtwdev, lna_idx, vga_idx);
> +               else
> +                       rx_pwr_db = rtw8812a_cck_rx_pwr(rtwdev, 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;
> +
> +               if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A &&
> +                   !rtwdev->hal.cck_high_power) {
> +                       if (pkt_stat->rssi >= 80)
> +                               pkt_stat->rssi = ((pkt_stat->rssi - 80) << 1) +
> +                                                ((pkt_stat->rssi - 80) >> 1) + 80;
> +                       else if (pkt_stat->rssi <= 78 && pkt_stat->rssi >= 20)
> +                               pkt_stat->rssi += 3;
> +               }
> +       } else { /* OFDM rate */
> +               for (i = RF_PATH_A; i < rtwdev->hal.rf_path_num; i++) {
> +                       val = phy_sts->gain_trsw[i];
> +                       pkt_stat->rx_power[i] = (val & 0x7F) - 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);
> +       }
> +}
> +
> +static void rtw8821a_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc,
> +                                  struct rtw_rx_pkt_stat *pkt_stat,
> +                                  struct ieee80211_rx_status *rx_status)
> +{
> +       u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz;
> +       struct ieee80211_hdr *hdr;
> +       u8 *phy_status = NULL;
> +
> +       memset(pkt_stat, 0, sizeof(*pkt_stat));
> +
> +       pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc);
> +       pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc);
> +       pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc);
> +       pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) &&
> +                             GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE;
> +       pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc);
> +       pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc);
> +       pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc);
> +       pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc);
> +       pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc);
> +       pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc);
> +       pkt_stat->ppdu_cnt = 0;
> +       pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc);
> +       pkt_stat->bw = GET_RX_DESC_BW(rx_desc);

More and more chips use these macros. Please add a patch using struct to 
access these fields. More, query_rx_desc() are very similar across chips,
please move them to mac.c or phy.c as a common function.

> +
> +       /* drv_info_sz is in unit of 8-bytes */
> +       pkt_stat->drv_info_sz *= 8;
> +
> +       /* c2h cmd pkt's rx/phy status is not interested */
> +       if (pkt_stat->is_c2h)
> +               return;
> +
> +       hdr = (struct ieee80211_hdr *)(rx_desc + desc_sz + pkt_stat->shift +
> +                                      pkt_stat->drv_info_sz);
> +       if (pkt_stat->phy_status) {
> +               phy_status = rx_desc + desc_sz + pkt_stat->shift;
> +               rtw8821a_query_phy_status(rtwdev, phy_status, pkt_stat);
> +       }
> +
> +       rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status);
> +}
> +

[...]

> +
> +static void rtw8821a_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 = FIELD_GET(GENMASK(15, 0), crc32_cnt);
> +       dm_info->cck_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);

use u32_get_bits() instead.

> +
> +       crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM);
> +       dm_info->ofdm_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt);
> +       dm_info->ofdm_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);
> +
> +       crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT);
> +       dm_info->ht_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt);
> +       dm_info->ht_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);
> +
> +       crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT);
> +       dm_info->vht_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt);
> +       dm_info->vht_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);
> +
> +       cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM);
> +       dm_info->ofdm_cca_cnt = FIELD_GET(GENMASK(31, 16), cca32_cnt);
> +       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 = FIELD_GET(GENMASK(15, 0), cca32_cnt);
> +               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_RXDESC, BIT(15));
> +       rtw_write32_set(rtwdev, REG_RXDESC, BIT(15));
> +       rtw_write32_set(rtwdev, REG_CNTRST, BIT(0));
> +       rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0));
> +}
> +

[...]

> +
> +static void rtw8821a_iqk_restore_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);
> +
> +       /* Reload AFE Parameters */
> +       for (i = 0; i < afe_num; i++)
> +               rtw_write32(rtwdev, backup_afe_reg[i], afe_backup[i]);
> +
> +       /* [31] = 1 --> Page C1 */
> +       rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
> +
> +       rtw_write32(rtwdev, 0xc80, 0x0);
> +       rtw_write32(rtwdev, 0xc84, 0x0);
> +       rtw_write32(rtwdev, 0xc88, 0x0);
> +       rtw_write32(rtwdev, 0xc8c, 0x3c000000);
> +       rtw_write32(rtwdev, REG_LSSI_WRITE_A, 0x00000080);
> +       rtw_write32(rtwdev, 0xc94, 0x00000000);
> +       rtw_write32(rtwdev, 0xcc4, 0x20040000);
> +       rtw_write32(rtwdev, 0xcc8, 0x20000000);
> +       rtw_write32(rtwdev, REG_RFECTL_A, 0x0);

I think we can reuse existing definitions:

rtw8723x.h:#define REG_OFDM_0_XA_TX_IQ_IMBALANCE        0x0c80
rtw8703b.h:#define REG_OFDM0_A_TX_AFE 0x0c84
rtw8723x.h:#define REG_OFDM_0_XB_TX_IQ_IMBALANCE        0x0c88

#define REG_TSSI_TRK_SW 0xc8c

rtw8821a.h:#define REG_TXAGCIDX                         0xc94

#define REG_IQK_DPD_CFG 0xcc4
#define REG_CFG_PMPD 0xcc8

[...]

> +
> +static void rtw8821a_iqk_tx_fill(struct rtw_dev *rtwdev,
> +                                unsigned int tx_x, unsigned int tx_y)
> +{
> +       /* [31] = 1 --> Page C1 */
> +       rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
> +
> +       rtw_write32(rtwdev, REG_LSSI_WRITE_A, 0x00000080);
> +       rtw_write32(rtwdev, 0xcc4, 0x20040000);
> +       rtw_write32(rtwdev, 0xcc8, 0x20000000);
> +       rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, tx_y);
> +       rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, tx_x);

#define REG_IQC_Y 0xccc
#define REG_IQC_X 0xcd4

> +}
> +
> +static void rtw8821a_iqk_tx_vdf_true(struct rtw_dev *rtwdev, u32 cal,
> +                                    bool *tx0iqkok,
> +                                    int tx_x0[CAL_NUM_8821A],
> +                                    int tx_y0[CAL_NUM_8821A])
> +{
> +       u32 cal_retry, delay_count, iqk_ready, tx_fail;
> +       int tx_dt[3], vdf_y[3], vdf_x[3];
> +       int k;
> +
> +       for (k = 0; k <= 2; k++) {

'< 3' would be more intuitive, because 'k' is index of array.

> +               switch (k) {
> +               case 0:
> +                       /* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
> +                       rtw_write32(rtwdev, 0xc80, 0x18008c38);
> +                       /* RX_Tone_idx[9:0], RxK_Mask[29] */
> +                       rtw_write32(rtwdev, 0xc84, 0x38008c38);
> +                       rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x0);
> +                       break;
> +               case 1:
> +                       rtw_write32_mask(rtwdev, 0xc80, BIT(28), 0x0);
> +                       rtw_write32_mask(rtwdev, 0xc84, BIT(28), 0x0);
> +                       rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x0);
> +                       break;
> +               case 2:
> +                       rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                               "vdf_y[1] = %x vdf_y[0] = %x\n",
> +                               vdf_y[1] >> 21 & 0x00007ff,
> +                               vdf_y[0] >> 21 & 0x00007ff);
> +
> +                       rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                               "vdf_x[1] = %x vdf_x[0] = %x\n",
> +                               vdf_x[1] >> 21 & 0x00007ff,
> +                               vdf_x[0] >> 21 & 0x00007ff);
> +
> +                       tx_dt[cal] = (vdf_y[1] >> 20) - (vdf_y[0] >> 20);
> +                       tx_dt[cal] = (16 * tx_dt[cal]) * 10000 / 15708;
> +                       tx_dt[cal] = (tx_dt[cal] >> 1) + (tx_dt[cal] & BIT(0));
> +
> +                       /* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
> +                       rtw_write32(rtwdev, 0xc80, 0x18008c20);
> +                       /* RX_Tone_idx[9:0], RxK_Mask[29] */
> +                       rtw_write32(rtwdev, 0xc84, 0x38008c20);
> +                       rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x1);
> +                       rtw_write32_mask(rtwdev, 0xce8, 0x3fff0000,
> +                                        tx_dt[cal] & 0x00003fff);
> +                       break;
> +               }
> +
> +               rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
> +               cal_retry = 0;
> +               while (1) {

Can we replace 'while()' by 'for (cal_retry = 0; cal_retry < 10; cal_retry++)'?

> +                       /* one shot */
> +                       rtw_write32(rtwdev, 0x980, 0xfa000000);
> +                       rtw_write32(rtwdev, 0x980, 0xf8000000);
> +
> +                       mdelay(10);
> +
> +                       rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
> +
> +                       delay_count = 0;
> +                       while (1) {

Similarly. 'for (delay_count = 0; delay_count < 20; delay_count++)'.

> +                               iqk_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
> +
> +                               /* Originally: if (~iqk_ready || delay_count > 20)
> +                                * that looks like a typo so make it more explicit
> +                                */
> +                               iqk_ready = true;
> +
> +                               if (iqk_ready || delay_count > 20)
> +                                       break;
> +
> +                               mdelay(1);
> +                               delay_count++;
> +                       }
> +
> +                       if (delay_count < 20) {
> +                               /* ============TXIQK Check============== */
> +                               tx_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(12));
> +
> +                               /* Originally: if (~tx_fail) {
> +                                * It looks like a typo, so make it more explicit.
> +                                */
> +                               tx_fail = false;
> +
> +                               if (!tx_fail) {
> +                                       rtw_write32(rtwdev, REG_RFECTL_A,
> +                                                   0x02000000);
> +                                       vdf_x[k] = rtw_read32_mask(rtwdev, 0xd00,
> +                                                                  0x07ff0000);
> +                                       vdf_x[k] <<= 21;
> +
> +                                       rtw_write32(rtwdev, REG_RFECTL_A,
> +                                                   0x04000000);
> +                                       vdf_y[k] = rtw_read32_mask(rtwdev, 0xd00,
> +                                                                  0x07ff0000);
> +                                       vdf_y[k] <<= 21;
> +
> +                                       *tx0iqkok = true;
> +                                       break;
> +                               }
> +
> +                               rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, 0x0);
> +                               rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, 0x200);
> +
> +                               *tx0iqkok = false;
> +                               cal_retry++;
> +                               if (cal_retry == 10)
> +                                       break;
> +                       } else { /* If 20ms No Result, then cal_retry++ */
> +                               *tx0iqkok = false;
> +                               cal_retry++;
> +                               if (cal_retry == 10)
> +                                       break;
> +                       }
> +               }
> +       }
> +
> +       if (k == 3) {
> +               tx_x0[cal] = vdf_x[k - 1];
> +               tx_y0[cal] = vdf_y[k - 1];
> +       }
> +}
> +
> +static void rtw8821a_iqk_tx_vdf_false(struct rtw_dev *rtwdev, u32 cal,
> +                                     bool *tx0iqkok,
> +                                     int tx_x0[CAL_NUM_8821A],
> +                                     int tx_y0[CAL_NUM_8821A])
> +{
> +       u32 cal_retry, delay_count, iqk_ready, tx_fail;
> +
> +       /* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
> +       rtw_write32(rtwdev, 0xc80, 0x18008c10);
> +       /* RX_Tone_idx[9:0], RxK_Mask[29] */
> +       rtw_write32(rtwdev, 0xc84, 0x38008c10);
> +       rtw_write32(rtwdev, 0xcb8, 0x00100000);
> +
> +       cal_retry = 0;
> +       while (1) {

Ditto. 'for (cal_retry = 0; cal_retry < 10; cal_retry++)'

> +               /* one shot */
> +               rtw_write32(rtwdev, 0x980, 0xfa000000);
> +               rtw_write32(rtwdev, 0x980, 0xf8000000);
> +
> +               mdelay(10);
> +               rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
> +
> +               delay_count = 0;
> +               while (1) {

'for (delay_count = 0; delay_count < 20; delay_count++)'.

> +                       iqk_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
> +
> +                       /* Originally: if (~iqk_ready || delay_count > 20)
> +                        * that looks like a typo so make it more explicit
> +                        */
> +                       iqk_ready = true;
> +
> +                       if (iqk_ready || delay_count > 20)
> +                               break;
> +
> +                       mdelay(1);
> +                       delay_count++;
> +               }
> +
> +               if (delay_count < 20) {
> +                       /* ============TXIQK Check============== */
> +                       tx_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(12));
> +
> +                       /* Originally: if (~tx_fail) {
> +                        * It looks like a typo, so make it more explicit.
> +                        */
> +                       tx_fail = false;
> +
> +                       if (!tx_fail) {
> +                               rtw_write32(rtwdev, REG_RFECTL_A, 0x02000000);
> +                               tx_x0[cal] = rtw_read32_mask(rtwdev, 0xd00,
> +                                                            0x07ff0000);
> +                               tx_x0[cal] <<= 21;
> +
> +                               rtw_write32(rtwdev, REG_RFECTL_A, 0x04000000);
> +                               tx_y0[cal] = rtw_read32_mask(rtwdev, 0xd00,
> +                                                            0x07ff0000);
> +                               tx_y0[cal] <<= 21;
> +
> +                               *tx0iqkok = true;
> +                               break;
> +                       }
> +
> +                       rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, 0x0);
> +                       rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, 0x200);
> +
> +                       *tx0iqkok = false;
> +                       cal_retry++;
> +                       if (cal_retry == 10)
> +                               break;
> +               } else { /* If 20ms No Result, then cal_retry++ */
> +                       *tx0iqkok = false;
> +                       cal_retry++;
> +                       if (cal_retry == 10)
> +                               break;
> +               }
> +       }
> +}
> +
> +static void rtw8821a_iqk_rx(struct rtw_dev *rtwdev, u32 cal, bool *rx0iqkok,
> +                           int rx_x0[CAL_NUM_8821A],
> +                           int rx_y0[CAL_NUM_8821A])
> +{
> +       u32 cal_retry, delay_count, iqk_ready, rx_fail;
> +
> +       rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
> +
> +       cal_retry = 0;
> +       while (1) {

'for (cal_retry = 0; cal_retry < 10; cal_retry++)'

> +               /* one shot */
> +               rtw_write32(rtwdev, 0x980, 0xfa000000);
> +               rtw_write32(rtwdev, 0x980, 0xf8000000);
> +
> +               mdelay(10);
> +
> +               rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
> +
> +               delay_count = 0;
> +               while (1) {

'for (delay_count = 0; delay_count < 20; delay_count++)'.

> +                       iqk_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
> +
> +                       /* Originally: if (~iqk_ready || delay_count > 20)
> +                        * that looks like a typo so make it more explicit
> +                        */
> +                       iqk_ready = true;
> +
> +                       if (iqk_ready || delay_count > 20)
> +                               break;
> +
> +                       mdelay(1);
> +                       delay_count++;
> +               }
> +
> +               if (delay_count < 20) {
> +                       /* ============RXIQK Check============== */
> +                       rx_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(11));
> +                       if (!rx_fail) {
> +                               rtw_write32(rtwdev, REG_RFECTL_A, 0x06000000);
> +                               rx_x0[cal] = rtw_read32_mask(rtwdev, 0xd00,
> +                                                            0x07ff0000);
> +                               rx_x0[cal] <<= 21;
> +
> +                               rtw_write32(rtwdev, REG_RFECTL_A, 0x08000000);
> +                               rx_y0[cal] = rtw_read32_mask(rtwdev, 0xd00,
> +                                                            0x07ff0000);
> +                               rx_y0[cal] <<= 21;
> +
> +                               *rx0iqkok = true;
> +                               break;
> +                       }
> +
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A,
> +                                        0x000003ff, 0x200 >> 1);
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A,
> +                                        0x03ff0000, 0x0 >> 1);
> +
> +                       *rx0iqkok = false;
> +                       cal_retry++;
> +                       if (cal_retry == 10)
> +                               break;
> +               } else { /* If 20ms No Result, then cal_retry++ */
> +                       *rx0iqkok = false;
> +                       cal_retry++;
> +                       if (cal_retry == 10)
> +                               break;
> +               }
> +       }
> +}
> +
> +static bool rtw8821a_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;
> +}
> +
> +static void rtw8821a_iqk(struct rtw_dev *rtwdev)
> +{
> +       int tx_average = 0, rx_average = 0, rx_iqk_loop = 0;
> +       const struct rtw_efuse *efuse = &rtwdev->efuse;
> +       int tx_x = 0, tx_y = 0, rx_x = 0, rx_y = 0;
> +       const struct rtw_hal *hal = &rtwdev->hal;
> +       bool tx0iqkok = false, rx0iqkok = false;
> +       int rx_x_temp = 0, rx_y_temp = 0;
> +       int rx_x0[2][CAL_NUM_8821A];
> +       int rx_y0[2][CAL_NUM_8821A];
> +       int tx_x0[CAL_NUM_8821A];
> +       int tx_y0[CAL_NUM_8821A];
> +       bool rx_finish1 = false;
> +       bool rx_finish2 = false;
> +       bool vdf_enable;
> +       u32 cal = 0;
> +       int i;
> +
> +       rtw_dbg(rtwdev, RTW_DBG_RFK,
> +               "band_width = %d, ext_pa = %d, ext_pa_5g = %d\n",
> +               hal->current_band_width, efuse->ext_pa_2g, efuse->ext_pa_5g);
> +
> +       vdf_enable = hal->current_band_width == RTW_CHANNEL_WIDTH_80;
> +
> +       while (cal < CAL_NUM_8821A) {

for (cal = 0; cal < CAL_NUM_8821A; cal++)

[...]


> +static void rtw8812a_iqk_rx_fill(struct rtw_dev *rtwdev, enum rtw_rf_path path,
> +                                unsigned int rx_x, unsigned int rx_y)
> +{
> +       switch (path) {
> +       case RF_PATH_A: {

no need brace for 'case:'.

> +               /* [31] = 0 --> Page C */
> +               rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
> +               if (rx_x >> 1 >= 0x112 || (rx_y >> 1 >= 0x12 && rx_y >> 1 <= 0x3ee)) {
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x000003ff, 0x100);
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x03ff0000, 0);
> +               } else {
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x000003ff, rx_x >> 1);
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x03ff0000, rx_y >> 1);
> +               }
> +               rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                       "rx_x = %x;;rx_y = %x ====>fill to IQC\n",
> +                       rx_x >> 1 & 0x000003ff, rx_y >> 1 & 0x000003ff);
> +               rtw_dbg(rtwdev, RTW_DBG_RFK, "0xc10 = %x ====>fill to IQC\n",
> +                       rtw_read32(rtwdev, REG_RX_IQC_AB_A));
> +       } break;
> +       case RF_PATH_B: {

ditto.

> +               /* [31] = 0 --> Page C */
> +               rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
> +               if (rx_x >> 1 >= 0x112 || (rx_y >> 1 >= 0x12 && rx_y >> 1 <= 0x3ee)) {
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x000003ff, 0x100);
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x03ff0000, 0);
> +               } else {
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x000003ff, rx_x >> 1);
> +                       rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x03ff0000, rx_y >> 1);
> +               }
> +               rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                       "rx_x = %x;;rx_y = %x ====>fill to IQC\n",
> +                       rx_x >> 1 & 0x000003ff, rx_y >> 1 & 0x000003ff);
> +               rtw_dbg(rtwdev, RTW_DBG_RFK, "0xe10 = %x====>fill to IQC\n",
> +                       rtw_read32(rtwdev, REG_RX_IQC_AB_B));
> +       } break;
> +       default:
> +               break;
> +       };

no need semicolon 

> +}
> +
> +static void rtw8812a_iqk_tx_fill(struct rtw_dev *rtwdev, enum rtw_rf_path path,
> +                                unsigned int tx_x, unsigned int tx_y)
> +{
> +       switch (path) {
> +       case RF_PATH_A: {

no need brace

> +               /* [31] = 1 --> Page C1 */
> +               rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
> +               rtw_write32_mask(rtwdev, 0xc90, BIT(7), 0x1);
> +               rtw_write32_mask(rtwdev, 0xcc4, BIT(18), 0x1);
> +               rtw_write32_mask(rtwdev, 0xcc4, BIT(29), 0x1);
> +               rtw_write32_mask(rtwdev, 0xcc8, BIT(29), 0x1);
> +               rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, tx_y);
> +               rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, tx_x);
> +               rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                       "tx_x = %x;;tx_y = %x =====> fill to IQC\n",
> +                       tx_x & 0x000007ff, tx_y & 0x000007ff);
> +               rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                       "0xcd4 = %x;;0xccc = %x ====>fill to IQC\n",
> +                       rtw_read32_mask(rtwdev, 0xcd4, 0x000007ff),
> +                       rtw_read32_mask(rtwdev, 0xccc, 0x000007ff));
> +       } break;
> +       case RF_PATH_B: {

ditto. 

> +               /* [31] = 1 --> Page C1 */
> +               rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
> +               rtw_write32_mask(rtwdev, 0xe90, BIT(7), 0x1);
> +               rtw_write32_mask(rtwdev, 0xec4, BIT(18), 0x1);
> +               rtw_write32_mask(rtwdev, 0xec4, BIT(29), 0x1);
> +               rtw_write32_mask(rtwdev, 0xec8, BIT(29), 0x1);
> +               rtw_write32_mask(rtwdev, 0xecc, 0x000007ff, tx_y);
> +               rtw_write32_mask(rtwdev, 0xed4, 0x000007ff, tx_x);
> +               rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                       "tx_x = %x;;tx_y = %x =====> fill to IQC\n",
> +                       tx_x & 0x000007ff, tx_y & 0x000007ff);
> +               rtw_dbg(rtwdev, RTW_DBG_RFK,
> +                       "0xed4 = %x;;0xecc = %x ====>fill to IQC\n",
> +                       rtw_read32_mask(rtwdev, 0xed4, 0x000007ff),
> +                       rtw_read32_mask(rtwdev, 0xecc, 0x000007ff));
> +       } break;
> +       default:
> +               break;
> +       };

no need semicolon

> +}
> +
> +static void rtw8812a_iqk(struct rtw_dev *rtwdev)
> +{
> +       int tx_x0_temp[10], tx_y0_temp[10], tx_x1_temp[10], tx_y1_temp[10];
> +       int rx_x0_temp[10], rx_y0_temp[10], rx_x1_temp[10], rx_y1_temp[10];
> +       bool iqk0_ready = false, tx0_finish = false, rx0_finish = false;
> +       bool iqk1_ready = false, tx1_finish = false, rx1_finish = false;
> +       u8 tx0_avg = 0, tx1_avg = 0, rx0_avg = 0, rx1_avg = 0;
> +       struct rtw_efuse *efuse = &rtwdev->efuse;
> +       bool tx0_fail = true, rx0_fail = true;
> +       bool tx1_fail = true, rx1_fail = true;
> +       int tx_x0, tx_y0, tx_x1, tx_y1;
> +       int rx_x0, rx_y0, rx_x1, rx_y1;
> +       u8 cal0_retry, cal1_retry;
> +       u8 delay_count;
> +
> +       /* [31] = 0 --> Page C */
> +       rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
> +
> +       /* ========path-A AFE all on======== */
> +       /* Port 0 DAC/ADC on */
> +       rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x77777777);
> +       rtw_write32(rtwdev, REG_AFE_PWR2_A, 0x77777777);
> +
> +       /* Port 1 DAC/ADC on */
> +       rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x77777777);
> +       rtw_write32(rtwdev, REG_AFE_PWR2_B, 0x77777777);
> +
> +       rtw_write32(rtwdev, REG_RX_WAIT_CCA_TX_CCK_RFON_A, 0x19791979);
> +       rtw_write32(rtwdev, REG_RX_WAIT_CCA_TX_CCK_RFON_B, 0x19791979);
> +
> +       /* hardware 3-wire off */
> +       rtw_write32_mask(rtwdev, 0xc00, 0xf, 0x4);
> +       rtw_write32_mask(rtwdev, 0xe00, 0xf, 0x4);
> +
> +       /* DAC/ADC sampling rate (160 MHz) */
> +       rtw_write32_mask(rtwdev, 0xc5c, BIT(26) | BIT(25) | BIT(24), 0x7);
> +       rtw_write32_mask(rtwdev, 0xe5c, BIT(26) | BIT(25) | BIT(24), 0x7);
> +
> +       /* [31] = 0 --> Page C */
> +       rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
> +       /* ====== path A TX IQK RF setting ====== */
> +       rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80002);
> +       rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x20000);
> +       rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x3fffd);
> +       rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xfe83f);
> +       rtw_write_rf(rtwdev, RF_PATH_A, 0x65, RFREG_MASK, 0x931d5);
> +       rtw_write_rf(rtwdev, RF_PATH_A, 0x8f, RFREG_MASK, 0x8a001);
> +
> +       /* ====== path B TX IQK RF setting ====== */
> +       rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE, RFREG_MASK, 0x80002);
> +       rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x20000);
> +       rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x3fffd);
> +       rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xfe83f);
> +       rtw_write_rf(rtwdev, RF_PATH_B, 0x65, RFREG_MASK, 0x931d5);
> +       rtw_write_rf(rtwdev, RF_PATH_B, 0x8f, RFREG_MASK, 0x8a001);
> +
> +       rtw_write32(rtwdev, 0x90c, 0x00008000);
> +       rtw_write32_mask(rtwdev, 0xc94, BIT(0), 0x1);
> +       rtw_write32_mask(rtwdev, 0xe94, BIT(0), 0x1);
> +       rtw_write32(rtwdev, 0x978, 0x29002000); /* TX (X,Y) */
> +       rtw_write32(rtwdev, 0x97c, 0xa9002000); /* RX (X,Y) */
> +       rtw_write32(rtwdev, 0x984, 0x00462910); /* [0]:AGC_en, [15]:idac_K_Mask */
> +       /* [31] = 1 --> Page C1 */
> +       rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
> +
> +       if (efuse->ext_pa_5g) {
> +               if (efuse->rfe_option == 1) {
> +                       rtw_write32(rtwdev, 0xc88, 0x821403e3);
> +                       rtw_write32(rtwdev, 0xe88, 0x821403e3);
> +               } else {
> +                       rtw_write32(rtwdev, 0xc88, 0x821403f7);
> +                       rtw_write32(rtwdev, 0xe88, 0x821403f7);
> +               }
> +       } else {
> +               rtw_write32(rtwdev, 0xc88, 0x821403f1);
> +               rtw_write32(rtwdev, 0xe88, 0x821403f1);
> +       }
> +
> +       if (rtwdev->hal.current_band_type == RTW_BAND_5G) {
> +               rtw_write32(rtwdev, 0xc8c, 0x68163e96);
> +               rtw_write32(rtwdev, 0xe8c, 0x68163e96);
> +       } else {
> +               rtw_write32(rtwdev, 0xc8c, 0x28163e96);
> +               rtw_write32(rtwdev, 0xe8c, 0x28163e96);
> +
> +               if (efuse->rfe_option == 3) {
> +                       if (efuse->ext_pa_2g)
> +                               rtw_write32(rtwdev, 0xc88, 0x821403e3);
> +                       else
> +                               rtw_write32(rtwdev, 0xc88, 0x821403f7);
> +               }
> +       }
> +
> +       /* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
> +       rtw_write32(rtwdev, 0xc80, 0x18008c10);
> +       /* RX_Tone_idx[9:0], RxK_Mask[29] */
> +       rtw_write32(rtwdev, 0xc84, 0x38008c10);
> +       rtw_write32(rtwdev, 0xce8, 0x00000000);
> +       /* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
> +       rtw_write32(rtwdev, 0xe80, 0x18008c10);
> +       /* RX_Tone_idx[9:0], RxK_Mask[29] */
> +       rtw_write32(rtwdev, 0xe84, 0x38008c10);
> +       rtw_write32(rtwdev, 0xee8, 0x00000000);
> +
> +       cal0_retry = 0;
> +       cal1_retry = 0;
> +       while (1) {
> +               /* one shot */
> +               rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
> +               rtw_write32(rtwdev, REG_RFECTL_B, 0x00100000);
> +               rtw_write32(rtwdev, 0x980, 0xfa000000);
> +               rtw_write32(rtwdev, 0x980, 0xf8000000);
> +
> +               mdelay(10);
> +
> +               rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
> +               rtw_write32(rtwdev, REG_RFECTL_B, 0x00000000);
> +
> +               delay_count = 0;
> +               while (1) {

prefer 'for' 

> +                       if (!tx0_finish)
> +                               iqk0_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
> +                       if (!tx1_finish)
> +                               iqk1_ready = rtw_read32_mask(rtwdev, 0xd40, BIT(10));
> +                       if ((iqk0_ready && iqk1_ready) || delay_count > 20)
> +                               break;
> +
> +                       mdelay(1);
> +                       delay_count++;
> +               }
> +
> +               rtw_dbg(rtwdev, RTW_DBG_RFK, "TX delay_count = %d\n",
> +                       delay_count);
> +
> +               if (delay_count < 20) { /* If 20ms No Result, then cal_retry++ */
> +                       /* ============TXIQK Check============== */
> +                       tx0_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(12));
> +                       tx1_fail = rtw_read32_mask(rtwdev, 0xd40, BIT(12));

Can you collect undefined register addresses? I can try to lookup them in one go. 

[...]

> +
> +#define MACBB_REG_NUM_8812A 9
> +#define AFE_REG_NUM_8812A 12
> +#define RF_REG_NUM_8812A 3
> +
> +static void rtw8812a_do_iqk(struct rtw_dev *rtwdev)
> +{
> +       static const u32 backup_macbb_reg[MACBB_REG_NUM_8812A] = {
> +               0x520, 0x550, 0x808, 0xa04, 0x90c, 0xc00, 0xe00, 0x838, 0x82c
> +       };
> +       static const u32 backup_afe_reg[AFE_REG_NUM_8812A] = {
> +               0xc5c, 0xc60, 0xc64, 0xc68, 0xcb0, 0xcb4,
> +               0xe5c, 0xe60, 0xe64, 0xe68, 0xeb0, 0xeb4
> +       };
> +       static const u32 backup_rf_reg[RF_REG_NUM_8812A] = {
> +               0x65, 0x8f, 0x0
> +       };
> +       u32 macbb_backup[MACBB_REG_NUM_8812A] = {0};
> +       u32 afe_backup[AFE_REG_NUM_8812A] = {0};
> +       u32 rfa_backup[RF_REG_NUM_8812A] = {0};
> +       u32 rfb_backup[RF_REG_NUM_8812A] = {0};

'= {}'


[...]

> +
> +static void rtw8821a_coex_cfg_gnt_fix(struct rtw_dev *rtwdev)
> +{}

Below looks regular. 

static void rtw8821a_coex_cfg_gnt_fix(struct rtw_dev *rtwdev)
{
}

As well as rtw8821a_coex_cfg_gnt_debug() and rtw8821a_coex_cfg_wl_rx_gain()

[...]

> +static struct rtw_hw_reg rtw8821a_dig[] = {

const ?

> +       [0] = { .addr = 0xc50, .mask = 0x7f },
> +       [1] = { .addr = 0xe50, .mask = 0x7f },
> +};
> +
> +static struct rtw_page_table page_table_8821a[] = {

const ?

> +       /* hq_num, nq_num, lq_num, exq_num, gapq_num */
> +       {0, 0, 0, 0, 0},        /* SDIO */
> +       {0, 0, 0, 0, 0},        /* PCI */
> +       {8, 0, 0, 0, 1},        /* 2 bulk out endpoints */
> +       {8, 0, 8, 0, 1},        /* 3 bulk out endpoints */
> +       {8, 0, 8, 4, 1},        /* 4 bulk out endpoints */
> +};
> +
> +static struct rtw_page_table page_table_8812a[] = {

const ?

> +       /* hq_num, nq_num, lq_num, exq_num, gapq_num */
> +       {0, 0, 0, 0, 0},        /* SDIO */
> +       {0, 0, 0, 0, 0},        /* PCI */
> +       {16, 0, 0, 0, 1},       /* 2 bulk out endpoints */
> +       {16, 0, 16, 0, 1},      /* 3 bulk out endpoints */
> +       {16, 0, 16, 0, 1},      /* 4 bulk out endpoints */
> +};
> +
> +static struct rtw_rqpn rqpn_table_8821a[] = {

const ?

> +       {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
> +        RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
> +        RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
> +
> +       {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
> +        RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
> +        RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
> +
> +       {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH,
> +        RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
> +        RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
> +
> +       {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
> +        RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
> +        RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
> +
> +       {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
> +        RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
> +        RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
> +};
> +
> +static struct rtw_prioq_addrs prioq_addrs_8821a = {

const?

> +       .prio[RTW_DMA_MAPPING_EXTRA] = {
> +               .rsvd = REG_RQPN_NPQ + 2, .avail = REG_RQPN_NPQ + 3,
> +       },
> +       .prio[RTW_DMA_MAPPING_LOW] = {
> +               .rsvd = REG_RQPN + 1, .avail = REG_FIFOPAGE_CTRL_2 + 1,
> +       },
> +       .prio[RTW_DMA_MAPPING_NORMAL] = {
> +               .rsvd = REG_RQPN_NPQ, .avail = REG_RQPN_NPQ + 1,
> +       },
> +       .prio[RTW_DMA_MAPPING_HIGH] = {
> +               .rsvd = REG_RQPN, .avail = REG_FIFOPAGE_CTRL_2,
> +       },
> +       .wsize = false,
> +};
> +
> +static struct rtw_chip_ops rtw8821a_ops = {
> +       .power_on               = rtw8821a_power_on,
> +       .power_off              = rtw8821a_power_off,
> +       .phy_set_param          = NULL,
> +       .read_efuse             = rtw8821a_read_efuse,
> +       .query_rx_desc          = rtw8821a_query_rx_desc,
> +       .set_channel            = rtw8821a_set_channel,
> +       .mac_init               = NULL,
> +       .read_rf                = rtw8821a_phy_read_rf,
> +       .write_rf               = rtw_phy_write_rf_reg_sipi,
> +       .set_antenna            = NULL,
> +       .set_tx_power_index     = rtw8821a_set_tx_power_index,
> +       .cfg_ldo25              = rtw8821a_cfg_ldo25,
> +       .efuse_grant            = rtw8821a_efuse_grant,
> +       .false_alarm_statistics = rtw8821a_false_alarm_statistics,
> +       .phy_calibration        = rtw8821a_phy_calibration,
> +       .cck_pd_set             = rtw8821a_phy_cck_pd_set,
> +       .pwr_track              = rtw8821a_pwr_track,
> +       .config_bfee            = NULL,
> +       .set_gid_table          = NULL,
> +       .cfg_csi_rate           = NULL,
> +       .fill_txdesc_checksum   = rtw8821a_fill_txdesc_checksum,
> +       .coex_set_init          = rtw8821a_coex_cfg_init,
> +       .coex_set_ant_switch    = rtw8821a_coex_cfg_ant_switch,
> +       .coex_set_gnt_fix       = rtw8821a_coex_cfg_gnt_fix,
> +       .coex_set_gnt_debug     = rtw8821a_coex_cfg_gnt_debug,
> +       .coex_set_rfe_type      = rtw8821a_coex_cfg_rfe_type,
> +       .coex_set_wl_tx_power   = rtw8821a_coex_cfg_wl_tx_power,
> +       .coex_set_wl_rx_gain    = rtw8821a_coex_cfg_wl_rx_gain,
> +};
> +

[...]

> +
> +const struct rtw_chip_info rtw8812a_hw_spec = {

Is it possible moving 8812a to individual file?
Since you have rtw8812au.c and rtw8821au.c. 

> +       .ops = &rtw8821a_ops,
> +       .id = RTW_CHIP_TYPE_8812A,
> +       .fw_name = "rtw88/rtw8812a_fw.bin",
> +       .wlan_cpu = RTW_WCPU_11N,
> +       .tx_pkt_desc_sz = 40,
> +       .tx_buf_desc_sz = 16,
> +       .rx_pkt_desc_sz = 24,
> +       .rx_buf_desc_sz = 8,
> +       .phy_efuse_size = 512,
> +       .log_efuse_size = 512,
> +       .ptct_efuse_size = 96 + 1, /* TODO or just 18? */
> +       .txff_size = 131072,
> +       .rxff_size = 16128,
> +       .rsvd_drv_pg_num = 9,
> +       .txgi_factor = 1,
> +       .is_pwr_by_rate_dec = true,
> +       .max_power_index = 0x3f,
> +       .csi_buf_pg_num = 0,
> +       .band = RTW_BAND_2G | RTW_BAND_5G,
> +       .page_size = 512,
> +       .dig_min = 0x20,
> +       .ht_supported = true,
> +       .vht_supported = true,
> +       .lps_deep_mode_supported = 0,
> +       .sys_func_en = 0xFD,
> +       .pwr_on_seq = card_enable_flow_8812a,
> +       .pwr_off_seq = card_disable_flow_8812a,
> +       .page_table = page_table_8812a,
> +       .rqpn_table = rqpn_table_8821a,
> +       .prioq_addrs = &prioq_addrs_8821a,
> +       .intf_table = &phy_para_table_8821a,
> +       .dig = rtw8821a_dig,
> +       .rf_sipi_addr = {REG_LSSI_WRITE_A, REG_LSSI_WRITE_B},
> +       .ltecoex_addr = NULL,
> +       .mac_tbl = &rtw8812a_mac_tbl,
> +       .agc_tbl = &rtw8812a_agc_tbl,
> +       .bb_tbl = &rtw8812a_bb_tbl,
> +       .rf_tbl = {&rtw8812a_rf_a_tbl, &rtw8812a_rf_b_tbl},
> +       .rfe_defs = rtw8812a_rfe_defs,
> +       .rfe_defs_size = ARRAY_SIZE(rtw8812a_rfe_defs),
> +       .rx_ldpc = false,
> +       .hw_feature_report = false,
> +       .c2h_ra_report_size = 4,
> +       .old_datarate_fb_limit = true,
> +       .usb_tx_agg_desc_num = 1,
> +       .iqk_threshold = 8,
> +       .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
> +       .max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
> +
> +       .coex_para_ver = 0, /* no coex code in 8812au driver */
> +       .bt_desired_ver = 0,
> +       .scbd_support = false,
> +       .new_scbd10_def = false,
> +       .ble_hid_profile_support = false,
> +       .wl_mimo_ps_support = false,
> +       .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
> +       .bt_rssi_type = COEX_BTRSSI_RATIO,
> +       .ant_isolation = 15,
> +       .rssi_tolerance = 2,
> +       .wl_rssi_step = wl_rssi_step_8821a,
> +       .bt_rssi_step = bt_rssi_step_8821a,
> +       .table_sant_num = 0,
> +       .table_sant = NULL,
> +       .table_nsant_num = 0,
> +       .table_nsant = NULL,
> +       .tdma_sant_num = 0,
> +       .tdma_sant = NULL,
> +       .tdma_nsant_num = 0,
> +       .tdma_nsant = NULL,
> +       .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8821a),
> +       .wl_rf_para_tx = rf_para_tx_8821a,
> +       .wl_rf_para_rx = rf_para_rx_8821a,
> +       .bt_afh_span_bw20 = 0x20,
> +       .bt_afh_span_bw40 = 0x30,
> +       .afh_5g_num = 0,
> +       .afh_5g = NULL,
> +
> +       .coex_info_hw_regs_num = 0,
> +       .coex_info_hw_regs = NULL,
> +};
> +EXPORT_SYMBOL(rtw8812a_hw_spec);
> +
> +MODULE_FIRMWARE("rtw88/rtw8821a_fw.bin");
> +MODULE_FIRMWARE("rtw88/rtw8812a_fw.bin");
> +
> +MODULE_AUTHOR("Realtek Corporation");
> +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.h
> b/drivers/net/wireless/realtek/rtw88/rtw8821a.h
> new file mode 100644
> index 000000000000..7f1c2d2eb6d2
> --- /dev/null
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.h
> @@ -0,0 +1,385 @@
> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
> +/* Copyright(c) 2018-2019  Realtek Corporation

Year 2024

> + */
> +
> +#ifndef __RTW8821A_H__
> +#define __RTW8821A_H__
> +
> +#include <asm/byteorder.h>
> +
> +struct rtw8821au_efuse {
> +       u8 res4[48];                    /* 0xd0 */
> +       u8 vid[2];                      /* 0x100 */
> +       u8 pid[2];
> +       u8 res8[3];
> +       u8 mac_addr[ETH_ALEN];          /* 0x107 */
> +       u8 res9[243];
> +};

__packed

> +
> +struct rtw8812au_efuse {
> +       u8 vid[2];                      /* 0xd0 */
> +       u8 pid[2];                      /* 0xd2 */
> +       u8 res0[3];
> +       u8 mac_addr[ETH_ALEN];          /* 0xd7 */
> +       u8 res1[291];
> +};

__packed

[...]

> +#define WLAN_SIFS_CFG  (WLAN_SIFS_CCK_CONT_TX | \
> +                       (WLAN_SIFS_OFDM_CONT_TX << BIT_SHIFT_SIFS_OFDM_CTX) | \
> +                       (WLAN_SIFS_CCK_TRX << BIT_SHIFT_SIFS_CCK_TRX) | \
> +                       (WLAN_SIFS_OFDM_TRX << BIT_SHIFT_SIFS_OFDM_TRX))

BIT_SHIFT_SIFS_OFDM_CTX is defined in reg.h, so this header file should
include reg.h. Because every header file should be included independently. 

> +
> +#define WLAN_TBTT_TIME (WLAN_TBTT_PROHIBIT |\
> +                       (WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP))
> +
> +#define WLAN_NAV_CFG           (WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16))
> +#define WLAN_RX_TSF_CFG                (WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8)
> +#define WLAN_PRE_TXCNT_TIME_TH                 0x1E4
> +
> +struct rtw8821a_phy_status_rpt {
> +       /* DWORD 0 */
> +       u8 gain_trsw[2]; /* path-A and path-B { TRSW, gain[6:0] } */
> +       u8 chl_num_lsb; /* channel number[7:0] */
> +#ifdef __LITTLE_ENDIAN
> +       u8 chl_num_msb:2; /* channel number[9:8] */
> +       u8 sub_chnl:4; /* sub-channel location[3:0] */
> +       u8 r_rfmod:2; /* RF mode[1:0] */
> +#else
> +       u8 r_rfmod:2;
> +       u8 sub_chnl:4;
> +       u8 chl_num_msb:2;
> +#endif

Not prefer "#ifdef __LITTLE_ENDIAN" style, because we have never verified
big endian part. Prefer to define mask and access them via u8_get_bits() and
its friends.


> +
> +       /* DWORD 1 */
> +       u8 pwdb_all; /* CCK signal quality / OFDM pwdb all */
> +       s8 cfosho[2]; /* CCK AGC report and CCK_BB_Power */
> +                     /* OFDM path-A and path-B short CFO */
> +#ifdef __LITTLE_ENDIAN
> +       u8 resvd_0:6;
> +       u8 bt_rf_ch_msb:2; /* 8812A: 2'b0 8814A: bt rf channel keep[7:6] */

Will you share this struct with 8814A? If not please remove comments related to 8814A.

> +#else
> +       u8 bt_rf_ch_msb:2;
> +       u8 resvd_0:6;
> +#endif

[...]


> +
> +#define REG_SYS_CTRL                           0x000
> +#define BIT_FEN_EN                             BIT(26)
> +#define REG_APS_FSMCO                          0x04
> +#define  APS_FSMCO_MAC_ENABLE                  BIT(8)
> +#define  APS_FSMCO_MAC_OFF                     BIT(9)
> +#define  APS_FSMCO_HW_POWERDOWN                        BIT(15)
> +#define REG_ACLK_MON                           0x3e
> +#define REG_RF_B_CTRL                          0x76
> +#define REG_HIMR0                              0xb0
> +#define REG_HISR0                              0xb4
> +#define REG_HIMR1                              0xb8
> +#define REG_HISR1                              0xbc

[...]

Can't we move all of these registers (including what I delete) into reg.h?
Bitterblue Smith Aug. 27, 2024, 5:52 p.m. UTC | #2
On 16/08/2024 09:06, Ping-Ke Shih wrote:
> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>> These contain all the logic for the RTL8821AU and RTL8812AU chips.
>>
>> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
>> ---
>>  drivers/net/wireless/realtek/rtw88/rtw8821a.c | 4139 +++++++++++++++++
>>  drivers/net/wireless/realtek/rtw88/rtw8821a.h |  385 ++
>>  2 files changed, 4524 insertions(+)
>>  create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821a.c
>>  create mode 100644 drivers/net/wireless/realtek/rtw88/rtw8821a.h
>>

[...]

>> +static int rtw8821a_power_on(struct rtw_dev *rtwdev)
>> +{
> 
> Will the coming RTL8814AU share this flow? If so, we can move this power on
> to main.c/mac.c as rtw_power_on_v1() or something else. 
> 

RTL8814AU will not use these power on/off functions. I'm hoping
it will work with the rtw_power_on/off functions. Right now I only
got as far as filling rtw8814a_table.c.

> If we decide moving power on flow into chip specific files, the duplicate code
> will become more and more. Please think about this. 
> 
> 
>> +       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.
>> +        */
>> +       rtw8812au_hw_reset(rtwdev);
>> +
>> +       ret = rtw8812au_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 = rtw8821a_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);
>> +
>> +       rtw8821au_init_queue_reserved_page(rtwdev);
>> +       rtw8821au_init_tx_buffer_boundary(rtwdev);
>> +       rtw8821au_init_queue_priority(rtwdev);
>> +
> 
> Seemingly above flow looks common. Can it share with other chips?
> 

I don't know if any other chips can use this.

> [...]
> 
>> +}
>> +
>> +static u32 rtw8821a_phy_read_rf(struct rtw_dev *rtwdev,
>> +                               enum rtw_rf_path rf_path, u32 addr, u32 mask)
>> +{
> 
> read/write RF functions are also common for chips. Can it share with coming RTL8814A?
> Move this to phy.c as v1?
> 

No, RTL8814A will use rtw_phy_read_rf(), like RTL8822B.

>> +       static const u32 pi_addr[2] = { 0xc00, 0xe00 };
>> +       static const u32 read_addr[2][2] = {
>> +               { REG_SI_READ_A, REG_SI_READ_B },
>> +               { REG_PI_READ_A, REG_PI_READ_B }
>> +       };
> 
> [...]
> 
>> +
>> +static void rtw8821a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
>> +                                     struct rtw_rx_pkt_stat *pkt_stat)
>> +{
>> +       struct rtw_dm_info *dm_info = &rtwdev->dm_info;
>> +       struct rtw8821a_phy_status_rpt *phy_sts;
>> +       u8 lna_idx, vga_idx, cck_agc_rpt;
>> +       s8 rx_pwr_db, power_a, power_b;
>> +       const s8 min_rx_power = -120;
>> +       u8 rssi, val, i;
>> +
>> +       phy_sts = (struct rtw8821a_phy_status_rpt *)phy_status;
>> +
>> +       if (pkt_stat->rate <= DESC_RATE11M) {
>> +               cck_agc_rpt = phy_sts->cfosho[0];
>> +               lna_idx = (cck_agc_rpt & 0xE0) >> 5;
>> +               vga_idx = cck_agc_rpt & 0x1F;
> 
> If we remove "#ifdef __LITTLE_ENDIAN" from rtw8821a_phy_status_rpt and define
> bit mask there, additionally define these bit masks and then use u8_get_bits().
>
> By the way, shouldn't the field of 'cfosho[2]' be 'u8' instead of 's8'?
> 

Those bytes mean different things for CCK and for OFDM. I suppose
they need to be s8 for "short CFO" (OFDM) but u8 for "CCK AGC report".
They are s8 in the official driver.

>> +
>> +               if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
>> +                       rx_pwr_db = rtw8821a_cck_rx_pwr(rtwdev, lna_idx, vga_idx);
>> +               else
>> +                       rx_pwr_db = rtw8812a_cck_rx_pwr(rtwdev, 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;
>> +
>> +               if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A &&
>> +                   !rtwdev->hal.cck_high_power) {
>> +                       if (pkt_stat->rssi >= 80)
>> +                               pkt_stat->rssi = ((pkt_stat->rssi - 80) << 1) +
>> +                                                ((pkt_stat->rssi - 80) >> 1) + 80;
>> +                       else if (pkt_stat->rssi <= 78 && pkt_stat->rssi >= 20)
>> +                               pkt_stat->rssi += 3;
>> +               }
>> +       } else { /* OFDM rate */
>> +               for (i = RF_PATH_A; i < rtwdev->hal.rf_path_num; i++) {
>> +                       val = phy_sts->gain_trsw[i];
>> +                       pkt_stat->rx_power[i] = (val & 0x7F) - 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);
>> +       }
>> +}
>> +
>> +static void rtw8821a_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc,
>> +                                  struct rtw_rx_pkt_stat *pkt_stat,
>> +                                  struct ieee80211_rx_status *rx_status)
>> +{
>> +       u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz;
>> +       struct ieee80211_hdr *hdr;
>> +       u8 *phy_status = NULL;
>> +
>> +       memset(pkt_stat, 0, sizeof(*pkt_stat));
>> +
>> +       pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc);
>> +       pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc);
>> +       pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc);
>> +       pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) &&
>> +                             GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE;
>> +       pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc);
>> +       pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc);
>> +       pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc);
>> +       pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc);
>> +       pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc);
>> +       pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc);
>> +       pkt_stat->ppdu_cnt = 0;
>> +       pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc);
>> +       pkt_stat->bw = GET_RX_DESC_BW(rx_desc);
> 
> More and more chips use these macros. Please add a patch using struct to 
> access these fields. More, query_rx_desc() are very similar across chips,
> please move them to mac.c or phy.c as a common function.
> 

Why not rx.c?

>> +
>> +       /* drv_info_sz is in unit of 8-bytes */
>> +       pkt_stat->drv_info_sz *= 8;
>> +
>> +       /* c2h cmd pkt's rx/phy status is not interested */
>> +       if (pkt_stat->is_c2h)
>> +               return;
>> +
>> +       hdr = (struct ieee80211_hdr *)(rx_desc + desc_sz + pkt_stat->shift +
>> +                                      pkt_stat->drv_info_sz);
>> +       if (pkt_stat->phy_status) {
>> +               phy_status = rx_desc + desc_sz + pkt_stat->shift;
>> +               rtw8821a_query_phy_status(rtwdev, phy_status, pkt_stat);
>> +       }
>> +
>> +       rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status);
>> +}
>> +

[...]

>> +static void rtw8821a_iqk_tx_vdf_true(struct rtw_dev *rtwdev, u32 cal,
>> +                                    bool *tx0iqkok,
>> +                                    int tx_x0[CAL_NUM_8821A],
>> +                                    int tx_y0[CAL_NUM_8821A])
>> +{
>> +       u32 cal_retry, delay_count, iqk_ready, tx_fail;
>> +       int tx_dt[3], vdf_y[3], vdf_x[3];
>> +       int k;
>> +
>> +       for (k = 0; k <= 2; k++) {
> 
> '< 3' would be more intuitive, because 'k' is index of array.
> 
>> +               switch (k) {
>> +               case 0:
>> +                       /* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
>> +                       rtw_write32(rtwdev, 0xc80, 0x18008c38);
>> +                       /* RX_Tone_idx[9:0], RxK_Mask[29] */
>> +                       rtw_write32(rtwdev, 0xc84, 0x38008c38);
>> +                       rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x0);
>> +                       break;
>> +               case 1:
>> +                       rtw_write32_mask(rtwdev, 0xc80, BIT(28), 0x0);
>> +                       rtw_write32_mask(rtwdev, 0xc84, BIT(28), 0x0);
>> +                       rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x0);
>> +                       break;
>> +               case 2:
>> +                       rtw_dbg(rtwdev, RTW_DBG_RFK,
>> +                               "vdf_y[1] = %x vdf_y[0] = %x\n",
>> +                               vdf_y[1] >> 21 & 0x00007ff,
>> +                               vdf_y[0] >> 21 & 0x00007ff);
>> +
>> +                       rtw_dbg(rtwdev, RTW_DBG_RFK,
>> +                               "vdf_x[1] = %x vdf_x[0] = %x\n",
>> +                               vdf_x[1] >> 21 & 0x00007ff,
>> +                               vdf_x[0] >> 21 & 0x00007ff);
>> +
>> +                       tx_dt[cal] = (vdf_y[1] >> 20) - (vdf_y[0] >> 20);
>> +                       tx_dt[cal] = (16 * tx_dt[cal]) * 10000 / 15708;
>> +                       tx_dt[cal] = (tx_dt[cal] >> 1) + (tx_dt[cal] & BIT(0));
>> +
>> +                       /* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
>> +                       rtw_write32(rtwdev, 0xc80, 0x18008c20);
>> +                       /* RX_Tone_idx[9:0], RxK_Mask[29] */
>> +                       rtw_write32(rtwdev, 0xc84, 0x38008c20);
>> +                       rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x1);
>> +                       rtw_write32_mask(rtwdev, 0xce8, 0x3fff0000,
>> +                                        tx_dt[cal] & 0x00003fff);
>> +                       break;
>> +               }
>> +
>> +               rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
>> +               cal_retry = 0;
>> +               while (1) {
> 
> Can we replace 'while()' by 'for (cal_retry = 0; cal_retry < 10; cal_retry++)'?
> 

I think so.

>> +                       /* one shot */
>> +                       rtw_write32(rtwdev, 0x980, 0xfa000000);
>> +                       rtw_write32(rtwdev, 0x980, 0xf8000000);
>> +
>> +                       mdelay(10);
>> +
>> +                       rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
>> +
>> +                       delay_count = 0;
>> +                       while (1) {
> 

[...]

>> +               if (delay_count < 20) { /* If 20ms No Result, then cal_retry++ */
>> +                       /* ============TXIQK Check============== */
>> +                       tx0_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(12));
>> +                       tx1_fail = rtw_read32_mask(rtwdev, 0xd40, BIT(12));
> 
> Can you collect undefined register addresses? I can try to lookup them in one go. 
> 

Here are the addresses I could find and a few bits/bit masks:

0x765
0x90c
0x978
        31, 0x03ff8000, 0x000007ff
0x97c
        31
0x980
0x984
0xa0a
0xc00
        0xf
0xc5c
        GENMASK(26, 24)
0xce8
        31, 0x3fff0000
0xd00
        10, 11, 12, 0x07ff0000
0xd40
        10, 11, 12
0xe00
0xe5c
        GENMASK(26, 24)
0xe80
0xe84
0xe88
0xe8c
0xe90
        7
0xe94
        0
0xec4
        18, 29
0xec8
        29
0xecc
0xed4
0xee8
0xf008
        3, 4
0xf050

And some RF registers: 0x8, 0x58, 0x65, 0x8f

[...]

>> +
>> +const struct rtw_chip_info rtw8812a_hw_spec = {
> 
> Is it possible moving 8812a to individual file?
> Since you have rtw8812au.c and rtw8821au.c. 
> 

I think it is possible. But most of the code is common to both chips.
Only the IQ calibration could be moved.

Another possibility is to move rtw8812au.c into rtw8821au.c and have
only one module handle both chips.

>> +       .ops = &rtw8821a_ops,
>> +       .id = RTW_CHIP_TYPE_8812A,
>> +       .fw_name = "rtw88/rtw8812a_fw.bin",
>> +       .wlan_cpu = RTW_WCPU_11N,
>> +       .tx_pkt_desc_sz = 40,
>> +       .tx_buf_desc_sz = 16,
>> +       .rx_pkt_desc_sz = 24,
>> +       .rx_buf_desc_sz = 8,
>> +       .phy_efuse_size = 512,
>> +       .log_efuse_size = 512,
>> +       .ptct_efuse_size = 96 + 1, /* TODO or just 18? */
>> +       .txff_size = 131072,
>> +       .rxff_size = 16128,
>> +       .rsvd_drv_pg_num = 9,
>> +       .txgi_factor = 1,
>> +       .is_pwr_by_rate_dec = true,
>> +       .max_power_index = 0x3f,
>> +       .csi_buf_pg_num = 0,
>> +       .band = RTW_BAND_2G | RTW_BAND_5G,
>> +       .page_size = 512,
>> +       .dig_min = 0x20,
>> +       .ht_supported = true,
>> +       .vht_supported = true,
>> +       .lps_deep_mode_supported = 0,
>> +       .sys_func_en = 0xFD,
>> +       .pwr_on_seq = card_enable_flow_8812a,
>> +       .pwr_off_seq = card_disable_flow_8812a,
>> +       .page_table = page_table_8812a,
>> +       .rqpn_table = rqpn_table_8821a,
>> +       .prioq_addrs = &prioq_addrs_8821a,
>> +       .intf_table = &phy_para_table_8821a,
>> +       .dig = rtw8821a_dig,
>> +       .rf_sipi_addr = {REG_LSSI_WRITE_A, REG_LSSI_WRITE_B},
>> +       .ltecoex_addr = NULL,
>> +       .mac_tbl = &rtw8812a_mac_tbl,
>> +       .agc_tbl = &rtw8812a_agc_tbl,
>> +       .bb_tbl = &rtw8812a_bb_tbl,
>> +       .rf_tbl = {&rtw8812a_rf_a_tbl, &rtw8812a_rf_b_tbl},
>> +       .rfe_defs = rtw8812a_rfe_defs,
>> +       .rfe_defs_size = ARRAY_SIZE(rtw8812a_rfe_defs),
>> +       .rx_ldpc = false,
>> +       .hw_feature_report = false,
>> +       .c2h_ra_report_size = 4,
>> +       .old_datarate_fb_limit = true,
>> +       .usb_tx_agg_desc_num = 1,
>> +       .iqk_threshold = 8,
>> +       .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
>> +       .max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
>> +
>> +       .coex_para_ver = 0, /* no coex code in 8812au driver */
>> +       .bt_desired_ver = 0,
>> +       .scbd_support = false,
>> +       .new_scbd10_def = false,
>> +       .ble_hid_profile_support = false,
>> +       .wl_mimo_ps_support = false,
>> +       .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
>> +       .bt_rssi_type = COEX_BTRSSI_RATIO,
>> +       .ant_isolation = 15,
>> +       .rssi_tolerance = 2,
>> +       .wl_rssi_step = wl_rssi_step_8821a,
>> +       .bt_rssi_step = bt_rssi_step_8821a,
>> +       .table_sant_num = 0,
>> +       .table_sant = NULL,
>> +       .table_nsant_num = 0,
>> +       .table_nsant = NULL,
>> +       .tdma_sant_num = 0,
>> +       .tdma_sant = NULL,
>> +       .tdma_nsant_num = 0,
>> +       .tdma_nsant = NULL,
>> +       .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8821a),
>> +       .wl_rf_para_tx = rf_para_tx_8821a,
>> +       .wl_rf_para_rx = rf_para_rx_8821a,
>> +       .bt_afh_span_bw20 = 0x20,
>> +       .bt_afh_span_bw40 = 0x30,
>> +       .afh_5g_num = 0,
>> +       .afh_5g = NULL,
>> +
>> +       .coex_info_hw_regs_num = 0,
>> +       .coex_info_hw_regs = NULL,
>> +};
>> +EXPORT_SYMBOL(rtw8812a_hw_spec);
>> +
>> +MODULE_FIRMWARE("rtw88/rtw8821a_fw.bin");
>> +MODULE_FIRMWARE("rtw88/rtw8812a_fw.bin");
>> +
>> +MODULE_AUTHOR("Realtek Corporation");
>> +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a driver");
>> +MODULE_LICENSE("Dual BSD/GPL");
>> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.h
>> b/drivers/net/wireless/realtek/rtw88/rtw8821a.h
>> new file mode 100644
>> index 000000000000..7f1c2d2eb6d2
>> --- /dev/null
>> +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.h
>> @@ -0,0 +1,385 @@
>> +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
>> +/* Copyright(c) 2018-2019  Realtek Corporation
> 
> Year 2024
> 
>> + */
>> +
>> +#ifndef __RTW8821A_H__
>> +#define __RTW8821A_H__
>> +
>> +#include <asm/byteorder.h>
>> +
>> +struct rtw8821au_efuse {
>> +       u8 res4[48];                    /* 0xd0 */
>> +       u8 vid[2];                      /* 0x100 */
>> +       u8 pid[2];
>> +       u8 res8[3];
>> +       u8 mac_addr[ETH_ALEN];          /* 0x107 */
>> +       u8 res9[243];
>> +};
> 
> __packed
> 
>> +
>> +struct rtw8812au_efuse {
>> +       u8 vid[2];                      /* 0xd0 */
>> +       u8 pid[2];                      /* 0xd2 */
>> +       u8 res0[3];
>> +       u8 mac_addr[ETH_ALEN];          /* 0xd7 */
>> +       u8 res1[291];
>> +};
> 
> __packed
> 
> [...]
> 
>> +#define WLAN_SIFS_CFG  (WLAN_SIFS_CCK_CONT_TX | \
>> +                       (WLAN_SIFS_OFDM_CONT_TX << BIT_SHIFT_SIFS_OFDM_CTX) | \
>> +                       (WLAN_SIFS_CCK_TRX << BIT_SHIFT_SIFS_CCK_TRX) | \
>> +                       (WLAN_SIFS_OFDM_TRX << BIT_SHIFT_SIFS_OFDM_TRX))
> 
> BIT_SHIFT_SIFS_OFDM_CTX is defined in reg.h, so this header file should
> include reg.h. Because every header file should be included independently. 
> 
>> +
>> +#define WLAN_TBTT_TIME (WLAN_TBTT_PROHIBIT |\
>> +                       (WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP))
>> +
>> +#define WLAN_NAV_CFG           (WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16))
>> +#define WLAN_RX_TSF_CFG                (WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8)
>> +#define WLAN_PRE_TXCNT_TIME_TH                 0x1E4
>> +
>> +struct rtw8821a_phy_status_rpt {
>> +       /* DWORD 0 */
>> +       u8 gain_trsw[2]; /* path-A and path-B { TRSW, gain[6:0] } */
>> +       u8 chl_num_lsb; /* channel number[7:0] */
>> +#ifdef __LITTLE_ENDIAN
>> +       u8 chl_num_msb:2; /* channel number[9:8] */
>> +       u8 sub_chnl:4; /* sub-channel location[3:0] */
>> +       u8 r_rfmod:2; /* RF mode[1:0] */
>> +#else
>> +       u8 r_rfmod:2;
>> +       u8 sub_chnl:4;
>> +       u8 chl_num_msb:2;
>> +#endif
> 
> Not prefer "#ifdef __LITTLE_ENDIAN" style, because we have never verified
> big endian part. Prefer to define mask and access them via u8_get_bits() and
> its friends.
> 

None of the members inside #ifdef __LITLLE_ENDIAN are used
in this driver. Do I still have to define all the masks?

> 
>> +
>> +       /* DWORD 1 */
>> +       u8 pwdb_all; /* CCK signal quality / OFDM pwdb all */
>> +       s8 cfosho[2]; /* CCK AGC report and CCK_BB_Power */
>> +                     /* OFDM path-A and path-B short CFO */
>> +#ifdef __LITTLE_ENDIAN
>> +       u8 resvd_0:6;
>> +       u8 bt_rf_ch_msb:2; /* 8812A: 2'b0 8814A: bt rf channel keep[7:6] */
> 
> Will you share this struct with 8814A? If not please remove comments related to 8814A.
> 

Yes, it uses the same struct.

>> +#else
>> +       u8 bt_rf_ch_msb:2;
>> +       u8 resvd_0:6;
>> +#endif
> 
> [...]
> 
> 
>> +
>> +#define REG_SYS_CTRL                           0x000
>> +#define BIT_FEN_EN                             BIT(26)
>> +#define REG_APS_FSMCO                          0x04
>> +#define  APS_FSMCO_MAC_ENABLE                  BIT(8)
>> +#define  APS_FSMCO_MAC_OFF                     BIT(9)
>> +#define  APS_FSMCO_HW_POWERDOWN                        BIT(15)
>> +#define REG_ACLK_MON                           0x3e
>> +#define REG_RF_B_CTRL                          0x76
>> +#define REG_HIMR0                              0xb0
>> +#define REG_HISR0                              0xb4
>> +#define REG_HIMR1                              0xb8
>> +#define REG_HISR1                              0xbc
> 
> [...]
> 
> Can't we move all of these registers (including what I delete) into reg.h?
> 

I think so.
Ping-Ke Shih Sept. 10, 2024, 2:30 a.m. UTC | #3
Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> On 16/08/2024 09:06, Ping-Ke Shih wrote:
> > Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> >> +       pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc);
> >> +       pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc);
> >> +       pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc);
> >> +       pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) &&
> >> +                             GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE;
> >> +       pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc);
> >> +       pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc);
> >> +       pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc);
> >> +       pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc);
> >> +       pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc);
> >> +       pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc);
> >> +       pkt_stat->ppdu_cnt = 0;
> >> +       pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc);
> >> +       pkt_stat->bw = GET_RX_DESC_BW(rx_desc);
> >
> > More and more chips use these macros. Please add a patch using struct to
> > access these fields. More, query_rx_desc() are very similar across chips,
> > please move them to mac.c or phy.c as a common function.
> >
> 
> Why not rx.c?

Also fine to me.

> > Can you collect undefined register addresses? I can try to lookup them in one go.
> >
> 
> Here are the addresses I could find and a few bits/bit masks:
> 
> 0x765

#define REG_GNT_BT 0x765
#define BIT_PTA_SW_CTL GENMASK(4, 3)

> 0x90c

#define REG_DAC_RSTB 0x90c

> 0x978
>         31, 0x03ff8000, 0x000007ff
> 0x97c
>         31
> 0x980
> 0x984

#define REG_IQK_COM00 0x978
#define REG_IQK_COM32 0x97c
#define REG_IQK_COM64 0x980
#define REG_IQK_COM96 0x984

> 0xa0a

#define REG_CCK_PD_TH 0xa0a

> 0xc00
>         0xf
> 0xe00

#define REG_3WIRE_SWA 0xc00
#define REG_3WIRE_SWB 0xe00

(0xc00 page for path A, 0xe00 page for path B)

> 0xc5c
> 0xe5c
>         GENMASK(26, 24)

#define REG_CK_MONHA 0xc5c
#define REG_CK_MONHB 0xe5c

> 0xce8
>         31, 0x3fff0000
> 0xee8

#define REG_INTPO_SETA 0xce8
#define REG_INTPO_SETB 0xee8

> 0xd00
>         10, 11, 12, 0x07ff0000

#define REG_IQKA_END 0xd00

> 0xd40
>         10, 11, 12

#define REG_IQKB_END 0xd40

>         GENMASK(26, 24)
> 0xe80

#define REG_TXTONEB 0xe80

> 0xe84

#define REG_RXTONEB 0xe84

> 0xe88

#define REG_TXPITMB 0xe88

> 0xe8c

#define REG_RXPITMB 0xe8c

> 0xe90

#define REG_PREDISTB 0xe90

>         7
> 0xe94

#define REG_INIDLYB 0xe94

>         0
> 0xec4
>         18, 29


#define REG_BPBDB 0xec4

> 0xec8
>         29

#define REG_PHYTXONB 0xec8

> 0xecc

#define REG_IQKYB 0xecc

> 0xed4

#define REG_IQKXB 0xed4

> 0xf008
>         3, 4
> 0xf050

I can't find these two, but just follow comments above.

#define REG_USB_MOD 0xf008
#define REG_USB3_RXITV 0xf050


> 
> And some RF registers: 0x8, 0x58, 0x65, 0x8f
> 

#define RF_DTXLOK	0x08 (already existing)
#define RF_TXMOD	0x58
#define RF_TXA_PREPAD 0x65
#define RF_RXBB2 0x8f 


> [...]
> 
> >> +
> >> +const struct rtw_chip_info rtw8812a_hw_spec = {
> >
> > Is it possible moving 8812a to individual file?
> > Since you have rtw8812au.c and rtw8821au.c.
> >
> 
> I think it is possible. But most of the code is common to both chips.
> Only the IQ calibration could be moved.

Yep, depend on how much IQK code echo chip has.

> 
> Another possibility is to move rtw8812au.c into rtw8821au.c and have
> only one module handle both chips.

Prefer two modules as was. That is clear to people we have two chips. 

> >> +
> >> +#define WLAN_TBTT_TIME (WLAN_TBTT_PROHIBIT |\
> >> +                       (WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP))
> >> +
> >> +#define WLAN_NAV_CFG           (WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16))
> >> +#define WLAN_RX_TSF_CFG                (WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8)
> >> +#define WLAN_PRE_TXCNT_TIME_TH                 0x1E4
> >> +
> >> +struct rtw8821a_phy_status_rpt {
> >> +       /* DWORD 0 */
> >> +       u8 gain_trsw[2]; /* path-A and path-B { TRSW, gain[6:0] } */
> >> +       u8 chl_num_lsb; /* channel number[7:0] */
> >> +#ifdef __LITTLE_ENDIAN
> >> +       u8 chl_num_msb:2; /* channel number[9:8] */
> >> +       u8 sub_chnl:4; /* sub-channel location[3:0] */
> >> +       u8 r_rfmod:2; /* RF mode[1:0] */
> >> +#else
> >> +       u8 r_rfmod:2;
> >> +       u8 sub_chnl:4;
> >> +       u8 chl_num_msb:2;
> >> +#endif
> >
> > Not prefer "#ifdef __LITTLE_ENDIAN" style, because we have never verified
> > big endian part. Prefer to define mask and access them via u8_get_bits() and
> > its friends.
> >
> 
> None of the members inside #ifdef __LITLLE_ENDIAN are used
> in this driver. Do I still have to define all the masks?

I prefer to define all masks, because the names have been there. Adding a name
in the future may be painful. 

> 
> >
> >> +
> >> +       /* DWORD 1 */
> >> +       u8 pwdb_all; /* CCK signal quality / OFDM pwdb all */
> >> +       s8 cfosho[2]; /* CCK AGC report and CCK_BB_Power */
> >> +                     /* OFDM path-A and path-B short CFO */
> >> +#ifdef __LITTLE_ENDIAN
> >> +       u8 resvd_0:6;
> >> +       u8 bt_rf_ch_msb:2; /* 8812A: 2'b0 8814A: bt rf channel keep[7:6] */
> >
> > Will you share this struct with 8814A? If not please remove comments related to 8814A.
> >
> 
> Yes, it uses the same struct.

But struct name is rtw8821a_phy_status_rpt that prefix is rtw8821a.

> >> +
> >> +#define REG_SYS_CTRL                           0x000
> >> +#define BIT_FEN_EN                             BIT(26)
> >> +#define REG_APS_FSMCO                          0x04
> >> +#define  APS_FSMCO_MAC_ENABLE                  BIT(8)
> >> +#define  APS_FSMCO_MAC_OFF                     BIT(9)
> >> +#define  APS_FSMCO_HW_POWERDOWN                        BIT(15)
> >> +#define REG_ACLK_MON                           0x3e
> >> +#define REG_RF_B_CTRL                          0x76
> >> +#define REG_HIMR0                              0xb0
> >> +#define REG_HISR0                              0xb4
> >> +#define REG_HIMR1                              0xb8
> >> +#define REG_HISR1                              0xbc
> >
> > [...]
> >
> > Can't we move all of these registers (including what I delete) into reg.h?
> >
> 
> I think so.

I remember we were afraid that definitions of registers for each are different,
so put them in chip specific header file. But now I feel most registers can be
shared, and easy to read code because we need spending extra time to disambiguate
duplicate definitions.
Bitterblue Smith Sept. 21, 2024, 10:47 p.m. UTC | #4
On 13/09/2024 04:50, Ping-Ke Shih wrote:
>>>
>>> #define REG_3WIRE_SWA 0xc00
>>> #define REG_3WIRE_SWB 0xe00
>>>
>>> (0xc00 page for path A, 0xe00 page for path B)
>>>
>>
>> Previously, you called 0xe00 REG_HSSI_WRITE_B. Are both names correct?
>> (I'm not sure why I put 0xc00 and 0xe00 on the list if you already gave
>> them a name.)
> 
> In fact, there is no _formal_ names for PHY registers, so I gave names by
> abbreviation name from RTL code. Previously I may reference to vendor
> drivers instead. Just choose one you like. 
> 
>>>> 0xe90
>>>
>>> #define REG_PREDISTB 0xe90
>>>
>>
>> I put 0xe90 on the list by mistake. We already have a name for it
>> in the official driver:
>>
>> ./include/Hal8812PhyReg.h:66:#define rB_LSSIWrite_Jaguar                        0xe90 /* RF write addr */
>>
>> I translated that as REG_LSSI_WRITE_B. Is REG_PREDISTB also a valid
>> name? Do we need both names?
> 
> For the use case of 0xe90 in rtw8812a_iqk_tx_fill():
> 
>    rtw_write32_mask(rtwdev, 0xc90, BIT(7), 0x1);
> 
> "RF write addr" seems not reasonable. I suggest to define both for this case. 
> 
> 
>> Some of these names are very different from their counterparts
>> from page C. In your previous email you said:
>>
>>> I think we can reuse existing definitions:
>>>
>>> rtw8723x.h:#define REG_OFDM_0_XA_TX_IQ_IMBALANCE        0x0c80
>>> rtw8703b.h:#define REG_OFDM0_A_TX_AFE 0x0c84
>>> rtw8723x.h:#define REG_OFDM_0_XB_TX_IQ_IMBALANCE        0x0c88
>>>
>>> #define REG_TSSI_TRK_SW 0xc8c
>>>
>>> rtw8821a.h:#define REG_TXAGCIDX                         0xc94
>>
>> Can we reuse these definitions?
> 
> Yes. You can reuse existing first. For non-existing definition, use the names
> I gave in this thread. 
> 
> Basically we can have two ways to have names. One is from vendor drivers, and
> the other is from abbreviation name of RTL code, which bit name instead of
> whole register name is given. Also I'm not very familiar with the functionality,
> so I did just paste reasonable names for undefined magic numbers.
> 

I see. Thank you for the explanations.

>>>>>> +
>>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
>>>>>
>>>>> Is it possible moving 8812a to individual file?
>>>>> Since you have rtw8812au.c and rtw8821au.c.
>>>>>
>>>>
>>>> I think it is possible. But most of the code is common to both chips.
>>>> Only the IQ calibration could be moved.
>>>
>>> Yep, depend on how much IQK code echo chip has.
>>>
>>
>> The IQ calibration for RTL8812AU is about 700 lines.
> 
> rtw8812au  -----> (a) rtw8812a 
>                         |
>                         v
>                   (b) rtw8821a_common  (hard to give a name)
>                         ^
>                         |
> rtw8821au  -----> (c) rtw8821a
> 
> Put all common code to (b). IQK code in (a) or (c). 
> 
> I feel you have thought above picture already. What are problems we will encounter?
> Many export symbols? If so, how about below?
> 
> rtw8812au  -----> (1) rtw8812a 
>     +---------+
>               +-> (2) rtw8821a_common  (hard to give a name)
>     +---------+
> rtw8821au  -----> (3) rtw8821a
> 
> Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
> respectively, and export IQK entry only. Does it work?
> 
>
For the name of the common module, I was thinking rtw88_88xxa.ko.

I wonder, what is the goal? To put the code in separate kernel
modules, or just separate files?

I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
three files compile into a single module, rtw88_88xxa.ko.

If each file compiles into a module of its own, we have circular
dependencies: rtw8821a_hw_spec -> common code -> IQK code.
If *_hw_spec go in the common module, it still depends on both of
the other two modules, so what use is it?
Ping-Ke Shih Sept. 23, 2024, 5:47 a.m. UTC | #5
Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> >>>>>> +
> >>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
> >>>>>
> >>>>> Is it possible moving 8812a to individual file?
> >>>>> Since you have rtw8812au.c and rtw8821au.c.
> >>>>>
> >>>>
> >>>> I think it is possible. But most of the code is common to both chips.
> >>>> Only the IQ calibration could be moved.
> >>>
> >>> Yep, depend on how much IQK code echo chip has.
> >>>
> >>
> >> The IQ calibration for RTL8812AU is about 700 lines.
> >
> > rtw8812au  -----> (a) rtw8812a
> >                         |
> >                         v
> >                   (b) rtw8821a_common  (hard to give a name)
> >                         ^
> >                         |
> > rtw8821au  -----> (c) rtw8821a
> >
> > Put all common code to (b). IQK code in (a) or (c).
> >
> > I feel you have thought above picture already. What are problems we will encounter?
> > Many export symbols? If so, how about below?
> >
> > rtw8812au  -----> (1) rtw8812a
> >     +---------+
> >               +-> (2) rtw8821a_common  (hard to give a name)
> >     +---------+
> > rtw8821au  -----> (3) rtw8821a
> >
> > Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
> > respectively, and export IQK entry only. Does it work?
> >
> >
> For the name of the common module, I was thinking rtw88_88xxa.ko.
> 
> I wonder, what is the goal? To put the code in separate kernel
> modules, or just separate files?

I would like to reduce runtime memory. As I asked, how many IQK code are different
from them? If you have separated and compiled them, can you share size by the
output of 'size' command? 

> 
> I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
> rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
> rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
> three files compile into a single module, rtw88_88xxa.ko.
> 
> If each file compiles into a module of its own, we have circular
> dependencies: rtw8821a_hw_spec -> common code -> IQK code.
> If *_hw_spec go in the common module, it still depends on both of
> the other two modules, so what use is it?

If we have dependency of common code -> IQK code, we can't save runtime
memory, because common code reference to both IQK code. So I felt
dependency of IQK code would be rtw8812au --> IQK code as above second
diagram. 

But if the work is complicated and save not few runtime memory, we can
use simple design as current did.
Ping-Ke Shih Sept. 23, 2024, 5:58 a.m. UTC | #6
Ping-Ke Shih <pkshih@realtek.com> wrote:
> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> > >>>>>> +
> > >>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
> > >>>>>
> > >>>>> Is it possible moving 8812a to individual file?
> > >>>>> Since you have rtw8812au.c and rtw8821au.c.
> > >>>>>
> > >>>>
> > >>>> I think it is possible. But most of the code is common to both chips.
> > >>>> Only the IQ calibration could be moved.
> > >>>
> > >>> Yep, depend on how much IQK code echo chip has.
> > >>>
> > >>
> > >> The IQ calibration for RTL8812AU is about 700 lines.
> > >
> > > rtw8812au  -----> (a) rtw8812a
> > >                         |
> > >                         v
> > >                   (b) rtw8821a_common  (hard to give a name)
> > >                         ^
> > >                         |
> > > rtw8821au  -----> (c) rtw8821a
> > >
> > > Put all common code to (b). IQK code in (a) or (c).
> > >
> > > I feel you have thought above picture already. What are problems we will encounter?
> > > Many export symbols? If so, how about below?
> > >
> > > rtw8812au  -----> (1) rtw8812a
> > >     +---------+
> > >               +-> (2) rtw8821a_common  (hard to give a name)
> > >     +---------+
> > > rtw8821au  -----> (3) rtw8821a
> > >
> > > Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
> > > respectively, and export IQK entry only. Does it work?
> > >
> > >
> > For the name of the common module, I was thinking rtw88_88xxa.ko.
> >
> > I wonder, what is the goal? To put the code in separate kernel
> > modules, or just separate files?
> 
> I would like to reduce runtime memory. As I asked, how many IQK code are different
> from them? If you have separated and compiled them, can you share size by the
> output of 'size' command?
> 
> >
> > I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
> > rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
> > rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
> > three files compile into a single module, rtw88_88xxa.ko.
> >
> > If each file compiles into a module of its own, we have circular
> > dependencies: rtw8821a_hw_spec -> common code -> IQK code.
> > If *_hw_spec go in the common module, it still depends on both of
> > the other two modules, so what use is it?
> 
> If we have dependency of common code -> IQK code, we can't save runtime
> memory, because common code reference to both IQK code. So I felt
> dependency of IQK code would be rtw8812au --> IQK code as above second
> diagram.
> 
> But if the work is complicated and save few runtime memory, we can
> use simple design as current did.
> 

Sorry. Correct typo. "... save few runtime memory ..." (remove 'not')
Bitterblue Smith Sept. 24, 2024, 9:19 p.m. UTC | #7
On 23/09/2024 08:47, Ping-Ke Shih wrote:
> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>>>>>>>> +
>>>>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
>>>>>>>
>>>>>>> Is it possible moving 8812a to individual file?
>>>>>>> Since you have rtw8812au.c and rtw8821au.c.
>>>>>>>
>>>>>>
>>>>>> I think it is possible. But most of the code is common to both chips.
>>>>>> Only the IQ calibration could be moved.
>>>>>
>>>>> Yep, depend on how much IQK code echo chip has.
>>>>>
>>>>
>>>> The IQ calibration for RTL8812AU is about 700 lines.
>>>
>>> rtw8812au  -----> (a) rtw8812a
>>>                         |
>>>                         v
>>>                   (b) rtw8821a_common  (hard to give a name)
>>>                         ^
>>>                         |
>>> rtw8821au  -----> (c) rtw8821a
>>>
>>> Put all common code to (b). IQK code in (a) or (c).
>>>
>>> I feel you have thought above picture already. What are problems we will encounter?
>>> Many export symbols? If so, how about below?
>>>
>>> rtw8812au  -----> (1) rtw8812a
>>>     +---------+
>>>               +-> (2) rtw8821a_common  (hard to give a name)
>>>     +---------+
>>> rtw8821au  -----> (3) rtw8821a
>>>
>>> Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
>>> respectively, and export IQK entry only. Does it work?
>>>
>>>
>> For the name of the common module, I was thinking rtw88_88xxa.ko.
>>
>> I wonder, what is the goal? To put the code in separate kernel
>> modules, or just separate files?
> 
> I would like to reduce runtime memory. As I asked, how many IQK code are different
> from them? If you have separated and compiled them, can you share size by the
> output of 'size' command? 
> 

I separated the IQK code into two files (still just one module).
size says:

   text    data     bss     dec     hex filename
   7192      32       0    7224    1c38 rtw8821a-iqk.o
  12319      40       0   12359    3047 rtw8812a-iqk.o

This is x86_64.

>>
>> I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
>> rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
>> rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
>> three files compile into a single module, rtw88_88xxa.ko.
>>
>> If each file compiles into a module of its own, we have circular
>> dependencies: rtw8821a_hw_spec -> common code -> IQK code.
>> If *_hw_spec go in the common module, it still depends on both of
>> the other two modules, so what use is it?
> 
> If we have dependency of common code -> IQK code, we can't save runtime
> memory, because common code reference to both IQK code. So I felt
> dependency of IQK code would be rtw8812au --> IQK code as above second
> diagram. 
> 
> But if the work is complicated and save not few runtime memory, we can
> use simple design as current did. 
> 
> 

The IQK code can be separated into different modules if I duplicate
rtw8821a_ops and rtw8821a_pwr_track, and rtw8821a_phy_pwrtrack takes
a pointer to the IQK function. Then your first diagram above can work.

The tables also take up a bit of space:

  text    data     bss     dec     hex filename
  16832       0       0   16832    41c0 rtw8821a_table.o
  21552       0       0   21552    5430 rtw8812a_table.o

I don't know how many kilobytes is enough to make it worth
creating two more modules.
Ping-Ke Shih Sept. 25, 2024, 1:25 a.m. UTC | #8
Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> On 23/09/2024 08:47, Ping-Ke Shih wrote:
> > Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> >>>>>>>> +
> >>>>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
> >>>>>>>
> >>>>>>> Is it possible moving 8812a to individual file?
> >>>>>>> Since you have rtw8812au.c and rtw8821au.c.
> >>>>>>>
> >>>>>>
> >>>>>> I think it is possible. But most of the code is common to both chips.
> >>>>>> Only the IQ calibration could be moved.
> >>>>>
> >>>>> Yep, depend on how much IQK code echo chip has.
> >>>>>
> >>>>
> >>>> The IQ calibration for RTL8812AU is about 700 lines.
> >>>
> >>> rtw8812au  -----> (a) rtw8812a
> >>>                         |
> >>>                         v
> >>>                   (b) rtw8821a_common  (hard to give a name)
> >>>                         ^
> >>>                         |
> >>> rtw8821au  -----> (c) rtw8821a
> >>>
> >>> Put all common code to (b). IQK code in (a) or (c).
> >>>
> >>> I feel you have thought above picture already. What are problems we will encounter?
> >>> Many export symbols? If so, how about below?
> >>>
> >>> rtw8812au  -----> (1) rtw8812a
> >>>     +---------+
> >>>               +-> (2) rtw8821a_common  (hard to give a name)
> >>>     +---------+
> >>> rtw8821au  -----> (3) rtw8821a
> >>>
> >>> Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
> >>> respectively, and export IQK entry only. Does it work?
> >>>
> >>>
> >> For the name of the common module, I was thinking rtw88_88xxa.ko.
> >>
> >> I wonder, what is the goal? To put the code in separate kernel
> >> modules, or just separate files?
> >
> > I would like to reduce runtime memory. As I asked, how many IQK code are different
> > from them? If you have separated and compiled them, can you share size by the
> > output of 'size' command?
> >
> 
> I separated the IQK code into two files (still just one module).
> size says:
> 
>    text    data     bss     dec     hex filename
>    7192      32       0    7224    1c38 rtw8821a-iqk.o
>   12319      40       0   12359    3047 rtw8812a-iqk.o
> 
> This is x86_64.
> 
> >>
> >> I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
> >> rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
> >> rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
> >> three files compile into a single module, rtw88_88xxa.ko.
> >>
> >> If each file compiles into a module of its own, we have circular
> >> dependencies: rtw8821a_hw_spec -> common code -> IQK code.
> >> If *_hw_spec go in the common module, it still depends on both of
> >> the other two modules, so what use is it?
> >
> > If we have dependency of common code -> IQK code, we can't save runtime
> > memory, because common code reference to both IQK code. So I felt
> > dependency of IQK code would be rtw8812au --> IQK code as above second
> > diagram.
> >
> > But if the work is complicated and save not few runtime memory, we can
> > use simple design as current did.
> >
> >
> 
> The IQK code can be separated into different modules if I duplicate
> rtw8821a_ops and rtw8821a_pwr_track, and rtw8821a_phy_pwrtrack takes
> a pointer to the IQK function. Then your first diagram above can work.

Not sure the "duplicate" you meant. If it only a struct, that would be fine.
Not prefer duplicate of tables.

> 
> The tables also take up a bit of space:
> 
>   text    data     bss     dec     hex filename
>   16832       0       0   16832    41c0 rtw8821a_table.o
>   21552       0       0   21552    5430 rtw8812a_table.o

Good point. 

> 
> I don't know how many kilobytes is enough to make it worth
> creating two more modules.

I think we can list all *.o related to rtw8821a/8812a, and check the
percentage to make decisions. I mean if it occupies 50%, I will prefer
to have separated module. But I don't have an exact number now.
Bitterblue Smith Sept. 25, 2024, 11:28 a.m. UTC | #9
On 25/09/2024 04:25, Ping-Ke Shih wrote:
> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>> On 23/09/2024 08:47, Ping-Ke Shih wrote:
>>> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>>>>>>>>>> +
>>>>>>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
>>>>>>>>>
>>>>>>>>> Is it possible moving 8812a to individual file?
>>>>>>>>> Since you have rtw8812au.c and rtw8821au.c.
>>>>>>>>>
>>>>>>>>
>>>>>>>> I think it is possible. But most of the code is common to both chips.
>>>>>>>> Only the IQ calibration could be moved.
>>>>>>>
>>>>>>> Yep, depend on how much IQK code echo chip has.
>>>>>>>
>>>>>>
>>>>>> The IQ calibration for RTL8812AU is about 700 lines.
>>>>>
>>>>> rtw8812au  -----> (a) rtw8812a
>>>>>                         |
>>>>>                         v
>>>>>                   (b) rtw8821a_common  (hard to give a name)
>>>>>                         ^
>>>>>                         |
>>>>> rtw8821au  -----> (c) rtw8821a
>>>>>
>>>>> Put all common code to (b). IQK code in (a) or (c).
>>>>>
>>>>> I feel you have thought above picture already. What are problems we will encounter?
>>>>> Many export symbols? If so, how about below?
>>>>>
>>>>> rtw8812au  -----> (1) rtw8812a
>>>>>     +---------+
>>>>>               +-> (2) rtw8821a_common  (hard to give a name)
>>>>>     +---------+
>>>>> rtw8821au  -----> (3) rtw8821a
>>>>>
>>>>> Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
>>>>> respectively, and export IQK entry only. Does it work?
>>>>>
>>>>>
>>>> For the name of the common module, I was thinking rtw88_88xxa.ko.
>>>>
>>>> I wonder, what is the goal? To put the code in separate kernel
>>>> modules, or just separate files?
>>>
>>> I would like to reduce runtime memory. As I asked, how many IQK code are different
>>> from them? If you have separated and compiled them, can you share size by the
>>> output of 'size' command?
>>>
>>
>> I separated the IQK code into two files (still just one module).
>> size says:
>>
>>    text    data     bss     dec     hex filename
>>    7192      32       0    7224    1c38 rtw8821a-iqk.o
>>   12319      40       0   12359    3047 rtw8812a-iqk.o
>>
>> This is x86_64.
>>
>>>>
>>>> I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
>>>> rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
>>>> rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
>>>> three files compile into a single module, rtw88_88xxa.ko.
>>>>
>>>> If each file compiles into a module of its own, we have circular
>>>> dependencies: rtw8821a_hw_spec -> common code -> IQK code.
>>>> If *_hw_spec go in the common module, it still depends on both of
>>>> the other two modules, so what use is it?
>>>
>>> If we have dependency of common code -> IQK code, we can't save runtime
>>> memory, because common code reference to both IQK code. So I felt
>>> dependency of IQK code would be rtw8812au --> IQK code as above second
>>> diagram.
>>>
>>> But if the work is complicated and save not few runtime memory, we can
>>> use simple design as current did.
>>>
>>>
>>
>> The IQK code can be separated into different modules if I duplicate
>> rtw8821a_ops and rtw8821a_pwr_track, and rtw8821a_phy_pwrtrack takes
>> a pointer to the IQK function. Then your first diagram above can work.
> 
> Not sure the "duplicate" you meant. If it only a struct, that would be fine.
> Not prefer duplicate of tables.
> 

Yes, it's a struct rtw_chip_ops.

>>
>> The tables also take up a bit of space:
>>
>>   text    data     bss     dec     hex filename
>>   16832       0       0   16832    41c0 rtw8821a_table.o
>>   21552       0       0   21552    5430 rtw8812a_table.o
> 
> Good point. 
> 
>>
>> I don't know how many kilobytes is enough to make it worth
>> creating two more modules.
> 
> I think we can list all *.o related to rtw8821a/8812a, and check the
> percentage to make decisions. I mean if it occupies 50%, I will prefer
> to have separated module. But I don't have an exact number now. 
> 

   text    data     bss     dec     hex filename
  12319      40       0   12359    3047 rtw8812a-iqk.o
  21552       0       0   21552    5430 rtw8812a_table.o
   7192      32       0    7224    1c38 rtw8821a-iqk.o
  16832       0       0   16832    41c0 rtw8821a_table.o
  29445     429       0   29874    74b2 rtw8821a.o
=========
  87340 total. So it's about 38% for 8812a and 27% for 8821a.
Maybe a bit more in the final version.
Ping-Ke Shih Sept. 26, 2024, 2:27 a.m. UTC | #10
Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> On 25/09/2024 04:25, Ping-Ke Shih wrote:
> > Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> >> On 23/09/2024 08:47, Ping-Ke Shih wrote:
> >>> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
> >>>>>>>>>> +
> >>>>>>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
> >>>>>>>>>
> >>>>>>>>> Is it possible moving 8812a to individual file?
> >>>>>>>>> Since you have rtw8812au.c and rtw8821au.c.
> >>>>>>>>>
> >>>>>>>>
> >>>>>>>> I think it is possible. But most of the code is common to both chips.
> >>>>>>>> Only the IQ calibration could be moved.
> >>>>>>>
> >>>>>>> Yep, depend on how much IQK code echo chip has.
> >>>>>>>
> >>>>>>
> >>>>>> The IQ calibration for RTL8812AU is about 700 lines.
> >>>>>
> >>>>> rtw8812au  -----> (a) rtw8812a
> >>>>>                         |
> >>>>>                         v
> >>>>>                   (b) rtw8821a_common  (hard to give a name)
> >>>>>                         ^
> >>>>>                         |
> >>>>> rtw8821au  -----> (c) rtw8821a
> >>>>>
> >>>>> Put all common code to (b). IQK code in (a) or (c).
> >>>>>
> >>>>> I feel you have thought above picture already. What are problems we will encounter?
> >>>>> Many export symbols? If so, how about below?
> >>>>>
> >>>>> rtw8812au  -----> (1) rtw8812a
> >>>>>     +---------+
> >>>>>               +-> (2) rtw8821a_common  (hard to give a name)
> >>>>>     +---------+
> >>>>> rtw8821au  -----> (3) rtw8821a
> >>>>>
> >>>>> Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
> >>>>> respectively, and export IQK entry only. Does it work?
> >>>>>
> >>>>>
> >>>> For the name of the common module, I was thinking rtw88_88xxa.ko.
> >>>>
> >>>> I wonder, what is the goal? To put the code in separate kernel
> >>>> modules, or just separate files?
> >>>
> >>> I would like to reduce runtime memory. As I asked, how many IQK code are different
> >>> from them? If you have separated and compiled them, can you share size by the
> >>> output of 'size' command?
> >>>
> >>
> >> I separated the IQK code into two files (still just one module).
> >> size says:
> >>
> >>    text    data     bss     dec     hex filename
> >>    7192      32       0    7224    1c38 rtw8821a-iqk.o
> >>   12319      40       0   12359    3047 rtw8812a-iqk.o
> >>
> >> This is x86_64.
> >>
> >>>>
> >>>> I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
> >>>> rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
> >>>> rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
> >>>> three files compile into a single module, rtw88_88xxa.ko.
> >>>>
> >>>> If each file compiles into a module of its own, we have circular
> >>>> dependencies: rtw8821a_hw_spec -> common code -> IQK code.
> >>>> If *_hw_spec go in the common module, it still depends on both of
> >>>> the other two modules, so what use is it?
> >>>
> >>> If we have dependency of common code -> IQK code, we can't save runtime
> >>> memory, because common code reference to both IQK code. So I felt
> >>> dependency of IQK code would be rtw8812au --> IQK code as above second
> >>> diagram.
> >>>
> >>> But if the work is complicated and save not few runtime memory, we can
> >>> use simple design as current did.
> >>>
> >>>
> >>
> >> The IQK code can be separated into different modules if I duplicate
> >> rtw8821a_ops and rtw8821a_pwr_track, and rtw8821a_phy_pwrtrack takes
> >> a pointer to the IQK function. Then your first diagram above can work.
> >
> > Not sure the "duplicate" you meant. If it only a struct, that would be fine.
> > Not prefer duplicate of tables.
> >
> 
> Yes, it's a struct rtw_chip_ops.
> 
> >>
> >> The tables also take up a bit of space:
> >>
> >>   text    data     bss     dec     hex filename
> >>   16832       0       0   16832    41c0 rtw8821a_table.o
> >>   21552       0       0   21552    5430 rtw8812a_table.o
> >
> > Good point.
> >
> >>
> >> I don't know how many kilobytes is enough to make it worth
> >> creating two more modules.
> >
> > I think we can list all *.o related to rtw8821a/8812a, and check the
> > percentage to make decisions. I mean if it occupies 50%, I will prefer
> > to have separated module. But I don't have an exact number now.
> >
> 
>    text    data     bss     dec     hex filename
>   12319      40       0   12359    3047 rtw8812a-iqk.o
>   21552       0       0   21552    5430 rtw8812a_table.o
>    7192      32       0    7224    1c38 rtw8821a-iqk.o
>   16832       0       0   16832    41c0 rtw8821a_table.o
>   29445     429       0   29874    74b2 rtw8821a.o
> =========
>   87340 total. So it's about 38% for 8812a and 27% for 8821a.
> Maybe a bit more in the final version.

chip    separated(a)   single one(b)   increase rate(c)
-----   ------------   -------------   ----------------
8812a   63,785         87,841          38%
8821a   53,930         87,841          63%

* increase rate (c) = (b - a) / a

Since increasing rate of 8821a is 63%, I feel separated case would be better.
Bitterblue Smith Sept. 26, 2024, 4:41 p.m. UTC | #11
On 26/09/2024 05:27, Ping-Ke Shih wrote:
> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>> On 25/09/2024 04:25, Ping-Ke Shih wrote:
>>> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>>>> On 23/09/2024 08:47, Ping-Ke Shih wrote:
>>>>> Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>>>>>>>>>>>> +
>>>>>>>>>>>> +const struct rtw_chip_info rtw8812a_hw_spec = {
>>>>>>>>>>>
>>>>>>>>>>> Is it possible moving 8812a to individual file?
>>>>>>>>>>> Since you have rtw8812au.c and rtw8821au.c.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> I think it is possible. But most of the code is common to both chips.
>>>>>>>>>> Only the IQ calibration could be moved.
>>>>>>>>>
>>>>>>>>> Yep, depend on how much IQK code echo chip has.
>>>>>>>>>
>>>>>>>>
>>>>>>>> The IQ calibration for RTL8812AU is about 700 lines.
>>>>>>>
>>>>>>> rtw8812au  -----> (a) rtw8812a
>>>>>>>                         |
>>>>>>>                         v
>>>>>>>                   (b) rtw8821a_common  (hard to give a name)
>>>>>>>                         ^
>>>>>>>                         |
>>>>>>> rtw8821au  -----> (c) rtw8821a
>>>>>>>
>>>>>>> Put all common code to (b). IQK code in (a) or (c).
>>>>>>>
>>>>>>> I feel you have thought above picture already. What are problems we will encounter?
>>>>>>> Many export symbols? If so, how about below?
>>>>>>>
>>>>>>> rtw8812au  -----> (1) rtw8812a
>>>>>>>     +---------+
>>>>>>>               +-> (2) rtw8821a_common  (hard to give a name)
>>>>>>>     +---------+
>>>>>>> rtw8821au  -----> (3) rtw8821a
>>>>>>>
>>>>>>> Put rtw8812a_hw_spec and rtw8821a_hw_spec in (2). Only IQK code in (1) and (3)
>>>>>>> respectively, and export IQK entry only. Does it work?
>>>>>>>
>>>>>>>
>>>>>> For the name of the common module, I was thinking rtw88_88xxa.ko.
>>>>>>
>>>>>> I wonder, what is the goal? To put the code in separate kernel
>>>>>> modules, or just separate files?
>>>>>
>>>>> I would like to reduce runtime memory. As I asked, how many IQK code are different
>>>>> from them? If you have separated and compiled them, can you share size by the
>>>>> output of 'size' command?
>>>>>
>>>>
>>>> I separated the IQK code into two files (still just one module).
>>>> size says:
>>>>
>>>>    text    data     bss     dec     hex filename
>>>>    7192      32       0    7224    1c38 rtw8821a-iqk.o
>>>>   12319      40       0   12359    3047 rtw8812a-iqk.o
>>>>
>>>> This is x86_64.
>>>>
>>>>>>
>>>>>> I think we can have rtw88xxa.c (common code), rtw8821a.c (IQK code,
>>>>>> rtw8821a_hw_spec, bluetooth stuff), and rtw8812a.c (IQK code,
>>>>>> rtw8812a_hw_spec, some efuse stuff, channel switching)... if these
>>>>>> three files compile into a single module, rtw88_88xxa.ko.
>>>>>>
>>>>>> If each file compiles into a module of its own, we have circular
>>>>>> dependencies: rtw8821a_hw_spec -> common code -> IQK code.
>>>>>> If *_hw_spec go in the common module, it still depends on both of
>>>>>> the other two modules, so what use is it?
>>>>>
>>>>> If we have dependency of common code -> IQK code, we can't save runtime
>>>>> memory, because common code reference to both IQK code. So I felt
>>>>> dependency of IQK code would be rtw8812au --> IQK code as above second
>>>>> diagram.
>>>>>
>>>>> But if the work is complicated and save not few runtime memory, we can
>>>>> use simple design as current did.
>>>>>
>>>>>
>>>>
>>>> The IQK code can be separated into different modules if I duplicate
>>>> rtw8821a_ops and rtw8821a_pwr_track, and rtw8821a_phy_pwrtrack takes
>>>> a pointer to the IQK function. Then your first diagram above can work.
>>>
>>> Not sure the "duplicate" you meant. If it only a struct, that would be fine.
>>> Not prefer duplicate of tables.
>>>
>>
>> Yes, it's a struct rtw_chip_ops.
>>
>>>>
>>>> The tables also take up a bit of space:
>>>>
>>>>   text    data     bss     dec     hex filename
>>>>   16832       0       0   16832    41c0 rtw8821a_table.o
>>>>   21552       0       0   21552    5430 rtw8812a_table.o
>>>
>>> Good point.
>>>
>>>>
>>>> I don't know how many kilobytes is enough to make it worth
>>>> creating two more modules.
>>>
>>> I think we can list all *.o related to rtw8821a/8812a, and check the
>>> percentage to make decisions. I mean if it occupies 50%, I will prefer
>>> to have separated module. But I don't have an exact number now.
>>>
>>
>>    text    data     bss     dec     hex filename
>>   12319      40       0   12359    3047 rtw8812a-iqk.o
>>   21552       0       0   21552    5430 rtw8812a_table.o
>>    7192      32       0    7224    1c38 rtw8821a-iqk.o
>>   16832       0       0   16832    41c0 rtw8821a_table.o
>>   29445     429       0   29874    74b2 rtw8821a.o
>> =========
>>   87340 total. So it's about 38% for 8812a and 27% for 8821a.
>> Maybe a bit more in the final version.
> 
> chip    separated(a)   single one(b)   increase rate(c)
> -----   ------------   -------------   ----------------
> 8812a   63,785         87,841          38%
> 8821a   53,930         87,841          63%
> 
> * increase rate (c) = (b - a) / a
> 
> Since increasing rate of 8821a is 63%, I feel separated case would be better. 
> 
> 

All right.
diff mbox series

Patch

diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
new file mode 100644
index 000000000000..e72599db74ed
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c
@@ -0,0 +1,4139 @@ 
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2018-2019  Realtek Corporation
+ */
+
+#include <linux/usb.h>
+#include "main.h"
+#include "coex.h"
+#include "fw.h"
+#include "tx.h"
+#include "rx.h"
+#include "phy.h"
+#include "rtw8821a.h"
+#include "rtw8821a_table.h"
+#include "rtw8812a_table.h"
+#include "mac.h"
+#include "reg.h"
+#include "sec.h"
+#include "debug.h"
+#include "bf.h"
+#include "regd.h"
+#include "efuse.h"
+#include "usb.h"
+
+static void rtw8821a_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);
+	}
+}
+
+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 rtw8821a_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 rtw8812a_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)
+		return;
+
+	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);
+		}
+	}
+}
+
+static int rtw8821a_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw8821a_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 rtw8821a_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;
+
+	rtw8812a_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;
+}
+
+static void rtw8821a_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 rtw8812au_hw_reset(struct rtw_dev *rtwdev)
+{
+	u8 val8;
+
+	if (!(rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_RAM_DL_SEL))
+		return;
+
+	rtw8821a_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 rtw8812au_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 = HCI_TXDMA_EN | HCI_RXDMA_EN | TXDMA_EN | RXDMA_EN |
+		PROTOCOL_EN | SCHEDULE_EN | ENSEC | CALTMR_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 rtw8821a_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 rtw8821a_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 = rtw8821a_llt_write(rtwdev, i, i + 1);
+		if (status)
+			return status;
+	}
+
+	status = rtw8821a_llt_write(rtwdev, boundary - 1, 0xFF);
+	if (status)
+		return status;
+
+	for (i = boundary; i < last_entry; i++) {
+		status = rtw8821a_llt_write(rtwdev, i, i + 1);
+		if (status)
+			return status;
+	}
+
+	status = rtw8821a_llt_write(rtwdev, last_entry, boundary);
+
+	return status;
+}
+
+static void rtw8821au_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 rtw8821au_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 rtw8821au_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 rtw8821a_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 rtw8821a_init_adaptive_ctrl(struct rtw_dev *rtwdev)
+{
+	rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xffff1);
+	rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030);
+}
+
+static void rtw8821a_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 rtw8821au_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 rtw8821a_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 rtw8821a_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 rtw8821a_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, 0x8bc, 0xc0000060, 0x4);
+
+	/* RF Path_B HSSI OFF */
+	rtw_write32_mask(rtwdev, 0xe00, 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 rtw8821a_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 rtw8821a_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 rtw8821a_get_swing_index(struct rtw_dev *rtwdev)
+{
+	u32 swing, table_value;
+	u8 i = 0;
+
+	swing = rtw8821a_get_bb_swing(rtwdev, rtwdev->hal.current_band_type,
+				      RF_PATH_A);
+
+	for (i = 0; i < ARRAY_SIZE(rtw8821a_txscale_tbl); i++) {
+		table_value = rtw8821a_txscale_tbl[i];
+		if (swing == table_value)
+			break;
+	}
+
+	return i;
+}
+
+static void rtw8821a_pwrtrack_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u8 ofdm_swing_idx;
+	u8 path;
+
+	ofdm_swing_idx = rtw8821a_get_swing_index(rtwdev);
+
+	if (ofdm_swing_idx >= ARRAY_SIZE(rtw8821a_txscale_tbl))
+		dm_info->default_ofdm_index = 24;
+	else
+		dm_info->default_ofdm_index = ofdm_swing_idx;
+
+	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;
+}
+
+static void rtw8821a_power_off(struct rtw_dev *rtwdev)
+{
+	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, 0xf008, 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);
+
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
+		rtw_pwr_seq_parser(rtwdev, enter_lps_flow_8821a);
+	else
+		rtw_pwr_seq_parser(rtwdev, enter_lps_flow_8812a);
+
+	if (rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_RAM_DL_SEL)
+		rtw8821a_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);
+}
+
+static void rtw8821a_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 band)
+{
+	rtw_write32_mask(rtwdev, REG_TXSCALE_A, BB_SWING_MASK,
+			 rtw8821a_get_bb_swing(rtwdev, band, RF_PATH_A));
+	rtw_write32_mask(rtwdev, REG_TXSCALE_B, BB_SWING_MASK,
+			 rtw8821a_get_bb_swing(rtwdev, band, RF_PATH_B));
+	rtw8821a_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 rtw8821a_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);
+	}
+
+	rtw8821a_set_channel_bb_swing(rtwdev, new_band);
+}
+
+static int rtw8821a_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.
+	 */
+	rtw8812au_hw_reset(rtwdev);
+
+	ret = rtw8812au_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 = rtw8821a_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);
+
+	rtw8821au_init_queue_reserved_page(rtwdev);
+	rtw8821au_init_tx_buffer_boundary(rtwdev);
+	rtw8821au_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);
+
+	rtw8821a_init_wmac_setting(rtwdev);
+	rtw8821a_init_adaptive_ctrl(rtwdev);
+	rtw8821a_init_edca(rtwdev);
+
+	rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_ACKTO, 0x80);
+
+	rtw8821au_tx_aggregation(rtwdev);
+
+	rtw8821a_init_beacon_parameters(rtwdev);
+	rtw_write8(rtwdev, REG_BCN_MAX_ERR, 0xff);
+
+	rtw_hci_interface_cfg(rtwdev);
+
+	/* usb3 rx interval */
+	rtw_write8(rtwdev, 0xf050, 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, 0xf008, 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, MACTXEN | MACRXEN);
+
+	rtw8821a_phy_bb_config(rtwdev);
+	rtw8821a_phy_rf_config(rtwdev);
+
+	if (chip->id == RTW_CHIP_TYPE_8812A && hal->rf_path_num == 1)
+		rtw8812a_config_1t(rtwdev);
+
+	rtw8821a_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);
+
+	rtw8821a_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:
+	rtw8821a_power_off(rtwdev);
+
+err:
+	return ret;
+}
+
+static u32 rtw8821a_phy_read_rf(struct rtw_dev *rtwdev,
+				enum rtw_rf_path rf_path, u32 addr, u32 mask)
+{
+	static const u32 pi_addr[2] = { 0xc00, 0xe00 };
+	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;
+}
+
+static void rtw8821a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable)
+{
+	/* Nothing to do. */
+}
+
+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 rtw8821a_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, 0x18,
+			     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, 0x18, RF18_CHANNEL_MASK, channel);
+	}
+}
+
+static void rtw8821a_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 rtw8821a_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;
+
+	rtw8821a_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 rtw8821a_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, 0x18, RF18_BW_MASK, 3);
+			break;
+		case RTW_CHANNEL_WIDTH_40:
+			rtw_write_rf(rtwdev, path, 0x18, RF18_BW_MASK, 1);
+			break;
+		case RTW_CHANNEL_WIDTH_80:
+			rtw_write_rf(rtwdev, path, 0x18, RF18_BW_MASK, 0);
+			break;
+		}
+	}
+}
+
+static void rtw8821a_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)
+		rtw8821a_switch_band(rtwdev, new_band, bw);
+
+	rtw8821a_switch_channel(rtwdev, channel, bw);
+
+	rtw8821a_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);
+
+	rtw8821a_set_channel_rf(rtwdev, channel, bw);
+}
+
+static s8 rtw8821a_cck_rx_pwr(struct rtw_dev *rtwdev, u8 lna_idx, u8 vga_idx)
+{
+	static const s8 lna_gain_table[] = {15, -1, -17, 0, -30, -38};
+	s8 rx_pwr_all = 0;
+	s8 lna_gain;
+
+	switch (lna_idx) {
+	case 5:
+	case 4:
+	case 2:
+	case 1:
+	case 0:
+		lna_gain = lna_gain_table[lna_idx];
+		rx_pwr_all = lna_gain - 2 * vga_idx;
+		break;
+	default:
+		break;
+	}
+
+	return rx_pwr_all;
+}
+
+static s8 rtw8812a_cck_rx_pwr(struct rtw_dev *rtwdev, u8 lna_idx, u8 vga_idx)
+{
+	s8 rx_pwr_all = 0;
+
+	switch (lna_idx) {
+	case 7:
+		if (vga_idx <= 27)
+			rx_pwr_all = -94 + 2 * (27 - vga_idx);
+		else
+			rx_pwr_all = -94;
+		break;
+	case 6:
+		rx_pwr_all = -42 + 2 * (2 - vga_idx);
+		break;
+	case 5:
+		rx_pwr_all = -36 + 2 * (7 - vga_idx);
+		break;
+	case 4:
+		rx_pwr_all = -30 + 2 * (7 - vga_idx);
+		break;
+	case 3:
+		rx_pwr_all = -18 + 2 * (7 - vga_idx);
+		break;
+	case 2:
+		rx_pwr_all = 2 * (5 - vga_idx);
+		break;
+	case 1:
+		rx_pwr_all = 14 - 2 * vga_idx;
+		break;
+	case 0:
+		rx_pwr_all = 20 - 2 * vga_idx;
+		break;
+	default:
+		break;
+	}
+
+	return rx_pwr_all;
+}
+
+static void rtw8821a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
+				      struct rtw_rx_pkt_stat *pkt_stat)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw8821a_phy_status_rpt *phy_sts;
+	u8 lna_idx, vga_idx, cck_agc_rpt;
+	s8 rx_pwr_db, power_a, power_b;
+	const s8 min_rx_power = -120;
+	u8 rssi, val, i;
+
+	phy_sts = (struct rtw8821a_phy_status_rpt *)phy_status;
+
+	if (pkt_stat->rate <= DESC_RATE11M) {
+		cck_agc_rpt = phy_sts->cfosho[0];
+		lna_idx = (cck_agc_rpt & 0xE0) >> 5;
+		vga_idx = cck_agc_rpt & 0x1F;
+
+		if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
+			rx_pwr_db = rtw8821a_cck_rx_pwr(rtwdev, lna_idx, vga_idx);
+		else
+			rx_pwr_db = rtw8812a_cck_rx_pwr(rtwdev, 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;
+
+		if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A &&
+		    !rtwdev->hal.cck_high_power) {
+			if (pkt_stat->rssi >= 80)
+				pkt_stat->rssi = ((pkt_stat->rssi - 80) << 1) +
+						 ((pkt_stat->rssi - 80) >> 1) + 80;
+			else if (pkt_stat->rssi <= 78 && pkt_stat->rssi >= 20)
+				pkt_stat->rssi += 3;
+		}
+	} else { /* OFDM rate */
+		for (i = RF_PATH_A; i < rtwdev->hal.rf_path_num; i++) {
+			val = phy_sts->gain_trsw[i];
+			pkt_stat->rx_power[i] = (val & 0x7F) - 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);
+	}
+}
+
+static void rtw8821a_query_rx_desc(struct rtw_dev *rtwdev, u8 *rx_desc,
+				   struct rtw_rx_pkt_stat *pkt_stat,
+				   struct ieee80211_rx_status *rx_status)
+{
+	u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz;
+	struct ieee80211_hdr *hdr;
+	u8 *phy_status = NULL;
+
+	memset(pkt_stat, 0, sizeof(*pkt_stat));
+
+	pkt_stat->phy_status = GET_RX_DESC_PHYST(rx_desc);
+	pkt_stat->icv_err = GET_RX_DESC_ICV_ERR(rx_desc);
+	pkt_stat->crc_err = GET_RX_DESC_CRC32(rx_desc);
+	pkt_stat->decrypted = !GET_RX_DESC_SWDEC(rx_desc) &&
+			      GET_RX_DESC_ENC_TYPE(rx_desc) != RX_DESC_ENC_NONE;
+	pkt_stat->is_c2h = GET_RX_DESC_C2H(rx_desc);
+	pkt_stat->pkt_len = GET_RX_DESC_PKT_LEN(rx_desc);
+	pkt_stat->drv_info_sz = GET_RX_DESC_DRV_INFO_SIZE(rx_desc);
+	pkt_stat->shift = GET_RX_DESC_SHIFT(rx_desc);
+	pkt_stat->rate = GET_RX_DESC_RX_RATE(rx_desc);
+	pkt_stat->cam_id = GET_RX_DESC_MACID(rx_desc);
+	pkt_stat->ppdu_cnt = 0;
+	pkt_stat->tsf_low = GET_RX_DESC_TSFL(rx_desc);
+	pkt_stat->bw = GET_RX_DESC_BW(rx_desc);
+
+	/* drv_info_sz is in unit of 8-bytes */
+	pkt_stat->drv_info_sz *= 8;
+
+	/* c2h cmd pkt's rx/phy status is not interested */
+	if (pkt_stat->is_c2h)
+		return;
+
+	hdr = (struct ieee80211_hdr *)(rx_desc + desc_sz + pkt_stat->shift +
+				       pkt_stat->drv_info_sz);
+	if (pkt_stat->phy_status) {
+		phy_status = rx_desc + desc_sz + pkt_stat->shift;
+		rtw8821a_query_phy_status(rtwdev, phy_status, pkt_stat);
+	}
+
+	rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status, phy_status);
+}
+
+static void
+rtw8821a_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path,
+				    u8 rs, u32 *phy_pwr_idx)
+{
+	static const u32 offset_txagc[2] = {0xc20, 0xe20};
+	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 rtw8821a_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);
+}
+
+static void rtw8821a_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_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;
+
+			rtw8821a_set_tx_power_index_by_rate(rtwdev, path, rs,
+							    &phy_pwr_idx);
+		}
+
+		rtw8821a_tx_power_training(rtwdev, hal->current_band_width,
+					   hal->current_channel, path);
+	}
+}
+
+static void rtw8821a_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 = FIELD_GET(GENMASK(15, 0), crc32_cnt);
+	dm_info->cck_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);
+
+	crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM);
+	dm_info->ofdm_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt);
+	dm_info->ofdm_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);
+
+	crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT);
+	dm_info->ht_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt);
+	dm_info->ht_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);
+
+	crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT);
+	dm_info->vht_ok_cnt = FIELD_GET(GENMASK(15, 0), crc32_cnt);
+	dm_info->vht_err_cnt = FIELD_GET(GENMASK(31, 16), crc32_cnt);
+
+	cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM);
+	dm_info->ofdm_cca_cnt = FIELD_GET(GENMASK(31, 16), cca32_cnt);
+	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 = FIELD_GET(GENMASK(15, 0), cca32_cnt);
+		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_RXDESC, BIT(15));
+	rtw_write32_set(rtwdev, REG_RXDESC, BIT(15));
+	rtw_write32_set(rtwdev, REG_CNTRST, BIT(0));
+	rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0));
+}
+
+#define CAL_NUM_8821A 3
+#define MACBB_REG_NUM_8821A 8
+#define AFE_REG_NUM_8821A 4
+#define RF_REG_NUM_8821A 3
+
+static void rtw8821a_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]);
+}
+
+static void rtw8821a_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]);
+}
+
+static void rtw8821a_iqk_backup_rf(struct rtw_dev *rtwdev, u32 *rfa_backup,
+				   const u32 *backup_rf_reg, u32 rf_num)
+{
+	u32 i;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	/* Save RF Parameters */
+	for (i = 0; i < rf_num; i++)
+		rfa_backup[i] = rtw_read_rf(rtwdev, RF_PATH_A,
+					    backup_rf_reg[i], MASKDWORD);
+}
+
+static void rtw8821a_iqk_restore_rf(struct rtw_dev *rtwdev,
+				    const u32 *backup_rf_reg,
+				    u32 *RF_backup, u32 rf_reg_num)
+{
+	u32 i;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	for (i = 0; i < rf_reg_num; i++)
+		rtw_write_rf(rtwdev, RF_PATH_A, backup_rf_reg[i],
+			     RFREG_MASK, RF_backup[i]);
+}
+
+static void rtw8821a_iqk_restore_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);
+
+	/* Reload AFE Parameters */
+	for (i = 0; i < afe_num; i++)
+		rtw_write32(rtwdev, backup_afe_reg[i], afe_backup[i]);
+
+	/* [31] = 1 --> Page C1 */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+	rtw_write32(rtwdev, 0xc80, 0x0);
+	rtw_write32(rtwdev, 0xc84, 0x0);
+	rtw_write32(rtwdev, 0xc88, 0x0);
+	rtw_write32(rtwdev, 0xc8c, 0x3c000000);
+	rtw_write32(rtwdev, REG_LSSI_WRITE_A, 0x00000080);
+	rtw_write32(rtwdev, 0xc94, 0x00000000);
+	rtw_write32(rtwdev, 0xcc4, 0x20040000);
+	rtw_write32(rtwdev, 0xcc8, 0x20000000);
+	rtw_write32(rtwdev, REG_RFECTL_A, 0x0);
+}
+
+static void rtw8821a_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]);
+}
+
+static void rtw8821a_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);
+}
+
+static void rtw8821a_iqk_rx_fill(struct rtw_dev *rtwdev,
+				 unsigned int rx_x, unsigned int rx_y)
+{
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A,
+			 0x000003ff, rx_x >> 1);
+	rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A,
+			 0x03ff0000, (rx_y >> 1) & 0x3ff);
+}
+
+static void rtw8821a_iqk_tx_fill(struct rtw_dev *rtwdev,
+				 unsigned int tx_x, unsigned int tx_y)
+{
+	/* [31] = 1 --> Page C1 */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+	rtw_write32(rtwdev, REG_LSSI_WRITE_A, 0x00000080);
+	rtw_write32(rtwdev, 0xcc4, 0x20040000);
+	rtw_write32(rtwdev, 0xcc8, 0x20000000);
+	rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, tx_y);
+	rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, tx_x);
+}
+
+static void rtw8821a_iqk_tx_vdf_true(struct rtw_dev *rtwdev, u32 cal,
+				     bool *tx0iqkok,
+				     int tx_x0[CAL_NUM_8821A],
+				     int tx_y0[CAL_NUM_8821A])
+{
+	u32 cal_retry, delay_count, iqk_ready, tx_fail;
+	int tx_dt[3], vdf_y[3], vdf_x[3];
+	int k;
+
+	for (k = 0; k <= 2; k++) {
+		switch (k) {
+		case 0:
+			/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+			rtw_write32(rtwdev, 0xc80, 0x18008c38);
+			/* RX_Tone_idx[9:0], RxK_Mask[29] */
+			rtw_write32(rtwdev, 0xc84, 0x38008c38);
+			rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x0);
+			break;
+		case 1:
+			rtw_write32_mask(rtwdev, 0xc80, BIT(28), 0x0);
+			rtw_write32_mask(rtwdev, 0xc84, BIT(28), 0x0);
+			rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x0);
+			break;
+		case 2:
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"vdf_y[1] = %x vdf_y[0] = %x\n",
+				vdf_y[1] >> 21 & 0x00007ff,
+				vdf_y[0] >> 21 & 0x00007ff);
+
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"vdf_x[1] = %x vdf_x[0] = %x\n",
+				vdf_x[1] >> 21 & 0x00007ff,
+				vdf_x[0] >> 21 & 0x00007ff);
+
+			tx_dt[cal] = (vdf_y[1] >> 20) - (vdf_y[0] >> 20);
+			tx_dt[cal] = (16 * tx_dt[cal]) * 10000 / 15708;
+			tx_dt[cal] = (tx_dt[cal] >> 1) + (tx_dt[cal] & BIT(0));
+
+			/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+			rtw_write32(rtwdev, 0xc80, 0x18008c20);
+			/* RX_Tone_idx[9:0], RxK_Mask[29] */
+			rtw_write32(rtwdev, 0xc84, 0x38008c20);
+			rtw_write32_mask(rtwdev, 0xce8, BIT(31), 0x1);
+			rtw_write32_mask(rtwdev, 0xce8, 0x3fff0000,
+					 tx_dt[cal] & 0x00003fff);
+			break;
+		}
+
+		rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
+		cal_retry = 0;
+		while (1) {
+			/* one shot */
+			rtw_write32(rtwdev, 0x980, 0xfa000000);
+			rtw_write32(rtwdev, 0x980, 0xf8000000);
+
+			mdelay(10);
+
+			rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
+
+			delay_count = 0;
+			while (1) {
+				iqk_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
+
+				/* Originally: if (~iqk_ready || delay_count > 20)
+				 * that looks like a typo so make it more explicit
+				 */
+				iqk_ready = true;
+
+				if (iqk_ready || delay_count > 20)
+					break;
+
+				mdelay(1);
+				delay_count++;
+			}
+
+			if (delay_count < 20) {
+				/* ============TXIQK Check============== */
+				tx_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(12));
+
+				/* Originally: if (~tx_fail) {
+				 * It looks like a typo, so make it more explicit.
+				 */
+				tx_fail = false;
+
+				if (!tx_fail) {
+					rtw_write32(rtwdev, REG_RFECTL_A,
+						    0x02000000);
+					vdf_x[k] = rtw_read32_mask(rtwdev, 0xd00,
+								   0x07ff0000);
+					vdf_x[k] <<= 21;
+
+					rtw_write32(rtwdev, REG_RFECTL_A,
+						    0x04000000);
+					vdf_y[k] = rtw_read32_mask(rtwdev, 0xd00,
+								   0x07ff0000);
+					vdf_y[k] <<= 21;
+
+					*tx0iqkok = true;
+					break;
+				}
+
+				rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, 0x0);
+				rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, 0x200);
+
+				*tx0iqkok = false;
+				cal_retry++;
+				if (cal_retry == 10)
+					break;
+			} else { /* If 20ms No Result, then cal_retry++ */
+				*tx0iqkok = false;
+				cal_retry++;
+				if (cal_retry == 10)
+					break;
+			}
+		}
+	}
+
+	if (k == 3) {
+		tx_x0[cal] = vdf_x[k - 1];
+		tx_y0[cal] = vdf_y[k - 1];
+	}
+}
+
+static void rtw8821a_iqk_tx_vdf_false(struct rtw_dev *rtwdev, u32 cal,
+				      bool *tx0iqkok,
+				      int tx_x0[CAL_NUM_8821A],
+				      int tx_y0[CAL_NUM_8821A])
+{
+	u32 cal_retry, delay_count, iqk_ready, tx_fail;
+
+	/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+	rtw_write32(rtwdev, 0xc80, 0x18008c10);
+	/* RX_Tone_idx[9:0], RxK_Mask[29] */
+	rtw_write32(rtwdev, 0xc84, 0x38008c10);
+	rtw_write32(rtwdev, 0xcb8, 0x00100000);
+
+	cal_retry = 0;
+	while (1) {
+		/* one shot */
+		rtw_write32(rtwdev, 0x980, 0xfa000000);
+		rtw_write32(rtwdev, 0x980, 0xf8000000);
+
+		mdelay(10);
+		rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
+
+		delay_count = 0;
+		while (1) {
+			iqk_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
+
+			/* Originally: if (~iqk_ready || delay_count > 20)
+			 * that looks like a typo so make it more explicit
+			 */
+			iqk_ready = true;
+
+			if (iqk_ready || delay_count > 20)
+				break;
+
+			mdelay(1);
+			delay_count++;
+		}
+
+		if (delay_count < 20) {
+			/* ============TXIQK Check============== */
+			tx_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(12));
+
+			/* Originally: if (~tx_fail) {
+			 * It looks like a typo, so make it more explicit.
+			 */
+			tx_fail = false;
+
+			if (!tx_fail) {
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x02000000);
+				tx_x0[cal] = rtw_read32_mask(rtwdev, 0xd00,
+							     0x07ff0000);
+				tx_x0[cal] <<= 21;
+
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x04000000);
+				tx_y0[cal] = rtw_read32_mask(rtwdev, 0xd00,
+							     0x07ff0000);
+				tx_y0[cal] <<= 21;
+
+				*tx0iqkok = true;
+				break;
+			}
+
+			rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, 0x0);
+			rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, 0x200);
+
+			*tx0iqkok = false;
+			cal_retry++;
+			if (cal_retry == 10)
+				break;
+		} else { /* If 20ms No Result, then cal_retry++ */
+			*tx0iqkok = false;
+			cal_retry++;
+			if (cal_retry == 10)
+				break;
+		}
+	}
+}
+
+static void rtw8821a_iqk_rx(struct rtw_dev *rtwdev, u32 cal, bool *rx0iqkok,
+			    int rx_x0[CAL_NUM_8821A],
+			    int rx_y0[CAL_NUM_8821A])
+{
+	u32 cal_retry, delay_count, iqk_ready, rx_fail;
+
+	rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
+
+	cal_retry = 0;
+	while (1) {
+		/* one shot */
+		rtw_write32(rtwdev, 0x980, 0xfa000000);
+		rtw_write32(rtwdev, 0x980, 0xf8000000);
+
+		mdelay(10);
+
+		rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
+
+		delay_count = 0;
+		while (1) {
+			iqk_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
+
+			/* Originally: if (~iqk_ready || delay_count > 20)
+			 * that looks like a typo so make it more explicit
+			 */
+			iqk_ready = true;
+
+			if (iqk_ready || delay_count > 20)
+				break;
+
+			mdelay(1);
+			delay_count++;
+		}
+
+		if (delay_count < 20) {
+			/* ============RXIQK Check============== */
+			rx_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(11));
+			if (!rx_fail) {
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x06000000);
+				rx_x0[cal] = rtw_read32_mask(rtwdev, 0xd00,
+							     0x07ff0000);
+				rx_x0[cal] <<= 21;
+
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x08000000);
+				rx_y0[cal] = rtw_read32_mask(rtwdev, 0xd00,
+							     0x07ff0000);
+				rx_y0[cal] <<= 21;
+
+				*rx0iqkok = true;
+				break;
+			}
+
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A,
+					 0x000003ff, 0x200 >> 1);
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A,
+					 0x03ff0000, 0x0 >> 1);
+
+			*rx0iqkok = false;
+			cal_retry++;
+			if (cal_retry == 10)
+				break;
+		} else { /* If 20ms No Result, then cal_retry++ */
+			*rx0iqkok = false;
+			cal_retry++;
+			if (cal_retry == 10)
+				break;
+		}
+	}
+}
+
+static bool rtw8821a_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;
+}
+
+static void rtw8821a_iqk(struct rtw_dev *rtwdev)
+{
+	int tx_average = 0, rx_average = 0, rx_iqk_loop = 0;
+	const struct rtw_efuse *efuse = &rtwdev->efuse;
+	int tx_x = 0, tx_y = 0, rx_x = 0, rx_y = 0;
+	const struct rtw_hal *hal = &rtwdev->hal;
+	bool tx0iqkok = false, rx0iqkok = false;
+	int rx_x_temp = 0, rx_y_temp = 0;
+	int rx_x0[2][CAL_NUM_8821A];
+	int rx_y0[2][CAL_NUM_8821A];
+	int tx_x0[CAL_NUM_8821A];
+	int tx_y0[CAL_NUM_8821A];
+	bool rx_finish1 = false;
+	bool rx_finish2 = false;
+	bool vdf_enable;
+	u32 cal = 0;
+	int i;
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK,
+		"band_width = %d, ext_pa = %d, ext_pa_5g = %d\n",
+		hal->current_band_width, efuse->ext_pa_2g, efuse->ext_pa_5g);
+
+	vdf_enable = hal->current_band_width == RTW_CHANNEL_WIDTH_80;
+
+	while (cal < CAL_NUM_8821A) {
+		/* path-A LOK */
+
+		/* [31] = 0 --> Page C */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+		/* ========path-A AFE all on======== */
+		/* Port 0 DAC/ADC on */
+		rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x77777777);
+		rtw_write32(rtwdev, REG_AFE_PWR2_A, 0x77777777);
+
+		rtw_write32(rtwdev, REG_RX_WAIT_CCA_TX_CCK_RFON_A, 0x19791979);
+
+		/* hardware 3-wire off */
+		rtw_write32_mask(rtwdev, 0xc00, 0xf, 0x4);
+
+		/* LOK setting */
+
+		/* 1. DAC/ADC sampling rate (160 MHz) */
+		rtw_write32_mask(rtwdev, 0xc5c, GENMASK(26, 24), 0x7);
+
+		/* 2. LoK RF setting (at BW = 20M) */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80002);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, 0x00c00, 0x3);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+			     0x20000);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK,
+			     0x0003f);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK,
+			     0xf3fc3);
+
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x65, RFREG_MASK, 0x931d5);
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x8f, RFREG_MASK, 0x8a001);
+		rtw_write32(rtwdev, 0x90c, 0x00008000);
+		rtw_write32_mask(rtwdev, 0xc94, BIT(0), 0x1);
+		/* TX (X,Y) */
+		rtw_write32(rtwdev, 0x978, 0x29002000);
+		/* RX (X,Y) */
+		rtw_write32(rtwdev, 0x97c, 0xa9002000);
+		/* [0]:AGC_en, [15]:idac_K_Mask */
+		rtw_write32(rtwdev, 0x984, 0x00462910);
+
+		/* [31] = 1 --> Page C1 */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+		if (efuse->ext_pa_5g)
+			rtw_write32(rtwdev, 0xc88, 0x821403f7);
+		else
+			rtw_write32(rtwdev, 0xc88, 0x821403f4);
+
+		if (hal->current_band_type == RTW_BAND_5G)
+			rtw_write32(rtwdev, 0xc8c, 0x68163e96);
+		else
+			rtw_write32(rtwdev, 0xc8c, 0x28163e96);
+
+		/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+		rtw_write32(rtwdev, 0xc80, 0x18008c10);
+		/* RX_Tone_idx[9:0], RxK_Mask[29] */
+		rtw_write32(rtwdev, 0xc84, 0x38008c10);
+		rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
+		rtw_write32(rtwdev, 0x980, 0xfa000000);
+		rtw_write32(rtwdev, 0x980, 0xf8000000);
+
+		mdelay(10);
+		rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
+
+		/* [31] = 0 --> Page C */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x58, 0x7fe00,
+			     rtw_read_rf(rtwdev, RF_PATH_A, RF_DTXLOK, 0xffc00));
+
+		if (hal->current_band_width == RTW_CHANNEL_WIDTH_40)
+			rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RF18_BW_MASK, 0x1);
+		else if (hal->current_band_width == RTW_CHANNEL_WIDTH_80)
+			rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RF18_BW_MASK, 0x0);
+
+		/* [31] = 1 --> Page C1 */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+		/* 3. TX RF setting */
+		/* [31] = 0 --> Page C */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80000);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+			     0x20000);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK,
+			     0x0003f);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK,
+			     0xf3fc3);
+
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x65, RFREG_MASK, 0x931d5);
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x8f, RFREG_MASK, 0x8a001);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x00000);
+		rtw_write32(rtwdev, 0x90c, 0x00008000);
+		rtw_write32_mask(rtwdev, 0xc94, BIT(0), 0x1);
+		/* TX (X,Y) */
+		rtw_write32(rtwdev, 0x978, 0x29002000);
+		/* RX (X,Y) */
+		rtw_write32(rtwdev, 0x97c, 0xa9002000);
+		/* [0]:AGC_en, [15]:idac_K_Mask */
+		rtw_write32(rtwdev, 0x984, 0x0046a910);
+
+		/* [31] = 1 --> Page C1 */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+		if (efuse->ext_pa_5g)
+			rtw_write32(rtwdev, 0xc88, 0x821403f7);
+		else
+			rtw_write32(rtwdev, 0xc88, 0x821403e3);
+
+		if (hal->current_band_type == RTW_BAND_5G)
+			rtw_write32(rtwdev, 0xc8c, 0x40163e96);
+		else
+			rtw_write32(rtwdev, 0xc8c, 0x00163e96);
+
+		if (vdf_enable)
+			rtw8821a_iqk_tx_vdf_true(rtwdev, cal, &tx0iqkok,
+						 tx_x0, tx_y0);
+		else
+			rtw8821a_iqk_tx_vdf_false(rtwdev, cal, &tx0iqkok,
+						  tx_x0, tx_y0);
+
+		if (!tx0iqkok)
+			break; /* TXK fail, Don't do RXK */
+
+		/* ====== RX IQK ====== */
+		/* [31] = 0 --> Page C */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+		/* 1. RX RF setting */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80000);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+			     0x30000);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK,
+			     0x0002f);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK,
+			     0xfffbb);
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x8f, RFREG_MASK, 0x88001);
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x65, RFREG_MASK, 0x931d8);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x00000);
+
+		rtw_write32_mask(rtwdev, 0x978, 0x03FF8000,
+				 (tx_x0[cal] >> 21) & 0x000007ff);
+		rtw_write32_mask(rtwdev, 0x978, 0x000007FF,
+				 (tx_y0[cal] >> 21) & 0x000007ff);
+		rtw_write32_mask(rtwdev, 0x978, BIT(31), 0x1);
+		rtw_write32_mask(rtwdev, 0x97c, BIT(31), 0x0);
+		rtw_write32(rtwdev, 0x90c, 0x00008000);
+		rtw_write32(rtwdev, 0x984, 0x0046a911);
+
+		/* [31] = 1 --> Page C1 */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+		/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+		rtw_write32(rtwdev, 0xc80, 0x38008c10);
+		/* RX_Tone_idx[9:0], RxK_Mask[29] */
+		rtw_write32(rtwdev, 0xc84, 0x18008c10);
+		rtw_write32(rtwdev, 0xc88, 0x02140119);
+
+		if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE)
+			rx_iqk_loop = 2; /* for 2% fail; */
+		else
+			rx_iqk_loop = 1;
+
+		for (i = 0; i < rx_iqk_loop; i++) {
+			if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE && i == 0)
+				rtw_write32(rtwdev, 0xc8c, 0x28161100); /* Good */
+			else
+				rtw_write32(rtwdev, 0xc8c, 0x28160d00);
+
+			rtw8821a_iqk_rx(rtwdev, cal, &rx0iqkok,
+					rx_x0[i], rx_y0[i]);
+		}
+
+		if (tx0iqkok)
+			tx_average++;
+		if (rx0iqkok)
+			rx_average++;
+
+		cal++;
+	}
+
+	/* FillIQK Result */
+
+	if (tx_average == 0)
+		return;
+
+	for (i = 0; i < tx_average; i++)
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"tx_x0[%d] = %x ;; tx_y0[%d] = %x\n",
+			i, (tx_x0[i] >> 21) & 0x000007ff,
+			i, (tx_y0[i] >> 21) & 0x000007ff);
+
+	if (rtw8821a_iqk_finish(tx_average, 3, tx_x0, tx_y0,
+				&tx_x, &tx_y, true, true))
+		rtw8821a_iqk_tx_fill(rtwdev, tx_x, tx_y);
+	else
+		rtw8821a_iqk_tx_fill(rtwdev, 0x200, 0x0);
+
+	if (rx_average == 0)
+		return;
+
+	for (i = 0; i < rx_average; i++) {
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"rx_x0[0][%d] = %x ;; rx_y0[0][%d] = %x\n",
+			i, (rx_x0[0][i] >> 21) & 0x000007ff,
+			i, (rx_y0[0][i] >> 21) & 0x000007ff);
+
+		if (rx_iqk_loop == 2)
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"rx_x0[1][%d] = %x ;; rx_y0[1][%d] = %x\n",
+				i, (rx_x0[1][i] >> 21) & 0x000007ff,
+				i, (rx_y0[1][i] >> 21) & 0x000007ff);
+	}
+
+	rx_finish1 = rtw8821a_iqk_finish(rx_average, 4, rx_x0[0], rx_y0[0],
+					 &rx_x_temp, &rx_y_temp, true, true);
+
+	if (rx_finish1) {
+		rx_x = rx_x_temp;
+		rx_y = rx_y_temp;
+	}
+
+	if (rx_iqk_loop == 2) {
+		rx_finish2 = rtw8821a_iqk_finish(rx_average, 4,
+						 rx_x0[1], rx_y0[1],
+						 &rx_x, &rx_y, true, true);
+
+		if (rx_finish1 && rx_finish2) {
+			rx_x = (rx_x + rx_x_temp) / 2;
+			rx_y = (rx_y + rx_y_temp) / 2;
+		}
+	}
+
+	if (rx_finish1 || rx_finish2)
+		rtw8821a_iqk_rx_fill(rtwdev, rx_x, rx_y);
+	else
+		rtw8821a_iqk_rx_fill(rtwdev, 0x200, 0x0);
+}
+
+static void rtw8821a_do_iqk(struct rtw_dev *rtwdev)
+{
+	static const u32 backup_macbb_reg[MACBB_REG_NUM_8821A] = {
+		0x520, 0x550, 0x808, 0xa04, 0x90c, 0xc00, 0x838, 0x82c
+	};
+	static const u32 backup_afe_reg[AFE_REG_NUM_8821A] = {
+		0xc5c, 0xc60, 0xc64, 0xc68
+	};
+	static const u32 backup_rf_reg[RF_REG_NUM_8821A] = {
+		0x65, 0x8f, 0x0
+	};
+	u32 macbb_backup[MACBB_REG_NUM_8821A];
+	u32 afe_backup[AFE_REG_NUM_8821A];
+	u32 rfa_backup[RF_REG_NUM_8821A];
+
+	rtw8821a_iqk_backup_mac_bb(rtwdev, macbb_backup,
+				   backup_macbb_reg, MACBB_REG_NUM_8821A);
+	rtw8821a_iqk_backup_afe(rtwdev, afe_backup,
+				backup_afe_reg, AFE_REG_NUM_8821A);
+	rtw8821a_iqk_backup_rf(rtwdev, rfa_backup,
+			       backup_rf_reg, RF_REG_NUM_8821A);
+
+	rtw8821a_iqk_configure_mac(rtwdev);
+
+	rtw8821a_iqk(rtwdev);
+
+	rtw8821a_iqk_restore_rf(rtwdev, backup_rf_reg,
+				rfa_backup, RF_REG_NUM_8821A);
+	rtw8821a_iqk_restore_afe(rtwdev, afe_backup,
+				 backup_afe_reg, AFE_REG_NUM_8821A);
+	rtw8821a_iqk_restore_mac_bb(rtwdev, macbb_backup,
+				    backup_macbb_reg, MACBB_REG_NUM_8821A);
+}
+
+static void rtw8812a_iqk_backup_rf(struct rtw_dev *rtwdev, u32 *rfa_backup,
+				   u32 *rfb_backup, const u32 *backup_rf_reg,
+				   u32 rf_num)
+{
+	u32 i;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	/* Save RF Parameters */
+	for (i = 0; i < rf_num; i++) {
+		rfa_backup[i] = rtw_read_rf(rtwdev, RF_PATH_A,
+					    backup_rf_reg[i], MASKDWORD);
+		rfb_backup[i] = rtw_read_rf(rtwdev, RF_PATH_B,
+					    backup_rf_reg[i], MASKDWORD);
+	}
+}
+
+static void rtw8812a_iqk_restore_rf(struct rtw_dev *rtwdev,
+				    enum rtw_rf_path path,
+				    const u32 *backup_rf_reg,
+				    u32 *RF_backup, u32 rf_reg_num)
+{
+	u32 i;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	for (i = 0; i < rf_reg_num; i++)
+		rtw_write_rf(rtwdev, path, backup_rf_reg[i],
+			     RFREG_MASK, RF_backup[i]);
+
+	rtw_write_rf(rtwdev, path, RF_LUTWE, RFREG_MASK, 0);
+}
+
+static void rtw8812a_iqk_restore_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);
+
+	/* Reload AFE Parameters */
+	for (i = 0; i < afe_num; i++)
+		rtw_write32(rtwdev, backup_afe_reg[i], afe_backup[i]);
+
+	/* [31] = 1 --> Page C1 */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+	rtw_write32(rtwdev, 0xc80, 0x0);
+	rtw_write32(rtwdev, 0xc84, 0x0);
+	rtw_write32(rtwdev, 0xc88, 0x0);
+	rtw_write32(rtwdev, 0xc8c, 0x3c000000);
+	rtw_write32_mask(rtwdev, REG_LSSI_WRITE_A, BIT(7), 1);
+	rtw_write32_mask(rtwdev, 0xcc4, BIT(18), 1);
+	rtw_write32_mask(rtwdev, 0xcc4, BIT(29), 1);
+	rtw_write32_mask(rtwdev, 0xcc8, BIT(29), 1);
+
+	rtw_write32(rtwdev, 0xe80, 0x0);
+	rtw_write32(rtwdev, 0xe84, 0x0);
+	rtw_write32(rtwdev, 0xe88, 0x0);
+	rtw_write32(rtwdev, 0xe8c, 0x3c000000);
+	rtw_write32_mask(rtwdev, REG_LSSI_WRITE_B, BIT(7), 1);
+	rtw_write32_mask(rtwdev, 0xec4, BIT(18), 1);
+	rtw_write32_mask(rtwdev, 0xec4, BIT(29), 1);
+	rtw_write32_mask(rtwdev, 0xec8, BIT(29), 1);
+}
+
+static void rtw8812a_iqk_rx_fill(struct rtw_dev *rtwdev, enum rtw_rf_path path,
+				 unsigned int rx_x, unsigned int rx_y)
+{
+	switch (path) {
+	case RF_PATH_A: {
+		/* [31] = 0 --> Page C */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+		if (rx_x >> 1 >= 0x112 || (rx_y >> 1 >= 0x12 && rx_y >> 1 <= 0x3ee)) {
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x000003ff, 0x100);
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x03ff0000, 0);
+		} else {
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x000003ff, rx_x >> 1);
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_A, 0x03ff0000, rx_y >> 1);
+		}
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"rx_x = %x;;rx_y = %x ====>fill to IQC\n",
+			rx_x >> 1 & 0x000003ff, rx_y >> 1 & 0x000003ff);
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "0xc10 = %x ====>fill to IQC\n",
+			rtw_read32(rtwdev, REG_RX_IQC_AB_A));
+	} break;
+	case RF_PATH_B: {
+		/* [31] = 0 --> Page C */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+		if (rx_x >> 1 >= 0x112 || (rx_y >> 1 >= 0x12 && rx_y >> 1 <= 0x3ee)) {
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x000003ff, 0x100);
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x03ff0000, 0);
+		} else {
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x000003ff, rx_x >> 1);
+			rtw_write32_mask(rtwdev, REG_RX_IQC_AB_B, 0x03ff0000, rx_y >> 1);
+		}
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"rx_x = %x;;rx_y = %x ====>fill to IQC\n",
+			rx_x >> 1 & 0x000003ff, rx_y >> 1 & 0x000003ff);
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "0xe10 = %x====>fill to IQC\n",
+			rtw_read32(rtwdev, REG_RX_IQC_AB_B));
+	} break;
+	default:
+		break;
+	};
+}
+
+static void rtw8812a_iqk_tx_fill(struct rtw_dev *rtwdev, enum rtw_rf_path path,
+				 unsigned int tx_x, unsigned int tx_y)
+{
+	switch (path) {
+	case RF_PATH_A: {
+		/* [31] = 1 --> Page C1 */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+		rtw_write32_mask(rtwdev, 0xc90, BIT(7), 0x1);
+		rtw_write32_mask(rtwdev, 0xcc4, BIT(18), 0x1);
+		rtw_write32_mask(rtwdev, 0xcc4, BIT(29), 0x1);
+		rtw_write32_mask(rtwdev, 0xcc8, BIT(29), 0x1);
+		rtw_write32_mask(rtwdev, 0xccc, 0x000007ff, tx_y);
+		rtw_write32_mask(rtwdev, 0xcd4, 0x000007ff, tx_x);
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"tx_x = %x;;tx_y = %x =====> fill to IQC\n",
+			tx_x & 0x000007ff, tx_y & 0x000007ff);
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"0xcd4 = %x;;0xccc = %x ====>fill to IQC\n",
+			rtw_read32_mask(rtwdev, 0xcd4, 0x000007ff),
+			rtw_read32_mask(rtwdev, 0xccc, 0x000007ff));
+	} break;
+	case RF_PATH_B: {
+		/* [31] = 1 --> Page C1 */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+		rtw_write32_mask(rtwdev, 0xe90, BIT(7), 0x1);
+		rtw_write32_mask(rtwdev, 0xec4, BIT(18), 0x1);
+		rtw_write32_mask(rtwdev, 0xec4, BIT(29), 0x1);
+		rtw_write32_mask(rtwdev, 0xec8, BIT(29), 0x1);
+		rtw_write32_mask(rtwdev, 0xecc, 0x000007ff, tx_y);
+		rtw_write32_mask(rtwdev, 0xed4, 0x000007ff, tx_x);
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"tx_x = %x;;tx_y = %x =====> fill to IQC\n",
+			tx_x & 0x000007ff, tx_y & 0x000007ff);
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"0xed4 = %x;;0xecc = %x ====>fill to IQC\n",
+			rtw_read32_mask(rtwdev, 0xed4, 0x000007ff),
+			rtw_read32_mask(rtwdev, 0xecc, 0x000007ff));
+	} break;
+	default:
+		break;
+	};
+}
+
+static void rtw8812a_iqk(struct rtw_dev *rtwdev)
+{
+	int tx_x0_temp[10], tx_y0_temp[10], tx_x1_temp[10], tx_y1_temp[10];
+	int rx_x0_temp[10], rx_y0_temp[10], rx_x1_temp[10], rx_y1_temp[10];
+	bool iqk0_ready = false, tx0_finish = false, rx0_finish = false;
+	bool iqk1_ready = false, tx1_finish = false, rx1_finish = false;
+	u8 tx0_avg = 0, tx1_avg = 0, rx0_avg = 0, rx1_avg = 0;
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	bool tx0_fail = true, rx0_fail = true;
+	bool tx1_fail = true, rx1_fail = true;
+	int tx_x0, tx_y0, tx_x1, tx_y1;
+	int rx_x0, rx_y0, rx_x1, rx_y1;
+	u8 cal0_retry, cal1_retry;
+	u8 delay_count;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	/* ========path-A AFE all on======== */
+	/* Port 0 DAC/ADC on */
+	rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x77777777);
+	rtw_write32(rtwdev, REG_AFE_PWR2_A, 0x77777777);
+
+	/* Port 1 DAC/ADC on */
+	rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x77777777);
+	rtw_write32(rtwdev, REG_AFE_PWR2_B, 0x77777777);
+
+	rtw_write32(rtwdev, REG_RX_WAIT_CCA_TX_CCK_RFON_A, 0x19791979);
+	rtw_write32(rtwdev, REG_RX_WAIT_CCA_TX_CCK_RFON_B, 0x19791979);
+
+	/* hardware 3-wire off */
+	rtw_write32_mask(rtwdev, 0xc00, 0xf, 0x4);
+	rtw_write32_mask(rtwdev, 0xe00, 0xf, 0x4);
+
+	/* DAC/ADC sampling rate (160 MHz) */
+	rtw_write32_mask(rtwdev, 0xc5c, BIT(26) | BIT(25) | BIT(24), 0x7);
+	rtw_write32_mask(rtwdev, 0xe5c, BIT(26) | BIT(25) | BIT(24), 0x7);
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+	/* ====== path A TX IQK RF setting ====== */
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80002);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x20000);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x3fffd);
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xfe83f);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x65, RFREG_MASK, 0x931d5);
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x8f, RFREG_MASK, 0x8a001);
+
+	/* ====== path B TX IQK RF setting ====== */
+	rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE, RFREG_MASK, 0x80002);
+	rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_ADDR, RFREG_MASK, 0x20000);
+	rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_DATA0, RFREG_MASK, 0x3fffd);
+	rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_DATA1, RFREG_MASK, 0xfe83f);
+	rtw_write_rf(rtwdev, RF_PATH_B, 0x65, RFREG_MASK, 0x931d5);
+	rtw_write_rf(rtwdev, RF_PATH_B, 0x8f, RFREG_MASK, 0x8a001);
+
+	rtw_write32(rtwdev, 0x90c, 0x00008000);
+	rtw_write32_mask(rtwdev, 0xc94, BIT(0), 0x1);
+	rtw_write32_mask(rtwdev, 0xe94, BIT(0), 0x1);
+	rtw_write32(rtwdev, 0x978, 0x29002000); /* TX (X,Y) */
+	rtw_write32(rtwdev, 0x97c, 0xa9002000); /* RX (X,Y) */
+	rtw_write32(rtwdev, 0x984, 0x00462910); /* [0]:AGC_en, [15]:idac_K_Mask */
+	/* [31] = 1 --> Page C1 */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+	if (efuse->ext_pa_5g) {
+		if (efuse->rfe_option == 1) {
+			rtw_write32(rtwdev, 0xc88, 0x821403e3);
+			rtw_write32(rtwdev, 0xe88, 0x821403e3);
+		} else {
+			rtw_write32(rtwdev, 0xc88, 0x821403f7);
+			rtw_write32(rtwdev, 0xe88, 0x821403f7);
+		}
+	} else {
+		rtw_write32(rtwdev, 0xc88, 0x821403f1);
+		rtw_write32(rtwdev, 0xe88, 0x821403f1);
+	}
+
+	if (rtwdev->hal.current_band_type == RTW_BAND_5G) {
+		rtw_write32(rtwdev, 0xc8c, 0x68163e96);
+		rtw_write32(rtwdev, 0xe8c, 0x68163e96);
+	} else {
+		rtw_write32(rtwdev, 0xc8c, 0x28163e96);
+		rtw_write32(rtwdev, 0xe8c, 0x28163e96);
+
+		if (efuse->rfe_option == 3) {
+			if (efuse->ext_pa_2g)
+				rtw_write32(rtwdev, 0xc88, 0x821403e3);
+			else
+				rtw_write32(rtwdev, 0xc88, 0x821403f7);
+		}
+	}
+
+	/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+	rtw_write32(rtwdev, 0xc80, 0x18008c10);
+	/* RX_Tone_idx[9:0], RxK_Mask[29] */
+	rtw_write32(rtwdev, 0xc84, 0x38008c10);
+	rtw_write32(rtwdev, 0xce8, 0x00000000);
+	/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+	rtw_write32(rtwdev, 0xe80, 0x18008c10);
+	/* RX_Tone_idx[9:0], RxK_Mask[29] */
+	rtw_write32(rtwdev, 0xe84, 0x38008c10);
+	rtw_write32(rtwdev, 0xee8, 0x00000000);
+
+	cal0_retry = 0;
+	cal1_retry = 0;
+	while (1) {
+		/* one shot */
+		rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
+		rtw_write32(rtwdev, REG_RFECTL_B, 0x00100000);
+		rtw_write32(rtwdev, 0x980, 0xfa000000);
+		rtw_write32(rtwdev, 0x980, 0xf8000000);
+
+		mdelay(10);
+
+		rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
+		rtw_write32(rtwdev, REG_RFECTL_B, 0x00000000);
+
+		delay_count = 0;
+		while (1) {
+			if (!tx0_finish)
+				iqk0_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
+			if (!tx1_finish)
+				iqk1_ready = rtw_read32_mask(rtwdev, 0xd40, BIT(10));
+			if ((iqk0_ready && iqk1_ready) || delay_count > 20)
+				break;
+
+			mdelay(1);
+			delay_count++;
+		}
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "TX delay_count = %d\n",
+			delay_count);
+
+		if (delay_count < 20) { /* If 20ms No Result, then cal_retry++ */
+			/* ============TXIQK Check============== */
+			tx0_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(12));
+			tx1_fail = rtw_read32_mask(rtwdev, 0xd40, BIT(12));
+
+			if (!(tx0_fail || tx0_finish)) {
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x02000000);
+				tx_x0_temp[tx0_avg] = rtw_read32_mask(rtwdev,
+								      0xd00,
+								      0x07ff0000);
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x04000000);
+				tx_y0_temp[tx0_avg] = rtw_read32_mask(rtwdev,
+								      0xd00,
+								      0x07ff0000);
+
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"tx_x0[%d] = %x ;; tx_y0[%d] = %x\n",
+					tx0_avg, tx_x0_temp[tx0_avg],
+					tx0_avg, tx_y0_temp[tx0_avg]);
+
+				tx_x0_temp[tx0_avg] <<= 21;
+				tx_y0_temp[tx0_avg] <<= 21;
+
+				tx0_avg++;
+			} else {
+				cal0_retry++;
+				if (cal0_retry == 10)
+					break;
+			}
+
+			if (!(tx1_fail || tx1_finish)) {
+				rtw_write32(rtwdev, REG_RFECTL_B, 0x02000000);
+				tx_x1_temp[tx1_avg] = rtw_read32_mask(rtwdev,
+								      0xd40,
+								      0x07ff0000);
+				rtw_write32(rtwdev, REG_RFECTL_B, 0x04000000);
+				tx_y1_temp[tx1_avg] = rtw_read32_mask(rtwdev,
+								      0xd40,
+								      0x07ff0000);
+
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"tx_x1[%d] = %x ;; tx_y1[%d] = %x\n",
+					tx1_avg, tx_x1_temp[tx1_avg],
+					tx1_avg, tx_y1_temp[tx1_avg]);
+
+				tx_x1_temp[tx1_avg] <<= 21;
+				tx_y1_temp[tx1_avg] <<= 21;
+
+				tx1_avg++;
+			} else {
+				cal1_retry++;
+				if (cal1_retry == 10)
+					break;
+			}
+		} else {
+			cal0_retry++;
+			cal1_retry++;
+
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"delay 20ms TX IQK Not Ready!!!!!\n");
+
+			if (cal0_retry == 10)
+				break;
+		}
+
+		if (tx0_avg >= 2)
+			tx0_finish = rtw8821a_iqk_finish(tx0_avg, 4,
+							 tx_x0_temp, tx_y0_temp, &tx_x0, &tx_y0,
+							 false, false);
+
+		if (tx1_avg >= 2)
+			tx1_finish = rtw8821a_iqk_finish(tx1_avg, 4,
+							 tx_x1_temp, tx_y1_temp, &tx_x1, &tx_y1,
+							 false, false);
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"tx0_average = %d, tx1_average = %d\n",
+			tx0_avg, tx1_avg);
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"tx0_finish = %d, tx1_finish = %d\n",
+			tx0_finish, tx1_finish);
+
+		if (tx0_finish && tx1_finish)
+			break;
+
+		if ((cal0_retry + tx0_avg) >= 10 ||
+		    (cal1_retry + tx1_avg) >= 10)
+			break;
+	}
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "TXA_cal_retry = %d\n", cal0_retry);
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "TXB_cal_retry = %d\n", cal1_retry);
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+	/* Load LOK */
+	rtw_write_rf(rtwdev, RF_PATH_A, 0x58, 0x7fe00,
+		     rtw_read_rf(rtwdev, RF_PATH_A, 0x8, 0xffc00));
+	rtw_write_rf(rtwdev, RF_PATH_B, 0x58, 0x7fe00,
+		     rtw_read_rf(rtwdev, RF_PATH_B, 0x8, 0xffc00));
+	/* [31] = 1 --> Page C1 */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+	if (tx0_finish) {
+		/* ====== path A RX IQK RF setting====== */
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x80000);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_ADDR, RFREG_MASK,
+			     0x30000);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA0, RFREG_MASK,
+			     0x3f7ff);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_MODE_TABLE_DATA1, RFREG_MASK,
+			     0xfe7bf);
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x8f, RFREG_MASK, 0x88001);
+		rtw_write_rf(rtwdev, RF_PATH_A, 0x65, RFREG_MASK, 0x931d1);
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x00000);
+	}
+	if (tx1_finish) {
+		/* ====== path B RX IQK RF setting====== */
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE, RFREG_MASK, 0x80000);
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_ADDR, RFREG_MASK,
+			     0x30000);
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_DATA0, RFREG_MASK,
+			     0x3f7ff);
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_MODE_TABLE_DATA1, RFREG_MASK,
+			     0xfe7bf);
+		rtw_write_rf(rtwdev, RF_PATH_B, 0x8f, RFREG_MASK, 0x88001);
+		rtw_write_rf(rtwdev, RF_PATH_B, 0x65, RFREG_MASK, 0x931d1);
+		rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE, RFREG_MASK, 0x00000);
+	}
+
+	rtw_write32_mask(rtwdev, 0x978, BIT(31), 0x1);
+	rtw_write32_mask(rtwdev, 0x97c, BIT(31), 0x0);
+	rtw_write32(rtwdev, 0x90c, 0x00008000);
+
+	if (rtwdev->hci.type == RTW_HCI_TYPE_PCIE)
+		rtw_write32(rtwdev, 0x984, 0x0046a911);
+	else
+		rtw_write32(rtwdev, 0x984, 0x0046a890);
+
+	if (efuse->rfe_option == 1) {
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777717);
+		rtw_write32(rtwdev, REG_RFE_INV_A, 0x00000077);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777717);
+		rtw_write32(rtwdev, REG_RFE_INV_B, 0x00000077);
+	} else {
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777717);
+		rtw_write32(rtwdev, REG_RFE_INV_A, 0x02000077);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777717);
+		rtw_write32(rtwdev, REG_RFE_INV_B, 0x02000077);
+	}
+
+	/* [31] = 1 --> Page C1 */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+
+	if (tx0_finish) {
+		/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+		rtw_write32(rtwdev, 0xc80, 0x38008c10);
+		/* RX_Tone_idx[9:0], RxK_Mask[29] */
+		rtw_write32(rtwdev, 0xc84, 0x18008c10);
+		rtw_write32(rtwdev, 0xc88, 0x82140119);
+	}
+	if (tx1_finish) {
+		/* TX_Tone_idx[9:0], TxK_Mask[29] TX_Tone = 16 */
+		rtw_write32(rtwdev, 0xe80, 0x38008c10);
+		/* RX_Tone_idx[9:0], RxK_Mask[29] */
+		rtw_write32(rtwdev, 0xe84, 0x18008c10);
+		rtw_write32(rtwdev, 0xe88, 0x82140119);
+	}
+
+	cal0_retry = 0;
+	cal1_retry = 0;
+	while (1) {
+		/* one shot */
+		/* [31] = 0 --> Page C */
+		rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+		if (tx0_finish) {
+			rtw_write32_mask(rtwdev, 0x978, 0x03FF8000,
+					 tx_x0 & 0x000007ff);
+			rtw_write32_mask(rtwdev, 0x978, 0x000007FF,
+					 tx_y0 & 0x000007ff);
+			/* [31] = 1 --> Page C1 */
+			rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+			if (efuse->rfe_option == 1)
+				rtw_write32(rtwdev, 0xc8c, 0x28161500);
+			else
+				rtw_write32(rtwdev, 0xc8c, 0x28160cc0);
+			rtw_write32(rtwdev, REG_RFECTL_A, 0x00300000);
+			rtw_write32(rtwdev, REG_RFECTL_A, 0x00100000);
+			mdelay(5);
+			rtw_write32(rtwdev, 0xc8c, 0x3c000000);
+			rtw_write32(rtwdev, REG_RFECTL_A, 0x00000000);
+		}
+
+		if (tx1_finish) {
+			/* [31] = 0 --> Page C */
+			rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+			rtw_write32_mask(rtwdev, 0x978, 0x03FF8000,
+					 tx_x1 & 0x000007ff);
+			rtw_write32_mask(rtwdev, 0x978, 0x000007FF,
+					 tx_y1 & 0x000007ff);
+			/* [31] = 1 --> Page C1 */
+			rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x1);
+			if (efuse->rfe_option == 1)
+				rtw_write32(rtwdev, 0xe8c, 0x28161500);
+			else
+				rtw_write32(rtwdev, 0xe8c, 0x28160ca0);
+			rtw_write32(rtwdev, REG_RFECTL_B, 0x00300000);
+			rtw_write32(rtwdev, REG_RFECTL_B, 0x00100000);
+			mdelay(5);
+			rtw_write32(rtwdev, 0xe8c, 0x3c000000);
+			rtw_write32(rtwdev, REG_RFECTL_B, 0x00000000);
+		}
+
+		delay_count = 0;
+		while (1) {
+			if (!rx0_finish && tx0_finish)
+				iqk0_ready = rtw_read32_mask(rtwdev, 0xd00, BIT(10));
+			if (!rx1_finish && tx1_finish)
+				iqk1_ready = rtw_read32_mask(rtwdev, 0xd40, BIT(10));
+			if ((iqk0_ready && iqk1_ready) || delay_count > 20)
+				break;
+
+			mdelay(1);
+			delay_count++;
+		}
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "RX delay_count = %d\n",
+			delay_count);
+
+		if (delay_count < 20) { /* If 20ms No Result, then cal_retry++ */
+			/* ============RXIQK Check============== */
+			rx0_fail = rtw_read32_mask(rtwdev, 0xd00, BIT(11));
+			rx1_fail = rtw_read32_mask(rtwdev, 0xd40, BIT(11));
+
+			if (!(rx0_fail || rx0_finish) && tx0_finish) {
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x06000000);
+				rx_x0_temp[rx0_avg] = rtw_read32_mask(rtwdev,
+								      0xd00,
+								      0x07ff0000);
+				rtw_write32(rtwdev, REG_RFECTL_A, 0x08000000);
+				rx_y0_temp[rx0_avg] = rtw_read32_mask(rtwdev,
+								      0xd00,
+								      0x07ff0000);
+
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"rx_x0[%d] = %x ;; rx_y0[%d] = %x\n",
+					rx0_avg, rx_x0_temp[rx0_avg],
+					rx0_avg, rx_y0_temp[rx0_avg]);
+
+				rx_x0_temp[rx0_avg] <<= 21;
+				rx_y0_temp[rx0_avg] <<= 21;
+
+				rx0_avg++;
+			} else {
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"1. RXA_cal_retry = %d\n", cal0_retry);
+
+				cal0_retry++;
+				if (cal0_retry == 10)
+					break;
+			}
+
+			if (!(rx1_fail || rx1_finish) && tx1_finish) {
+				rtw_write32(rtwdev, REG_RFECTL_B, 0x06000000);
+				rx_x1_temp[rx1_avg] = rtw_read32_mask(rtwdev,
+								      0xd40,
+								      0x07ff0000);
+				rtw_write32(rtwdev, REG_RFECTL_B, 0x08000000);
+				rx_y1_temp[rx1_avg] = rtw_read32_mask(rtwdev,
+								      0xd40,
+								      0x07ff0000);
+
+				rtw_dbg(rtwdev, RTW_DBG_RFK,
+					"rx_x1[%d] = %x ;; rx_y1[%d] = %x\n",
+					rx1_avg, rx_x1_temp[rx1_avg],
+					rx1_avg, rx_y1_temp[rx1_avg]);
+
+				rx_x1_temp[rx1_avg] <<= 21;
+				rx_y1_temp[rx1_avg] <<= 21;
+
+				rx1_avg++;
+			} else {
+				cal1_retry++;
+				if (cal1_retry == 10)
+					break;
+			}
+		} else {
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"2. RXA_cal_retry = %d\n", cal0_retry);
+
+			cal0_retry++;
+			cal1_retry++;
+
+			rtw_dbg(rtwdev, RTW_DBG_RFK,
+				"delay 20ms RX IQK Not Ready!!!!!\n");
+
+			if (cal0_retry == 10)
+				break;
+		}
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "3. RXA_cal_retry = %d\n",
+			cal0_retry);
+
+		if (rx0_avg >= 2)
+			rx0_finish = rtw8821a_iqk_finish(rx0_avg, 4,
+							 rx_x0_temp, rx_y0_temp,
+							 &rx_x0, &rx_y0,
+							 true, false);
+
+		if (rx1_avg >= 2)
+			rx1_finish = rtw8821a_iqk_finish(rx1_avg, 4,
+							 rx_x1_temp, rx_y1_temp,
+							 &rx_x1, &rx_y1,
+							 true, false);
+
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"rx0_average = %d, rx1_average = %d\n",
+			rx0_avg, rx1_avg);
+		rtw_dbg(rtwdev, RTW_DBG_RFK,
+			"rx0_finish = %d, rx1_finish = %d\n",
+			rx0_finish, rx1_finish);
+
+		if ((rx0_finish || !tx0_finish) && (rx1_finish || !tx1_finish))
+			break;
+
+		if ((cal0_retry + rx0_avg) >= 10 ||
+		    (cal1_retry + rx1_avg) >= 10 ||
+		    rx0_avg == 3 || rx1_avg == 3)
+			break;
+	}
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "RXA_cal_retry = %d\n", cal0_retry);
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "RXB_cal_retry = %d\n", cal1_retry);
+
+	/* FillIQK Result */
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "========Path_A =======\n");
+
+	if (tx0_finish)
+		rtw8812a_iqk_tx_fill(rtwdev, RF_PATH_A, tx_x0, tx_y0);
+	else
+		rtw8812a_iqk_tx_fill(rtwdev, RF_PATH_A, 0x200, 0x0);
+
+	if (rx0_finish)
+		rtw8812a_iqk_rx_fill(rtwdev, RF_PATH_A, rx_x0, rx_y0);
+	else
+		rtw8812a_iqk_rx_fill(rtwdev, RF_PATH_A, 0x200, 0x0);
+
+	rtw_dbg(rtwdev, RTW_DBG_RFK, "========Path_B =======\n");
+
+	if (tx1_finish)
+		rtw8812a_iqk_tx_fill(rtwdev, RF_PATH_B, tx_x1, tx_y1);
+	else
+		rtw8812a_iqk_tx_fill(rtwdev, RF_PATH_B, 0x200, 0x0);
+
+	if (rx1_finish)
+		rtw8812a_iqk_rx_fill(rtwdev, RF_PATH_B, rx_x1, rx_y1);
+	else
+		rtw8812a_iqk_rx_fill(rtwdev, RF_PATH_B, 0x200, 0x0);
+}
+
+#define MACBB_REG_NUM_8812A 9
+#define AFE_REG_NUM_8812A 12
+#define RF_REG_NUM_8812A 3
+
+static void rtw8812a_do_iqk(struct rtw_dev *rtwdev)
+{
+	static const u32 backup_macbb_reg[MACBB_REG_NUM_8812A] = {
+		0x520, 0x550, 0x808, 0xa04, 0x90c, 0xc00, 0xe00, 0x838, 0x82c
+	};
+	static const u32 backup_afe_reg[AFE_REG_NUM_8812A] = {
+		0xc5c, 0xc60, 0xc64, 0xc68, 0xcb0, 0xcb4,
+		0xe5c, 0xe60, 0xe64, 0xe68, 0xeb0, 0xeb4
+	};
+	static const u32 backup_rf_reg[RF_REG_NUM_8812A] = {
+		0x65, 0x8f, 0x0
+	};
+	u32 macbb_backup[MACBB_REG_NUM_8812A] = {0};
+	u32 afe_backup[AFE_REG_NUM_8812A] = {0};
+	u32 rfa_backup[RF_REG_NUM_8812A] = {0};
+	u32 rfb_backup[RF_REG_NUM_8812A] = {0};
+	u32 reg_cb8, reg_eb8;
+
+	rtw8821a_iqk_backup_mac_bb(rtwdev, macbb_backup,
+				   backup_macbb_reg, MACBB_REG_NUM_8812A);
+
+	rtw_write32_set(rtwdev, REG_CCASEL, BIT(31));
+	reg_cb8 = rtw_read32(rtwdev, REG_RFECTL_A);
+	reg_eb8 = rtw_read32(rtwdev, REG_RFECTL_B);
+	rtw_write32_clr(rtwdev, REG_CCASEL, BIT(31));
+
+	rtw8821a_iqk_backup_afe(rtwdev, afe_backup,
+				backup_afe_reg, AFE_REG_NUM_8812A);
+	rtw8812a_iqk_backup_rf(rtwdev, rfa_backup, rfb_backup,
+			       backup_rf_reg, RF_REG_NUM_8812A);
+
+	rtw8821a_iqk_configure_mac(rtwdev);
+
+	rtw8812a_iqk(rtwdev);
+
+	rtw8812a_iqk_restore_rf(rtwdev, RF_PATH_A, backup_rf_reg,
+				rfa_backup, RF_REG_NUM_8812A);
+	rtw8812a_iqk_restore_rf(rtwdev, RF_PATH_B, backup_rf_reg,
+				rfb_backup, RF_REG_NUM_8812A);
+
+	rtw8812a_iqk_restore_afe(rtwdev, afe_backup,
+				 backup_afe_reg, AFE_REG_NUM_8812A);
+
+	rtw_write32_set(rtwdev, REG_CCASEL, BIT(31));
+	rtw_write32(rtwdev, REG_RFECTL_A, reg_cb8);
+	rtw_write32(rtwdev, REG_RFECTL_B, reg_eb8);
+	rtw_write32_clr(rtwdev, REG_CCASEL, BIT(31));
+
+	rtw8821a_iqk_restore_mac_bb(rtwdev, macbb_backup,
+				    backup_macbb_reg, MACBB_REG_NUM_8812A);
+}
+
+static void rtw8821a_phy_calibration(struct rtw_dev *rtwdev)
+{
+	u8 channel = rtwdev->hal.current_channel;
+
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A) {
+		rtw8821a_do_iqk(rtwdev);
+	} else {
+		rtw8812a_do_iqk(rtwdev);
+
+		/* The official driver wants to do this after connecting
+		 * but before first writing a new igi (phydm_get_new_igi).
+		 * Here seems close enough.
+		 */
+		if (channel >= 36 && channel <= 64)
+			rtw_load_table(rtwdev, &rtw8812a_agc_diff_lb_tbl);
+		else if (channel >= 100)
+			rtw_load_table(rtwdev, &rtw8812a_agc_diff_hb_tbl);
+	}
+}
+
+static void rtw8812a_do_lck(struct rtw_dev *rtwdev)
+{
+	u32 cont_tx, lc_cal, i;
+
+	cont_tx = rtw_read32_mask(rtwdev, REG_SINGLE_TONE_CONT_TX, 0x70000);
+
+	lc_cal = rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK);
+
+	if (!cont_tx)
+		rtw_write8(rtwdev, REG_TXPAUSE, 0xff);
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LCK, BIT(14), 1);
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, 0x08000, 1);
+
+	mdelay(150);
+
+	for (i = 0; i < 5; i++) {
+		if (rtw_read_rf(rtwdev, RF_PATH_A, RF_CFGCH, 0x08000) != 1)
+			break;
+
+		mdelay(10);
+	}
+
+	if (i == 5)
+		rtw_dbg(rtwdev, RTW_DBG_RFK, "LCK timed out\n");
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, lc_cal);
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_LCK, BIT(14), 0);
+
+	if (!cont_tx)
+		rtw_write8(rtwdev, REG_TXPAUSE, 0);
+
+	rtw_write_rf(rtwdev, RF_PATH_A, RF_CFGCH, RFREG_MASK, lc_cal);
+}
+
+static void rtw8821a_coex_cfg_init(struct rtw_dev *rtwdev)
+{
+	u8 val8;
+
+	/* BT report packet sample rate */
+	rtw_write8_mask(rtwdev, REG_BT_TDMA_TIME, BIT_MASK_SAMPLE_RATE, 0x5);
+
+	val8 = BIT_STATIS_BT_EN;
+	if (rtwdev->efuse.share_ant)
+		val8 |= BIT_R_GRANTALL_WLMASK;
+	rtw_write8(rtwdev, REG_BT_COEX_ENH_INTR_CTRL, val8);
+
+	/* enable BT counter statistics */
+	rtw_write8(rtwdev, REG_BT_STAT_CTRL, 0x3);
+
+	/* enable PTA */
+	rtw_write32_set(rtwdev, REG_GPIO_MUXCFG, BIT_BT_PTA_EN);
+}
+
+static void rtw8821a_coex_cfg_ant_switch(struct rtw_dev *rtwdev, u8 ctrl_type,
+					 u8 pos_type)
+{
+	bool share_ant = rtwdev->efuse.share_ant;
+	struct rtw_coex *coex = &rtwdev->coex;
+	struct rtw_coex_dm *coex_dm = &coex->dm;
+	u32 phase = coex_dm->cur_ant_pos_type;
+
+	if (!rtwdev->efuse.btcoex)
+		return;
+
+	switch (phase) {
+	case COEX_SET_ANT_POWERON:
+	case COEX_SET_ANT_INIT:
+		rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN);
+		rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL);
+		rtw_write8_set(rtwdev, 0x765, 0x18);
+
+		rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89,
+				share_ant ? PTA_CTRL_PIN : DPDT_CTRL_PIN);
+		rtw_write32_mask(rtwdev, REG_RFE_CTRL8, 0x30000000, 0x1);
+		break;
+	case COEX_SET_ANT_WONLY:
+		rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN);
+		rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL);
+		rtw_write8_clr(rtwdev, 0x765, 0x18);
+
+		rtw_write8_mask(rtwdev, REG_RFE_CTRL8,
+				BIT_MASK_RFE_SEL89, DPDT_CTRL_PIN);
+		rtw_write32_mask(rtwdev, REG_RFE_CTRL8, 0x30000000, 0x1);
+		break;
+	case COEX_SET_ANT_2G:
+		rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN);
+		rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL);
+		rtw_write8_clr(rtwdev, 0x765, 0x18);
+
+		rtw_write8_mask(rtwdev, REG_RFE_CTRL8, BIT_MASK_RFE_SEL89,
+				share_ant ? PTA_CTRL_PIN : DPDT_CTRL_PIN);
+		rtw_write32_mask(rtwdev, REG_RFE_CTRL8, 0x30000000, 0x1);
+		break;
+	case COEX_SET_ANT_5G:
+		rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN);
+		rtw_write32_set(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL);
+		rtw_write8_set(rtwdev, 0x765, 0x18);
+
+		rtw_write8_mask(rtwdev, REG_RFE_CTRL8,
+				BIT_MASK_RFE_SEL89, DPDT_CTRL_PIN);
+		rtw_write32_mask(rtwdev, REG_RFE_CTRL8, 0x30000000,
+				 share_ant ? 0x2 : 0x1);
+		break;
+	case COEX_SET_ANT_WOFF:
+		rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN);
+		rtw_write32_clr(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL);
+		rtw_write8_set(rtwdev, 0x765, 0x18);
+
+		rtw_write8_mask(rtwdev, REG_RFE_CTRL8,
+				BIT_MASK_RFE_SEL89, DPDT_CTRL_PIN);
+		rtw_write32_mask(rtwdev, REG_RFE_CTRL8, 0x30000000,
+				 share_ant ? 0x2 : 0x1);
+		break;
+	default:
+		rtw_warn(rtwdev, "%s: not handling phase %d\n",
+			 __func__, phase);
+		break;
+	}
+}
+
+static void rtw8821a_coex_cfg_gnt_fix(struct rtw_dev *rtwdev)
+{}
+
+static void rtw8821a_coex_cfg_gnt_debug(struct rtw_dev *rtwdev)
+{}
+
+static void rtw8821a_coex_cfg_rfe_type(struct rtw_dev *rtwdev)
+{
+	struct rtw_coex *coex = &rtwdev->coex;
+	struct rtw_coex_rfe *coex_rfe = &coex->rfe;
+
+	coex_rfe->ant_switch_exist = true;
+}
+
+static void rtw8821a_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr)
+{
+	struct rtw_coex *coex = &rtwdev->coex;
+	struct rtw_coex_dm *coex_dm = &coex->dm;
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	bool share_ant = efuse->share_ant;
+
+	if (share_ant)
+		return;
+
+	if (wl_pwr == coex_dm->cur_wl_pwr_lvl)
+		return;
+
+	coex_dm->cur_wl_pwr_lvl = wl_pwr;
+}
+
+static void rtw8821a_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain)
+{}
+
+static void rtw8821a_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),
+			 rtw8821a_txscale_tbl[ofdm_swing_idx]);
+}
+
+static void rtw8821a_phy_pwrtrack(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 (rtwdev->chip->id == RTW_CHIP_TYPE_8812A) {
+		if (need_iqk)
+			rtw8812a_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];
+
+			rtw8821a_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)
+		rtw8821a_do_iqk(rtwdev);
+}
+
+static void rtw8821a_pwr_track(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	if (!dm_info->pwr_trk_triggered) {
+		rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER,
+			     GENMASK(17, 16), 0x03);
+		dm_info->pwr_trk_triggered = true;
+		return;
+	}
+
+	rtw8821a_phy_pwrtrack(rtwdev);
+	dm_info->pwr_trk_triggered = false;
+}
+
+static void rtw8821a_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, 0xa0a, pd[new_lvl]);
+}
+
+static void rtw8821a_fill_txdesc_checksum(struct rtw_dev *rtwdev,
+					  struct rtw_tx_pkt_info *pkt_info,
+					  u8 *txdesc)
+{
+	fill_txdesc_checksum_common(txdesc, 16);
+}
+
+static const struct rtw_intf_phy_para usb2_param_8821a[] = {
+	{0xFFFF, 0x00,
+	 RTW_IP_SEL_PHY,
+	 RTW_INTF_PHY_CUT_ALL,
+	 RTW_INTF_PHY_PLATFORM_ALL},
+};
+
+static const struct rtw_intf_phy_para usb3_param_8821a[] = {
+	{0xFFFF, 0x0000,
+	 RTW_IP_SEL_PHY,
+	 RTW_INTF_PHY_CUT_ALL,
+	 RTW_INTF_PHY_PLATFORM_ALL},
+};
+
+static const struct rtw_intf_phy_para_table phy_para_table_8821a = {
+	.usb2_para	= usb2_param_8821a,
+	.usb3_para	= usb3_param_8821a,
+	.n_usb2_para	= ARRAY_SIZE(usb2_param_8821a),
+	.n_usb3_para	= ARRAY_SIZE(usb2_param_8821a),
+};
+
+static struct rtw_hw_reg rtw8821a_dig[] = {
+	[0] = { .addr = 0xc50, .mask = 0x7f },
+	[1] = { .addr = 0xe50, .mask = 0x7f },
+};
+
+static struct rtw_page_table page_table_8821a[] = {
+	/* hq_num, nq_num, lq_num, exq_num, gapq_num */
+	{0, 0, 0, 0, 0},	/* SDIO */
+	{0, 0, 0, 0, 0},	/* PCI */
+	{8, 0, 0, 0, 1},	/* 2 bulk out endpoints */
+	{8, 0, 8, 0, 1},	/* 3 bulk out endpoints */
+	{8, 0, 8, 4, 1},	/* 4 bulk out endpoints */
+};
+
+static struct rtw_page_table page_table_8812a[] = {
+	/* hq_num, nq_num, lq_num, exq_num, gapq_num */
+	{0, 0, 0, 0, 0},	/* SDIO */
+	{0, 0, 0, 0, 0},	/* PCI */
+	{16, 0, 0, 0, 1},	/* 2 bulk out endpoints */
+	{16, 0, 16, 0, 1},	/* 3 bulk out endpoints */
+	{16, 0, 16, 0, 1},	/* 4 bulk out endpoints */
+};
+
+static struct rtw_rqpn rqpn_table_8821a[] = {
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
+
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
+
+	{RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH,
+	 RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+
+	{RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH},
+
+	{RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL,
+	 RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW,
+	 RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH},
+};
+
+static struct rtw_prioq_addrs prioq_addrs_8821a = {
+	.prio[RTW_DMA_MAPPING_EXTRA] = {
+		.rsvd = REG_RQPN_NPQ + 2, .avail = REG_RQPN_NPQ + 3,
+	},
+	.prio[RTW_DMA_MAPPING_LOW] = {
+		.rsvd = REG_RQPN + 1, .avail = REG_FIFOPAGE_CTRL_2 + 1,
+	},
+	.prio[RTW_DMA_MAPPING_NORMAL] = {
+		.rsvd = REG_RQPN_NPQ, .avail = REG_RQPN_NPQ + 1,
+	},
+	.prio[RTW_DMA_MAPPING_HIGH] = {
+		.rsvd = REG_RQPN, .avail = REG_FIFOPAGE_CTRL_2,
+	},
+	.wsize = false,
+};
+
+static struct rtw_chip_ops rtw8821a_ops = {
+	.power_on		= rtw8821a_power_on,
+	.power_off		= rtw8821a_power_off,
+	.phy_set_param		= NULL,
+	.read_efuse		= rtw8821a_read_efuse,
+	.query_rx_desc		= rtw8821a_query_rx_desc,
+	.set_channel		= rtw8821a_set_channel,
+	.mac_init		= NULL,
+	.read_rf		= rtw8821a_phy_read_rf,
+	.write_rf		= rtw_phy_write_rf_reg_sipi,
+	.set_antenna		= NULL,
+	.set_tx_power_index	= rtw8821a_set_tx_power_index,
+	.cfg_ldo25		= rtw8821a_cfg_ldo25,
+	.efuse_grant		= rtw8821a_efuse_grant,
+	.false_alarm_statistics	= rtw8821a_false_alarm_statistics,
+	.phy_calibration	= rtw8821a_phy_calibration,
+	.cck_pd_set		= rtw8821a_phy_cck_pd_set,
+	.pwr_track		= rtw8821a_pwr_track,
+	.config_bfee		= NULL,
+	.set_gid_table		= NULL,
+	.cfg_csi_rate		= NULL,
+	.fill_txdesc_checksum	= rtw8821a_fill_txdesc_checksum,
+	.coex_set_init		= rtw8821a_coex_cfg_init,
+	.coex_set_ant_switch	= rtw8821a_coex_cfg_ant_switch,
+	.coex_set_gnt_fix	= rtw8821a_coex_cfg_gnt_fix,
+	.coex_set_gnt_debug	= rtw8821a_coex_cfg_gnt_debug,
+	.coex_set_rfe_type	= rtw8821a_coex_cfg_rfe_type,
+	.coex_set_wl_tx_power	= rtw8821a_coex_cfg_wl_tx_power,
+	.coex_set_wl_rx_gain	= rtw8821a_coex_cfg_wl_rx_gain,
+};
+
+/* TODO */
+/* rssi in percentage % (dbm = % - 100) */
+static const u8 wl_rssi_step_8821a[] = {101, 45, 101, 40};
+static const u8 bt_rssi_step_8821a[] = {101, 101, 101, 101};
+
+/* table_sant_8821a, table_nsant_8821a, tdma_sant_8821a, and tdma_nsant_8821a
+ * are copied from rtw8821c.c because the 8821au driver's tables are not
+ * compatible with the coex code in rtw88.
+ *
+ * tdma case 112 (A2DP) byte 0 had to be modified from 0x61 to 0x51,
+ * otherwise the firmware gets confused after pausing the music:
+ * rtw_8821au 1-2:1.2: [BTCoex], Bt_info[1], len=7, data=[81 00 0a 01 00 00]
+ * - 81 means PAN (personal area network) when it should be 4x (A2DP)
+ * The music is not smooth with the PAN algorithm.
+ */
+
+/* Shared-Antenna Coex Table */
+static const struct coex_table_para table_sant_8821a[] = {
+	{0x55555555, 0x55555555}, /* case-0 */
+	{0x55555555, 0x55555555},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xfafafafa, 0xfafafafa}, /* case-5 */
+	{0x6a5a5555, 0xaaaaaaaa},
+	{0x6a5a56aa, 0x6a5a56aa},
+	{0x6a5a5a5a, 0x6a5a5a5a},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-10 */
+	{0x66555555, 0xaaaaaaaa},
+	{0x66555555, 0x6a5a5aaa},
+	{0x66555555, 0x6aaa6aaa},
+	{0x66555555, 0x6a5a5aaa},
+	{0x66555555, 0xaaaaaaaa}, /* case-15 */
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x6afa5afa},
+	{0xaaffffaa, 0xfafafafa},
+	{0xaa5555aa, 0x5a5a5a5a},
+	{0xaa5555aa, 0x6a5a5a5a}, /* case-20 */
+	{0xaa5555aa, 0xaaaaaaaa},
+	{0xffffffff, 0x55555555},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x5a5a5a5a},
+	{0xffffffff, 0x5a5a5aaa}, /* case-25 */
+	{0x55555555, 0x5a5a5a5a},
+	{0x55555555, 0xaaaaaaaa},
+	{0x66555555, 0x6a5a6a5a},
+	{0x66556655, 0x66556655},
+	{0x66556aaa, 0x6a5a6aaa}, /* case-30 */
+	{0xffffffff, 0x5aaa5aaa},
+	{0x56555555, 0x5a5a5aaa}
+};
+
+/* Non-Shared-Antenna Coex Table */
+static const struct coex_table_para table_nsant_8821a[] = {
+	{0xffffffff, 0xffffffff}, /* case-100 */
+	{0xffff55ff, 0xfafafafa},
+	{0x66555555, 0x66555555},
+	{0xaaaaaaaa, 0xaaaaaaaa},
+	{0x5a5a5a5a, 0x5a5a5a5a},
+	{0xffffffff, 0xffffffff}, /* case-105 */
+	{0x5afa5afa, 0x5afa5afa},
+	{0x55555555, 0xfafafafa},
+	{0x66555555, 0xfafafafa},
+	{0x66555555, 0x5a5a5a5a},
+	{0x66555555, 0x6a5a5a5a}, /* case-110 */
+	{0x66555555, 0xaaaaaaaa},
+	{0xffff55ff, 0xfafafafa},
+	{0xffff55ff, 0x5afa5afa},
+	{0xffff55ff, 0xaaaaaaaa},
+	{0xffff55ff, 0xffff55ff}, /* case-115 */
+	{0xaaffffaa, 0x5afa5afa},
+	{0xaaffffaa, 0xaaaaaaaa},
+	{0xffffffff, 0xfafafafa},
+	{0xffff55ff, 0xfafafafa},
+	{0xffffffff, 0xaaaaaaaa}, /* case-120 */
+	{0xffff55ff, 0x5afa5afa},
+	{0xffff55ff, 0x5afa5afa},
+	{0x55ff55ff, 0x55ff55ff}
+};
+
+/* Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_sant_8821a[] = {
+	{ {0x00, 0x00, 0x00, 0x00, 0x00} }, /* case-0 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} }, /* case-1 */
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x35, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x3a, 0x03, 0x11, 0x11} }, /* case-5 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x35, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-10 */
+	{ {0x61, 0x08, 0x03, 0x11, 0x15} },
+	{ {0x61, 0x08, 0x03, 0x10, 0x14} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-15 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x11, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x21, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }, /* case-20 */
+	{ {0x51, 0x4a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x08, 0x03, 0x30, 0x54} },
+	{ {0x55, 0x08, 0x03, 0x10, 0x54} },
+	{ {0x65, 0x10, 0x03, 0x11, 0x10} },
+	{ {0x51, 0x10, 0x03, 0x10, 0x51} }, /* case-25 */
+	{ {0x51, 0x21, 0x03, 0x10, 0x50} },
+	{ {0x61, 0x08, 0x03, 0x11, 0x11} }
+};
+
+/* Non-Shared-Antenna TDMA */
+static const struct coex_tdma_para tdma_nsant_8821a[] = {
+	{ {0x00, 0x00, 0x00, 0x40, 0x00} }, /* case-100 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x25, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x35, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x11} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} }, /* case-105 */
+	{ {0x61, 0x45, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x30, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x20, 0x03, 0x11, 0x10} },
+	{ {0x61, 0x10, 0x03, 0x11, 0x10} }, /* case-110 */
+	{ {0x61, 0x10, 0x03, 0x11, 0x11} },
+	{ {0x51, 0x08, 0x03, 0x10, 0x14} }, /* a2dp high rssi */
+	{ {0x51, 0x08, 0x03, 0x10, 0x54} }, /* a2dp not high rssi */
+	{ {0x51, 0x08, 0x03, 0x10, 0x55} },
+	{ {0x51, 0x08, 0x07, 0x10, 0x54} }, /* case-115 */
+	{ {0x51, 0x45, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x3a, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x30, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x21, 0x03, 0x10, 0x50} },
+	{ {0x51, 0x21, 0x03, 0x10, 0x50} }, /* case-120 */
+	{ {0x51, 0x10, 0x03, 0x10, 0x50} }
+};
+
+static const struct coex_5g_afh_map afh_5g_8821a[] = { {0, 0, 0} };
+
+/* TODO */
+static const struct coex_rf_para rf_para_tx_8821a[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 20, false, 7}, /* for WL-CPT */
+	{8, 17, true, 4},
+	{7, 18, true, 4},
+	{6, 19, true, 4},
+	{5, 20, true, 4}
+};
+
+static const struct coex_rf_para rf_para_rx_8821a[] = {
+	{0, 0, false, 7},  /* for normal */
+	{0, 20, false, 7}, /* for WL-CPT */
+	{3, 24, true, 5},
+	{2, 26, true, 5},
+	{1, 27, true, 5},
+	{0, 28, true, 5}
+};
+
+static_assert(ARRAY_SIZE(rf_para_tx_8821a) == ARRAY_SIZE(rf_para_rx_8821a));
+
+static const struct rtw_rfe_def rtw8821a_rfe_defs[] = {
+	[0] = { .phy_pg_tbl	= &rtw8821a_bb_pg_tbl,
+		.txpwr_lmt_tbl	= &rtw8821a_txpwr_lmt_tbl,
+		.pwr_track_tbl	= &rtw8821a_rtw_pwr_track_tbl, },
+};
+
+static const struct rtw_rfe_def rtw8812a_rfe_defs[] = {
+	[0] = { .phy_pg_tbl	= &rtw8812a_bb_pg_tbl,
+		.txpwr_lmt_tbl	= &rtw8812a_txpwr_lmt_tbl,
+		.pwr_track_tbl	= &rtw8812a_rtw_pwr_track_tbl, },
+	[1] = { .phy_pg_tbl	= &rtw8812a_bb_pg_tbl,
+		.txpwr_lmt_tbl	= &rtw8812a_txpwr_lmt_tbl,
+		.pwr_track_tbl	= &rtw8812a_rtw_pwr_track_tbl, },
+	[3] = { .phy_pg_tbl	= &rtw8812a_bb_pg_rfe3_tbl,
+		.txpwr_lmt_tbl	= &rtw8812a_txpwr_lmt_tbl,
+		.pwr_track_tbl	= &rtw8812a_rtw_pwr_track_rfe3_tbl, },
+};
+
+static const struct rtw_reg_domain coex_info_hw_regs_8821a[] = {
+	{0xCB0, MASKDWORD, RTW_REG_DOMAIN_MAC32},
+	{0xCB4, MASKDWORD, RTW_REG_DOMAIN_MAC32},
+	{0xCBA, MASKBYTE0, RTW_REG_DOMAIN_MAC8},
+	{0, 0, RTW_REG_DOMAIN_NL},
+	{0x430, MASKDWORD, RTW_REG_DOMAIN_MAC32},
+	{0x434, MASKDWORD, RTW_REG_DOMAIN_MAC32},
+	{0x42a, MASKLWORD, RTW_REG_DOMAIN_MAC16},
+	{0x426, MASKBYTE0, RTW_REG_DOMAIN_MAC8},
+	{0x45e, BIT(3), RTW_REG_DOMAIN_MAC8},
+	{0x454, MASKLWORD, RTW_REG_DOMAIN_MAC16},
+	{0, 0, RTW_REG_DOMAIN_NL},
+	{0x4c, BIT(24) | BIT(23), RTW_REG_DOMAIN_MAC32},
+	{0x64, BIT(0), RTW_REG_DOMAIN_MAC8},
+	{0x4c6, BIT(4), RTW_REG_DOMAIN_MAC8},
+	{0x40, BIT(5), RTW_REG_DOMAIN_MAC8},
+	{0x1, RFREG_MASK, RTW_REG_DOMAIN_RF_A},
+	{0, 0, RTW_REG_DOMAIN_NL},
+	{0x550, MASKDWORD, RTW_REG_DOMAIN_MAC32},
+	{0x522, MASKBYTE0, RTW_REG_DOMAIN_MAC8},
+	{0x953, BIT(1), RTW_REG_DOMAIN_MAC8},
+	{0xc50,  MASKBYTE0, RTW_REG_DOMAIN_MAC8},
+	{0x60A, MASKBYTE0, RTW_REG_DOMAIN_MAC8},
+};
+
+const struct rtw_chip_info rtw8821a_hw_spec = {
+	.ops = &rtw8821a_ops,
+	.id = RTW_CHIP_TYPE_8821A,
+	.fw_name = "rtw88/rtw8821a_fw.bin",
+	.wlan_cpu = RTW_WCPU_11N,
+	.tx_pkt_desc_sz = 40,
+	.tx_buf_desc_sz = 16,
+	.rx_pkt_desc_sz = 24,
+	.rx_buf_desc_sz = 8,
+	.phy_efuse_size = 512,
+	.log_efuse_size = 512,
+	.ptct_efuse_size = 96 + 1, /* TODO or just 18? */
+	.txff_size = 65536,
+	.rxff_size = 16128,
+	.rsvd_drv_pg_num = 8,
+	.txgi_factor = 1,
+	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
+	.csi_buf_pg_num = 0,
+	.band = RTW_BAND_2G | RTW_BAND_5G,
+	.page_size = 256,
+	.dig_min = 0x20,
+	.ht_supported = true,
+	.vht_supported = true,
+	.lps_deep_mode_supported = 0,
+	.sys_func_en = 0xFD,
+	.pwr_on_seq = card_enable_flow_8821a,
+	.pwr_off_seq = card_disable_flow_8821a,
+	.page_table = page_table_8821a,
+	.rqpn_table = rqpn_table_8821a,
+	.prioq_addrs = &prioq_addrs_8821a,
+	.intf_table = &phy_para_table_8821a,
+	.dig = rtw8821a_dig,
+	.rf_sipi_addr = {REG_LSSI_WRITE_A, REG_LSSI_WRITE_B},
+	.ltecoex_addr = NULL,
+	.mac_tbl = &rtw8821a_mac_tbl,
+	.agc_tbl = &rtw8821a_agc_tbl,
+	.bb_tbl = &rtw8821a_bb_tbl,
+	.rf_tbl = {&rtw8821a_rf_a_tbl},
+	.rfe_defs = rtw8821a_rfe_defs,
+	.rfe_defs_size = ARRAY_SIZE(rtw8821a_rfe_defs),
+	.rx_ldpc = false,
+	.hw_feature_report = false,
+	.c2h_ra_report_size = 4,
+	.old_datarate_fb_limit = true,
+	.usb_tx_agg_desc_num = 6,
+	.iqk_threshold = 8,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+
+	.coex_para_ver = 20190509, /* glcoex_ver_date_8821a_1ant */
+	.bt_desired_ver = 0x62, /* But for 2 ant it's 0x5c */
+	.scbd_support = false,
+	.new_scbd10_def = false,
+	.ble_hid_profile_support = false,
+	.wl_mimo_ps_support = false,
+	.pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
+	.bt_rssi_type = COEX_BTRSSI_RATIO,
+	.ant_isolation = 10,
+	.rssi_tolerance = 2,
+	.wl_rssi_step = wl_rssi_step_8821a,
+	.bt_rssi_step = bt_rssi_step_8821a,
+	.table_sant_num = ARRAY_SIZE(table_sant_8821a),
+	.table_sant = table_sant_8821a,
+	.table_nsant_num = ARRAY_SIZE(table_nsant_8821a),
+	.table_nsant = table_nsant_8821a,
+	.tdma_sant_num = ARRAY_SIZE(tdma_sant_8821a),
+	.tdma_sant = tdma_sant_8821a,
+	.tdma_nsant_num = ARRAY_SIZE(tdma_nsant_8821a),
+	.tdma_nsant = tdma_nsant_8821a,
+	.wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8821a),
+	.wl_rf_para_tx = rf_para_tx_8821a,
+	.wl_rf_para_rx = rf_para_rx_8821a,
+	.bt_afh_span_bw20 = 0x20,
+	.bt_afh_span_bw40 = 0x30,
+	.afh_5g_num = ARRAY_SIZE(afh_5g_8821a),
+	.afh_5g = afh_5g_8821a,
+
+	.coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8821a),
+	.coex_info_hw_regs = coex_info_hw_regs_8821a,
+};
+EXPORT_SYMBOL(rtw8821a_hw_spec);
+
+const struct rtw_chip_info rtw8812a_hw_spec = {
+	.ops = &rtw8821a_ops,
+	.id = RTW_CHIP_TYPE_8812A,
+	.fw_name = "rtw88/rtw8812a_fw.bin",
+	.wlan_cpu = RTW_WCPU_11N,
+	.tx_pkt_desc_sz = 40,
+	.tx_buf_desc_sz = 16,
+	.rx_pkt_desc_sz = 24,
+	.rx_buf_desc_sz = 8,
+	.phy_efuse_size = 512,
+	.log_efuse_size = 512,
+	.ptct_efuse_size = 96 + 1, /* TODO or just 18? */
+	.txff_size = 131072,
+	.rxff_size = 16128,
+	.rsvd_drv_pg_num = 9,
+	.txgi_factor = 1,
+	.is_pwr_by_rate_dec = true,
+	.max_power_index = 0x3f,
+	.csi_buf_pg_num = 0,
+	.band = RTW_BAND_2G | RTW_BAND_5G,
+	.page_size = 512,
+	.dig_min = 0x20,
+	.ht_supported = true,
+	.vht_supported = true,
+	.lps_deep_mode_supported = 0,
+	.sys_func_en = 0xFD,
+	.pwr_on_seq = card_enable_flow_8812a,
+	.pwr_off_seq = card_disable_flow_8812a,
+	.page_table = page_table_8812a,
+	.rqpn_table = rqpn_table_8821a,
+	.prioq_addrs = &prioq_addrs_8821a,
+	.intf_table = &phy_para_table_8821a,
+	.dig = rtw8821a_dig,
+	.rf_sipi_addr = {REG_LSSI_WRITE_A, REG_LSSI_WRITE_B},
+	.ltecoex_addr = NULL,
+	.mac_tbl = &rtw8812a_mac_tbl,
+	.agc_tbl = &rtw8812a_agc_tbl,
+	.bb_tbl = &rtw8812a_bb_tbl,
+	.rf_tbl = {&rtw8812a_rf_a_tbl, &rtw8812a_rf_b_tbl},
+	.rfe_defs = rtw8812a_rfe_defs,
+	.rfe_defs_size = ARRAY_SIZE(rtw8812a_rfe_defs),
+	.rx_ldpc = false,
+	.hw_feature_report = false,
+	.c2h_ra_report_size = 4,
+	.old_datarate_fb_limit = true,
+	.usb_tx_agg_desc_num = 1,
+	.iqk_threshold = 8,
+	.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16,
+	.max_scan_ie_len = IEEE80211_MAX_DATA_LEN,
+
+	.coex_para_ver = 0, /* no coex code in 8812au driver */
+	.bt_desired_ver = 0,
+	.scbd_support = false,
+	.new_scbd10_def = false,
+	.ble_hid_profile_support = false,
+	.wl_mimo_ps_support = false,
+	.pstdma_type = COEX_PSTDMA_FORCE_LPSOFF,
+	.bt_rssi_type = COEX_BTRSSI_RATIO,
+	.ant_isolation = 15,
+	.rssi_tolerance = 2,
+	.wl_rssi_step = wl_rssi_step_8821a,
+	.bt_rssi_step = bt_rssi_step_8821a,
+	.table_sant_num = 0,
+	.table_sant = NULL,
+	.table_nsant_num = 0,
+	.table_nsant = NULL,
+	.tdma_sant_num = 0,
+	.tdma_sant = NULL,
+	.tdma_nsant_num = 0,
+	.tdma_nsant = NULL,
+	.wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8821a),
+	.wl_rf_para_tx = rf_para_tx_8821a,
+	.wl_rf_para_rx = rf_para_rx_8821a,
+	.bt_afh_span_bw20 = 0x20,
+	.bt_afh_span_bw40 = 0x30,
+	.afh_5g_num = 0,
+	.afh_5g = NULL,
+
+	.coex_info_hw_regs_num = 0,
+	.coex_info_hw_regs = NULL,
+};
+EXPORT_SYMBOL(rtw8812a_hw_spec);
+
+MODULE_FIRMWARE("rtw88/rtw8821a_fw.bin");
+MODULE_FIRMWARE("rtw88/rtw8812a_fw.bin");
+
+MODULE_AUTHOR("Realtek Corporation");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.h b/drivers/net/wireless/realtek/rtw88/rtw8821a.h
new file mode 100644
index 000000000000..7f1c2d2eb6d2
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.h
@@ -0,0 +1,385 @@ 
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2018-2019  Realtek Corporation
+ */
+
+#ifndef __RTW8821A_H__
+#define __RTW8821A_H__
+
+#include <asm/byteorder.h>
+
+struct rtw8821au_efuse {
+	u8 res4[48];			/* 0xd0 */
+	u8 vid[2];			/* 0x100 */
+	u8 pid[2];
+	u8 res8[3];
+	u8 mac_addr[ETH_ALEN];		/* 0x107 */
+	u8 res9[243];
+};
+
+struct rtw8812au_efuse {
+	u8 vid[2];			/* 0xd0 */
+	u8 pid[2];			/* 0xd2 */
+	u8 res0[3];
+	u8 mac_addr[ETH_ALEN];		/* 0xd7 */
+	u8 res1[291];
+};
+
+struct rtw8821a_efuse {
+	__le16 rtl_id;
+	u8 res0[6];			/* 0x02 */
+	u8 usb_mode;			/* 0x08 */
+	u8 res1[7];			/* 0x09 */
+
+	/* power index for four RF paths */
+	struct rtw_txpwr_idx txpwr_idx_table[4];
+
+	u8 channel_plan;		/* 0xb8 */
+	u8 xtal_k;
+	u8 thermal_meter;
+	u8 iqk_lck;
+	u8 pa_type;			/* 0xbc */
+	u8 lna_type_2g;			/* 0xbd */
+	u8 res2;
+	u8 lna_type_5g;			/* 0xbf */
+	u8 res3;
+	u8 rf_board_option;		/* 0xc1 */
+	u8 rf_feature_option;
+	u8 rf_bt_setting;
+	u8 eeprom_version;
+	u8 eeprom_customer_id;		/* 0xc5 */
+	u8 tx_bb_swing_setting_2g;
+	u8 tx_bb_swing_setting_5g;
+	u8 tx_pwr_calibrate_rate;
+	u8 rf_antenna_option;		/* 0xc9 */
+	u8 rfe_option;
+	u8 country_code[2];
+	u8 res4[3];
+	union {
+		struct rtw8821au_efuse rtw8821au;
+		struct rtw8812au_efuse rtw8812au;
+	};
+} __packed;
+
+static_assert(sizeof(struct rtw8821a_efuse) == 512);
+
+extern const struct rtw_chip_info rtw8821a_hw_spec;
+extern const struct rtw_chip_info rtw8812a_hw_spec;
+
+#define BIT_FEN_USBA				BIT(2)
+#define BIT_FEN_PCIEA				BIT(6)
+#define WLAN_SLOT_TIME				0x09
+#define WLAN_PIFS_TIME				0x19
+#define WLAN_SIFS_CCK_CONT_TX			0xA
+#define WLAN_SIFS_OFDM_CONT_TX			0xE
+#define WLAN_SIFS_CCK_TRX			0x10
+#define WLAN_SIFS_OFDM_TRX			0x10
+#define WLAN_VO_TXOP_LIMIT			0x186
+#define WLAN_VI_TXOP_LIMIT			0x3BC
+#define WLAN_RDG_NAV				0x05
+#define WLAN_TXOP_NAV				0x1B
+#define WLAN_CCK_RX_TSF				0x30
+#define WLAN_OFDM_RX_TSF			0x30
+#define WLAN_TBTT_PROHIBIT			0x04
+#define WLAN_TBTT_HOLD_TIME			0x064
+#define WLAN_DRV_EARLY_INT			0x04
+#define WLAN_BCN_DMA_TIME			0x02
+
+#define WLAN_RX_FILTER0				0x0FFFFFFF
+#define WLAN_RX_FILTER2				0xFFFF
+#define WLAN_RCR_CFG				0xE400220E
+#define WLAN_RXPKT_MAX_SZ			12288
+#define WLAN_RXPKT_MAX_SZ_512			(WLAN_RXPKT_MAX_SZ >> 9)
+
+#define WLAN_AMPDU_MAX_TIME			0x70
+#define WLAN_RTS_LEN_TH				0xFF
+#define WLAN_RTS_TX_TIME_TH			0x08
+#define WLAN_MAX_AGG_PKT_LIMIT			0x20
+#define WLAN_RTS_MAX_AGG_PKT_LIMIT		0x20
+#define FAST_EDCA_VO_TH				0x06
+#define FAST_EDCA_VI_TH				0x06
+#define FAST_EDCA_BE_TH				0x06
+#define FAST_EDCA_BK_TH				0x06
+#define WLAN_BAR_RETRY_LIMIT			0x01
+#define WLAN_RA_TRY_RATE_AGG_LIMIT		0x08
+
+#define WLAN_TX_FUNC_CFG1			0x30
+#define WLAN_TX_FUNC_CFG2			0x30
+#define WLAN_MAC_OPT_NORM_FUNC1			0x98
+#define WLAN_MAC_OPT_LB_FUNC1			0x80
+#define WLAN_MAC_OPT_FUNC2			0xb0810041
+
+#define WLAN_SIFS_CFG	(WLAN_SIFS_CCK_CONT_TX | \
+			(WLAN_SIFS_OFDM_CONT_TX << BIT_SHIFT_SIFS_OFDM_CTX) | \
+			(WLAN_SIFS_CCK_TRX << BIT_SHIFT_SIFS_CCK_TRX) | \
+			(WLAN_SIFS_OFDM_TRX << BIT_SHIFT_SIFS_OFDM_TRX))
+
+#define WLAN_TBTT_TIME	(WLAN_TBTT_PROHIBIT |\
+			(WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP))
+
+#define WLAN_NAV_CFG		(WLAN_RDG_NAV | (WLAN_TXOP_NAV << 16))
+#define WLAN_RX_TSF_CFG		(WLAN_CCK_RX_TSF | (WLAN_OFDM_RX_TSF) << 8)
+#define WLAN_PRE_TXCNT_TIME_TH			0x1E4
+
+struct rtw8821a_phy_status_rpt {
+	/* DWORD 0 */
+	u8 gain_trsw[2]; /* path-A and path-B { TRSW, gain[6:0] } */
+	u8 chl_num_lsb; /* channel number[7:0] */
+#ifdef __LITTLE_ENDIAN
+	u8 chl_num_msb:2; /* channel number[9:8] */
+	u8 sub_chnl:4; /* sub-channel location[3:0] */
+	u8 r_rfmod:2; /* RF mode[1:0] */
+#else
+	u8 r_rfmod:2;
+	u8 sub_chnl:4;
+	u8 chl_num_msb:2;
+#endif
+
+	/* DWORD 1 */
+	u8 pwdb_all; /* CCK signal quality / OFDM pwdb all */
+	s8 cfosho[2]; /* CCK AGC report and CCK_BB_Power */
+		      /* OFDM path-A and path-B short CFO */
+#ifdef __LITTLE_ENDIAN
+	u8 resvd_0:6;
+	u8 bt_rf_ch_msb:2; /* 8812A: 2'b0 8814A: bt rf channel keep[7:6] */
+#else
+	u8 bt_rf_ch_msb:2;
+	u8 resvd_0:6;
+#endif
+
+	/* DWORD 2 */
+#ifdef __LITTLE_ENDIAN
+	u8 ant_div_sw_a:1; /* 8812A: ant_div_sw_a 8814A: 1'b0 */
+	u8 ant_div_sw_b:1; /* 8812A: ant_div_sw_b 8814A: 1'b0 */
+	u8 bt_rf_ch_lsb:6; /* 8812A: 6'b0 8814A: bt rf channel keep[5:0] */
+#else
+	u8 bt_rf_ch_lsb:6;
+	u8 ant_div_sw_b:1;
+	u8 ant_div_sw_a:1;
+#endif
+	s8 cfotail[2]; /* path-A and path-B CFO tail */
+	u8 pcts_msk_rpt_0; /* PCTS mask report[7:0] */
+
+	/* DWORD 3 */
+	u8 pcts_msk_rpt_1; /* PCTS mask report[15:8] */
+	s8 rxevm[2]; /* stream 1 and stream 2 RX EVM */
+	s8 rxsnr[2]; /* path-A and path-B RX SNR */
+
+	/* DWORD 4 */
+	/* rxsnr[1] */
+	u8 pcts_msk_rpt_2; /* PCTS mask report[23:16] */
+#ifdef __LITTLE_ENDIAN
+	u8 pcts_msk_rpt_3:6; /* PCTS mask report[29:24] */
+	u8 pcts_rpt_valid:1;
+	u8 resvd_1:1; /* 1'b0 */
+#else
+	u8 resvd_1:1;
+	u8 pcts_rpt_valid:1;
+	u8 pcts_msk_rpt_3:6;
+#endif
+	s8 rxevm_cd[2]; /* 8812A: 16'b0 8814A: stream 3 and stream 4 RX EVM */
+
+	/* DWORD 5 */
+	/* rxevm_cd[1] */
+	u8 csi_current[2]; /* 8812A: stream 1 and 2 CSI */
+			   /* 8814A: path-C and path-D RX SNR */
+	u8 gain_trsw_cd[2]; /* path-C and path-D { TRSW, gain[6:0] } */
+
+	/* DWORD 6 */
+	/* gain_trsw_cd[1] */
+	s8 sigevm; /* signal field EVM */
+#ifdef __LITTLE_ENDIAN
+	u8 antidx_antc:3; /* 8812A: 3'b0 8814A: antidx_antc[2:0] */
+	u8 antidx_antd:3; /* 8812A: 3'b0 8814A: antidx_antd[2:0] */
+	u8 dpdt_ctrl_keep:1; /* 8812A: 1'b0 8814A: dpdt_ctrl_keep */
+	u8 gnt_bt_keep:1; /* 8812A: 1'b0 8814A: GNT_BT_keep */
+#else
+	u8 gnt_bt_keep:1;
+	u8 dpdt_ctrl_keep:1;
+	u8 antidx_antd:3;
+	u8 antidx_antc:3;
+#endif
+#ifdef __LITTLE_ENDIAN
+	u8 antidx_anta:3; /* antidx_anta[2:0] */
+	u8 antidx_antb:3; /* antidx_antb[2:0] */
+	u8 hw_antsw_occur:2; /* 1'b0 */
+#else
+	u8 hw_antsw_occur:2;
+	u8 antidx_antb:3;
+	u8 antidx_anta:3;
+#endif
+};
+
+#define REG_SYS_CTRL				0x000
+#define BIT_FEN_EN				BIT(26)
+#define REG_APS_FSMCO				0x04
+#define  APS_FSMCO_MAC_ENABLE			BIT(8)
+#define  APS_FSMCO_MAC_OFF			BIT(9)
+#define  APS_FSMCO_HW_POWERDOWN			BIT(15)
+#define REG_ACLK_MON				0x3e
+#define REG_RF_B_CTRL				0x76
+#define REG_HIMR0				0xb0
+#define REG_HISR0				0xb4
+#define REG_HIMR1				0xb8
+#define REG_HISR1				0xbc
+#define HCI_TXDMA_EN				BIT(0)
+#define HCI_RXDMA_EN				BIT(1)
+#define TXDMA_EN				BIT(2)
+#define RXDMA_EN				BIT(3)
+#define PROTOCOL_EN				BIT(4)
+#define SCHEDULE_EN				BIT(5)
+#define MACTXEN					BIT(6)
+#define MACRXEN					BIT(7)
+#define ENSWBCN					BIT(8)
+#define ENSEC					BIT(9)
+#define CALTMR_EN				BIT(10)	/* 32k CAL TMR enable */
+#define REG_MSR					0x102
+#define REG_PBP					0x104
+#define PBP_RX_MASK				0x0f
+#define PBP_TX_MASK				0xf0
+#define PBP_64					0x0
+#define PBP_128					0x1
+#define PBP_256					0x2
+#define PBP_512					0x3
+#define PBP_1024				0x4
+#define REG_LLT_INIT				0x01E0
+#define BIT_LLT_WRITE_ACCESS			BIT(30)
+#define REG_DWBCN1_CTRL				0x228
+#define REG_EARLY_MODE_CONTROL			0x2bc
+#define REG_TXPKT_EMPTY				0x41a
+#define REG_AMPDU_MAX_LENGTH			0x458
+#define REG_FAST_EDCA_CTRL			0x460
+#define REG_TX_RPT_CTRL				0x4ec
+#define REG_TX_RPT_TIME				0x4f0
+#define REG_BCNTCFG				0x510
+#define REG_INIRTS_RATE_SEL			0x0480
+#define REG_HTSTFWT				0x800
+#define REG_CCK_RPT_FORMAT			0x804
+#define BIT_CCK_RPT_FORMAT			BIT(16)
+#define REG_RXPSEL				0x808
+#define BIT_RX_PSEL_RST				(BIT(28) | BIT(29))
+#define REG_TXPSEL				0x80c
+#define REG_RXCCAMSK				0x814
+#define REG_CCASEL				0x82c
+#define REG_PDMFTH				0x830
+#define REG_BWINDICATION			0x834
+#define REG_CCA2ND				0x838
+#define REG_L1WT				0x83c
+#define REG_L1PKWT				0x840
+#define REG_L1PKTH				0x848
+#define REG_MRC					0x850
+#define REG_CLKTRK				0x860
+#define REG_ADCCLK				0x8ac
+#define REG_HSSI_READ				0x8b0
+#define REG_FPGA0_XCD_RF_PARA			0x8b4
+#define REG_ADC160				0x8c4
+#define REG_ADC40				0x8c8
+#define REG_CHFIR				0x8f0
+#define REG_ANTSEL_SW				0x900
+#define REG_SINGLE_TONE_CONT_TX			0x914
+#define REG_CDDTXP				0x93c
+#define REG_TXPSEL1				0x940
+#define REG_ACBB0				0x948
+#define REG_ACBBRXFIR				0x94c
+#define REG_ACGG2TBL				0x958
+#define REG_FAS					0x9a4
+#define REG_RXSB				0xa00
+#define REG_CCK_RX				0xa04
+#define REG_PWRTH				0xa08
+#define REG_CCA_FLTR				0xa20
+#define REG_TXSF2				0xa24
+#define REG_TXSF6				0xa28
+#define REG_FA_CCK				0xa5c
+#define REG_RXDESC				0xa2c
+#define REG_ENTXCCK				0xa80
+#define BTG_LNA					0xfc84
+#define WLG_LNA					0x7532
+#define REG_ENRXCCA				0xa84
+#define BTG_CCA					0x0e
+#define WLG_CCA					0x12
+#define REG_PWRTH2				0xaa8
+#define REG_CSRATIO				0xaaa
+#define REG_TXFILTER				0xaac
+#define REG_CNTRST				0xb58
+#define REG_AGCTR_A				0xc08
+#define REG_RX_IQC_AB_A				0xc10
+#define REG_TXSCALE_A				0xc1c
+#define BB_SWING_MASK				GENMASK(31, 21)
+#define REG_TXDFIR				0xc20
+#define REG_RXIGI_A				0xc50
+#define REG_TX_PWR_TRAINING_A			0xc54
+#define REG_AFE_PWR1_A				0xc60
+#define REG_AFE_PWR2_A				0xc64
+#define REG_RX_WAIT_CCA_TX_CCK_RFON_A		0xc68
+#define REG_LSSI_WRITE_A			0xc90
+#define REG_TXAGCIDX				0xc94
+#define REG_TRSW				0xca0
+#define REG_RFESEL0				0xcb0
+#define REG_RFE_PINMUX_A			0xcb0
+#define REG_RFESEL8				0xcb4
+#define REG_RFE_INV_A				0xcb4
+#define RFE_INV_MASK				0x3ff00000
+#define REG_RFECTL_A				0xcb8
+#define B_BTG_SWITCH				BIT(16)
+#define B_CTRL_SWITCH				BIT(18)
+#define B_WL_SWITCH				(BIT(20) | BIT(22))
+#define B_WLG_SWITCH				BIT(21)
+#define B_WLA_SWITCH				BIT(23)
+#define REG_RFEINV				0xcbc
+#define REG_PI_READ_A				0xd04
+#define REG_SI_READ_A				0xd08
+#define REG_PI_READ_B				0xd44
+#define REG_SI_READ_B				0xd48
+#define REG_AGCTR_B				0xe08
+#define REG_RX_IQC_AB_B				0xe10
+#define REG_TXSCALE_B				0xe1c
+#define REG_RXIGI_B				0xe50
+#define REG_TX_PWR_TRAINING_B			0xe54
+#define REG_AFE_PWR1_B				0xe60
+#define REG_AFE_PWR2_B				0xe64
+#define REG_RX_WAIT_CCA_TX_CCK_RFON_B		0xe68
+#define REG_LSSI_WRITE_B			0xe90
+#define REG_RFE_PINMUX_B			0xeb0
+#define REG_RFE_INV_B				0xeb4
+#define REG_RFECTL_B				0xeb8
+#define REG_CRC_CCK				0xf04
+#define REG_CRC_OFDM				0xf14
+#define REG_CRC_HT				0xf10
+#define REG_CRC_VHT				0xf0c
+#define REG_CCA_OFDM				0xf08
+#define REG_FA_OFDM				0xf48
+#define REG_CCA_CCK				0xfcc
+#define REG_DMEM_CTRL				0x1080
+#define BIT_WL_RST				BIT(16)
+#define REG_ANTWT				0x1904
+#define REG_IQKFAILMSK				0x1bf0
+#define BIT_MASK_R_RFE_SEL_15			GENMASK(31, 28)
+#define BIT_SDIO_INT				BIT(18)
+#define BT_CNT_ENABLE				0x1
+#define BIT_BCN_QUEUE				BIT(3)
+#define BCN_PRI_EN				0x1
+#define PTA_CTRL_PIN				0x66
+#define DPDT_CTRL_PIN				0x77
+#define ANTDIC_CTRL_PIN				0x88
+#define REG_CTRL_TYPE				0x67
+#define BIT_CTRL_TYPE1				BIT(5)
+#define BIT_CTRL_TYPE2				BIT(4)
+#define CTRL_TYPE_MASK				GENMASK(15, 8)
+#define REG_USB_HRPWM				0xfe58
+
+#define RF18_BAND_MASK				(BIT(16) | BIT(9) | BIT(8))
+#define RF18_BAND_2G				(0)
+#define RF18_BAND_5G				(BIT(16) | BIT(8))
+#define RF18_CHANNEL_MASK			(MASKBYTE0)
+#define RF18_RFSI_MASK				(BIT(18) | BIT(17))
+#define RF18_RFSI_GE				(BIT(17))
+#define RF18_RFSI_GT				(BIT(18))
+#define RF18_BW_MASK				(BIT(11) | BIT(10))
+#define RF18_BW_20M				(BIT(11) | BIT(10))
+#define RF18_BW_40M				(BIT(10))
+#define RF18_BW_80M				(0)
+#define RF_MODE_TABLE_ADDR			0x30
+#define RF_MODE_TABLE_DATA0			0x31
+#define RF_MODE_TABLE_DATA1			0x32
+#define RF_LCK					0xb4
+
+#endif