diff mbox series

[v4,3/7] wifi: rtw88: Add rtw88xxa.{c,h}

Message ID b8590382-a954-412d-a96b-63e360b97acc@gmail.com
State New
Headers show
Series wifi: rtw88: Add support for RTL8821AU and RTL8812AU | expand

Commit Message

Bitterblue Smith Oct. 30, 2024, 6:27 p.m. UTC
These contain code shared by both RTL8821AU and RTL8812AU chips.

Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
v2:
 - Rename rtw8821a.{c,h} to rtw88xxa.{c,h}.
 - Rename functions to use the "rtw88xxa" prefix.
 - Rename struct rtw8821a_efuse to rtw88xxa_efuse.
 - Keep only the common code in this patch. The rest is in the next
   two patches now.
 - Fix copyright year.
 - Print hw_cap in rtw88xxa_read_usb_type.
 - Use pointer to rtwdev->efuse in rtw88xxa_get_bb_swing instead of
   making a copy of the struct.
 - Make rtw88xxa_get_swing_index return 24 instead of 0 by default.
 - Use new register definitions.
 - Delete rtw8821a_query_rx_desc. Use the new rtw_rx_query_rx_desc
   function instead.
 - Use u32_get_bits instead of FIELD_GET in
   rtw88xxa_false_alarm_statistics.
 - Use existing bit definitions for REG_CR bits instead of creating new
   ones.
 - Use existing name of REG_CCK0_FAREPORT instead of adding a new name.
 - Delete unnecessary braces and semicolons in switch statements.
 - Initialise arrays with {} instead of {0}.
 - Empty functions should have the braces on separate lines.
 - Include reg.h in rtw88xxa.h.
 - Make struct rtw8821au_efuse and rtw8812au_efuse __packed.
 - Delete most macros from rtw88xxa.h. Some were moved to reg.h, some
   were unused (inherited from rtw8821c.h).
 - Fix some indentation.
 - Use the correct IQK function for 8812au in the tx power tracking.
   v1 was accidentally using the IQK function meant for 8821au.
 - Rename struct rtw8821a_phy_status_rpt to rtw_jaguar_phy_status_rpt.
   It's shared by RTL8821AU and RTL8812AU, and maybe RTL8814AU in the
   future. These are all "Jaguar" chips.
 - Make struct rtw_jaguar_phy_status_rpt __packed.
 - Access struct rtw_jaguar_phy_status_rpt with le32_get_bits instead
   of bit fields.

v3:
 - No change.

v4:
 - No change.
---
 drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 1989 +++++++++++++++++
 drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  175 ++
 2 files changed, 2164 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw88xxa.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/rtw88xxa.h

Comments

Kees Bakker Nov. 12, 2024, 8:04 p.m. UTC | #1
Op 30-10-2024 om 19:27 schreef Bitterblue Smith:
> These contain code shared by both RTL8821AU and RTL8812AU chips.
>
> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
> ---
> v2:
>   - Rename rtw8821a.{c,h} to rtw88xxa.{c,h}.
>   - Rename functions to use the "rtw88xxa" prefix.
>   - Rename struct rtw8821a_efuse to rtw88xxa_efuse.
>   - Keep only the common code in this patch. The rest is in the next
>     two patches now.
>   - Fix copyright year.
>   - Print hw_cap in rtw88xxa_read_usb_type.
>   - Use pointer to rtwdev->efuse in rtw88xxa_get_bb_swing instead of
>     making a copy of the struct.
>   - Make rtw88xxa_get_swing_index return 24 instead of 0 by default.
>   - Use new register definitions.
>   - Delete rtw8821a_query_rx_desc. Use the new rtw_rx_query_rx_desc
>     function instead.
>   - Use u32_get_bits instead of FIELD_GET in
>     rtw88xxa_false_alarm_statistics.
>   - Use existing bit definitions for REG_CR bits instead of creating new
>     ones.
>   - Use existing name of REG_CCK0_FAREPORT instead of adding a new name.
>   - Delete unnecessary braces and semicolons in switch statements.
>   - Initialise arrays with {} instead of {0}.
>   - Empty functions should have the braces on separate lines.
>   - Include reg.h in rtw88xxa.h.
>   - Make struct rtw8821au_efuse and rtw8812au_efuse __packed.
>   - Delete most macros from rtw88xxa.h. Some were moved to reg.h, some
>     were unused (inherited from rtw8821c.h).
>   - Fix some indentation.
>   - Use the correct IQK function for 8812au in the tx power tracking.
>     v1 was accidentally using the IQK function meant for 8821au.
>   - Rename struct rtw8821a_phy_status_rpt to rtw_jaguar_phy_status_rpt.
>     It's shared by RTL8821AU and RTL8812AU, and maybe RTL8814AU in the
>     future. These are all "Jaguar" chips.
>   - Make struct rtw_jaguar_phy_status_rpt __packed.
>   - Access struct rtw_jaguar_phy_status_rpt with le32_get_bits instead
>     of bit fields.
>
> v3:
>   - No change.
>
> v4:
>   - No change.
> ---
>   drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 1989 +++++++++++++++++
>   drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  175 ++
>   2 files changed, 2164 insertions(+)
>   create mode 100644 drivers/net/wireless/realtek/rtw88/rtw88xxa.c
>   create mode 100644 drivers/net/wireless/realtek/rtw88/rtw88xxa.h
> [...]

> +static void rtw88xxau_init_queue_reserved_page(struct rtw_dev *rtwdev)
> +{
> +	const struct rtw_chip_info *chip = rtwdev->chip;
> +	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
> +	const struct rtw_page_table *pg_tbl = NULL;
> +	u16 pubq_num;
> +	u32 val32;
> +
> +	switch (rtw_hci_type(rtwdev)) {
> +	case RTW_HCI_TYPE_PCIE:
> +		pg_tbl = &chip->page_table[1];
> +		break;
> +	case RTW_HCI_TYPE_USB:
> +		if (rtwdev->hci.bulkout_num == 2)
> +			pg_tbl = &chip->page_table[2];
> +		else if (rtwdev->hci.bulkout_num == 3)
> +			pg_tbl = &chip->page_table[3];
> +		else if (rtwdev->hci.bulkout_num == 4)
> +			pg_tbl = &chip->page_table[4];
> +		break;
> +	case RTW_HCI_TYPE_SDIO:
> +		pg_tbl = &chip->page_table[0];
> +		break;
> +	default:
All other switch (rtw_hci_type(rtwdev)) in this module handle
the default with an error. Shouldn't you be doing that here too?
In the default case the code continues with pg_tbl == NULL
and thus it will crash.
> +		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);
> +}
> [...]
Ping-Ke Shih Nov. 13, 2024, 1:14 a.m. UTC | #2
> > +static void rtw88xxau_init_queue_reserved_page(struct rtw_dev *rtwdev)
> > +{
> > +     const struct rtw_chip_info *chip = rtwdev->chip;
> > +     struct rtw_fifo_conf *fifo = &rtwdev->fifo;
> > +     const struct rtw_page_table *pg_tbl = NULL;
> > +     u16 pubq_num;
> > +     u32 val32;
> > +
> > +     switch (rtw_hci_type(rtwdev)) {
> > +     case RTW_HCI_TYPE_PCIE:
> > +             pg_tbl = &chip->page_table[1];
> > +             break;
> > +     case RTW_HCI_TYPE_USB:
> > +             if (rtwdev->hci.bulkout_num == 2)
> > +                     pg_tbl = &chip->page_table[2];
> > +             else if (rtwdev->hci.bulkout_num == 3)
> > +                     pg_tbl = &chip->page_table[3];
> > +             else if (rtwdev->hci.bulkout_num == 4)
> > +                     pg_tbl = &chip->page_table[4];
> > +             break;
> > +     case RTW_HCI_TYPE_SDIO:
> > +             pg_tbl = &chip->page_table[0];
> > +             break;
> > +     default:
> All other switch (rtw_hci_type(rtwdev)) in this module handle
> the default with an error. Shouldn't you be doing that here too?
> In the default case the code continues with pg_tbl == NULL
> and thus it will crash.

Actually three supported HCI types are listed, so it will never fall into
the default case, which I think this is just to avoid static checkers complain. 
However, 'return' would be more suitable than 'break' for default case. 

> +             break;
> +     }
Bitterblue Smith Nov. 13, 2024, 11:53 p.m. UTC | #3
On 12/11/2024 22:04, Kees Bakker wrote:
> Op 30-10-2024 om 19:27 schreef Bitterblue Smith:
>> These contain code shared by both RTL8821AU and RTL8812AU chips.
>>
>> Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
>> ---
>> v2:
>>   - Rename rtw8821a.{c,h} to rtw88xxa.{c,h}.
>>   - Rename functions to use the "rtw88xxa" prefix.
>>   - Rename struct rtw8821a_efuse to rtw88xxa_efuse.
>>   - Keep only the common code in this patch. The rest is in the next
>>     two patches now.
>>   - Fix copyright year.
>>   - Print hw_cap in rtw88xxa_read_usb_type.
>>   - Use pointer to rtwdev->efuse in rtw88xxa_get_bb_swing instead of
>>     making a copy of the struct.
>>   - Make rtw88xxa_get_swing_index return 24 instead of 0 by default.
>>   - Use new register definitions.
>>   - Delete rtw8821a_query_rx_desc. Use the new rtw_rx_query_rx_desc
>>     function instead.
>>   - Use u32_get_bits instead of FIELD_GET in
>>     rtw88xxa_false_alarm_statistics.
>>   - Use existing bit definitions for REG_CR bits instead of creating new
>>     ones.
>>   - Use existing name of REG_CCK0_FAREPORT instead of adding a new name.
>>   - Delete unnecessary braces and semicolons in switch statements.
>>   - Initialise arrays with {} instead of {0}.
>>   - Empty functions should have the braces on separate lines.
>>   - Include reg.h in rtw88xxa.h.
>>   - Make struct rtw8821au_efuse and rtw8812au_efuse __packed.
>>   - Delete most macros from rtw88xxa.h. Some were moved to reg.h, some
>>     were unused (inherited from rtw8821c.h).
>>   - Fix some indentation.
>>   - Use the correct IQK function for 8812au in the tx power tracking.
>>     v1 was accidentally using the IQK function meant for 8821au.
>>   - Rename struct rtw8821a_phy_status_rpt to rtw_jaguar_phy_status_rpt.
>>     It's shared by RTL8821AU and RTL8812AU, and maybe RTL8814AU in the
>>     future. These are all "Jaguar" chips.
>>   - Make struct rtw_jaguar_phy_status_rpt __packed.
>>   - Access struct rtw_jaguar_phy_status_rpt with le32_get_bits instead
>>     of bit fields.
>>
>> v3:
>>   - No change.
>>
>> v4:
>>   - No change.
>> ---
>>   drivers/net/wireless/realtek/rtw88/rtw88xxa.c | 1989 +++++++++++++++++
>>   drivers/net/wireless/realtek/rtw88/rtw88xxa.h |  175 ++
>>   2 files changed, 2164 insertions(+)
>>   create mode 100644 drivers/net/wireless/realtek/rtw88/rtw88xxa.c
>>   create mode 100644 drivers/net/wireless/realtek/rtw88/rtw88xxa.h
>> [...]
> 
>> +static void rtw88xxau_init_queue_reserved_page(struct rtw_dev *rtwdev)
>> +{
>> +    const struct rtw_chip_info *chip = rtwdev->chip;
>> +    struct rtw_fifo_conf *fifo = &rtwdev->fifo;
>> +    const struct rtw_page_table *pg_tbl = NULL;
>> +    u16 pubq_num;
>> +    u32 val32;
>> +
>> +    switch (rtw_hci_type(rtwdev)) {
>> +    case RTW_HCI_TYPE_PCIE:
>> +        pg_tbl = &chip->page_table[1];
>> +        break;
>> +    case RTW_HCI_TYPE_USB:
>> +        if (rtwdev->hci.bulkout_num == 2)
>> +            pg_tbl = &chip->page_table[2];
>> +        else if (rtwdev->hci.bulkout_num == 3)
>> +            pg_tbl = &chip->page_table[3];
>> +        else if (rtwdev->hci.bulkout_num == 4)
>> +            pg_tbl = &chip->page_table[4];
>> +        break;
>> +    case RTW_HCI_TYPE_SDIO:
>> +        pg_tbl = &chip->page_table[0];
>> +        break;
>> +    default:
> All other switch (rtw_hci_type(rtwdev)) in this module handle
> the default with an error. Shouldn't you be doing that here too?

I guess, if you want. I don't remember why there is no error here.

> In the default case the code continues with pg_tbl == NULL
> and thus it will crash.

The default case should be impossible to reach because we don't
have other interface types. And probably one of those other switch
statements is executed earlier, so this function wouldn't run.

>> +        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);
>> +}
>> [...]
diff mbox series

Patch

diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
new file mode 100644
index 000000000000..71e61b9c0bec
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c
@@ -0,0 +1,1989 @@ 
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/* Copyright(c) 2024  Realtek Corporation
+ */
+
+#include <linux/usb.h>
+#include "main.h"
+#include "coex.h"
+#include "phy.h"
+#include "rtw88xxa.h"
+#include "mac.h"
+#include "reg.h"
+#include "sec.h"
+#include "debug.h"
+#include "bf.h"
+#include "efuse.h"
+#include "usb.h"
+
+void rtw88xxa_efuse_grant(struct rtw_dev *rtwdev, bool on)
+{
+	if (on) {
+		rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON);
+
+		rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR);
+		rtw_write16_set(rtwdev, REG_SYS_CLKR,
+				BIT_LOADER_CLK_EN | BIT_ANA8M);
+	} else {
+		rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF);
+	}
+}
+EXPORT_SYMBOL(rtw88xxa_efuse_grant);
+
+static void rtw8812a_read_amplifier_type(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+
+	efuse->ext_pa_2g = (efuse->pa_type_2g & BIT(5)) &&
+			   (efuse->pa_type_2g & BIT(4));
+	efuse->ext_lna_2g = (efuse->lna_type_2g & BIT(7)) &&
+			    (efuse->lna_type_2g & BIT(3));
+
+	efuse->ext_pa_5g = (efuse->pa_type_5g & BIT(1)) &&
+			   (efuse->pa_type_5g & BIT(0));
+	efuse->ext_lna_5g = (efuse->lna_type_5g & BIT(7)) &&
+			    (efuse->lna_type_5g & BIT(3));
+
+	/* For rtw_phy_cond2: */
+	if (efuse->ext_pa_2g) {
+		u8 ext_type_pa_2g_a = u8_get_bits(efuse->lna_type_2g, BIT(2));
+		u8 ext_type_pa_2g_b = u8_get_bits(efuse->lna_type_2g, BIT(6));
+
+		efuse->gpa_type = (ext_type_pa_2g_b << 2) | ext_type_pa_2g_a;
+	}
+
+	if (efuse->ext_pa_5g) {
+		u8 ext_type_pa_5g_a = u8_get_bits(efuse->lna_type_5g, BIT(2));
+		u8 ext_type_pa_5g_b = u8_get_bits(efuse->lna_type_5g, BIT(6));
+
+		efuse->apa_type = (ext_type_pa_5g_b << 2) | ext_type_pa_5g_a;
+	}
+
+	if (efuse->ext_lna_2g) {
+		u8 ext_type_lna_2g_a = u8_get_bits(efuse->lna_type_2g,
+						   BIT(1) | BIT(0));
+		u8 ext_type_lna_2g_b = u8_get_bits(efuse->lna_type_2g,
+						   BIT(5) | BIT(4));
+
+		efuse->glna_type = (ext_type_lna_2g_b << 2) | ext_type_lna_2g_a;
+	}
+
+	if (efuse->ext_lna_5g) {
+		u8 ext_type_lna_5g_a = u8_get_bits(efuse->lna_type_5g,
+						   BIT(1) | BIT(0));
+		u8 ext_type_lna_5g_b = u8_get_bits(efuse->lna_type_5g,
+						   BIT(5) | BIT(4));
+
+		efuse->alna_type = (ext_type_lna_5g_b << 2) | ext_type_lna_5g_a;
+	}
+}
+
+static void rtw8812a_read_rfe_type(struct rtw_dev *rtwdev,
+				   struct rtw88xxa_efuse *map)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+
+	if (map->rfe_option == 0xff) {
+		if (rtwdev->hci.type == RTW_HCI_TYPE_USB)
+			efuse->rfe_option = 0;
+		else if (rtwdev->hci.type == RTW_HCI_TYPE_PCIE)
+			efuse->rfe_option = 2;
+		else
+			efuse->rfe_option = 4;
+	} else if (map->rfe_option & BIT(7)) {
+		if (efuse->ext_lna_5g) {
+			if (efuse->ext_pa_5g) {
+				if (efuse->ext_lna_2g && efuse->ext_pa_2g)
+					efuse->rfe_option = 3;
+				else
+					efuse->rfe_option = 0;
+			} else {
+				efuse->rfe_option = 2;
+			}
+		} else {
+			efuse->rfe_option = 4;
+		}
+	} else {
+		efuse->rfe_option = map->rfe_option & 0x3f;
+
+		/* Due to other customer already use incorrect EFUSE map for
+		 * their product. We need to add workaround to prevent to
+		 * modify spec and notify all customer to revise the IC 0xca
+		 * content.
+		 */
+		if (efuse->rfe_option == 4 &&
+		    (efuse->ext_pa_5g || efuse->ext_pa_2g ||
+		     efuse->ext_lna_5g || efuse->ext_lna_2g)) {
+			if (rtwdev->hci.type == RTW_HCI_TYPE_USB)
+				efuse->rfe_option = 0;
+			else if (rtwdev->hci.type == RTW_HCI_TYPE_PCIE)
+				efuse->rfe_option = 2;
+		}
+	}
+}
+
+static void rtw88xxa_read_usb_type(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw_hal *hal = &rtwdev->hal;
+	u8 antenna = 0;
+	u8 wmode = 0;
+	u8 val8, i;
+
+	efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) |
+			   BIT(RTW_CHANNEL_WIDTH_40) |
+			   BIT(RTW_CHANNEL_WIDTH_80);
+	efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT;
+
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
+		efuse->hw_cap.nss = 1;
+	else
+		efuse->hw_cap.nss = 2;
+
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
+		goto print_hw_cap;
+
+	for (i = 0; i < 2; i++) {
+		rtw_read8_physical_efuse(rtwdev, 1019 - i, &val8);
+
+		antenna = u8_get_bits(val8, GENMASK(7, 5));
+		if (antenna)
+			break;
+		antenna = u8_get_bits(val8, GENMASK(3, 1));
+		if (antenna)
+			break;
+	}
+
+	for (i = 0; i < 2; i++) {
+		rtw_read8_physical_efuse(rtwdev, 1021 - i, &val8);
+
+		wmode = u8_get_bits(val8, GENMASK(3, 2));
+		if (wmode)
+			break;
+	}
+
+	if (antenna == 1) {
+		rtw_info(rtwdev, "This RTL8812AU says it is 1T1R.\n");
+
+		efuse->hw_cap.nss = 1;
+		hal->rf_type = RF_1T1R;
+		hal->rf_path_num = 1;
+		hal->rf_phy_num = 1;
+		hal->antenna_tx = BB_PATH_A;
+		hal->antenna_rx = BB_PATH_A;
+	} else {
+		/* Override rtw_chip_parameter_setup(). It detects 8812au as 1T1R. */
+		efuse->hw_cap.nss = 2;
+		hal->rf_type = RF_2T2R;
+		hal->rf_path_num = 2;
+		hal->rf_phy_num = 2;
+		hal->antenna_tx = BB_PATH_AB;
+		hal->antenna_rx = BB_PATH_AB;
+
+		if (antenna == 2 && wmode == 2) {
+			rtw_info(rtwdev, "This RTL8812AU says it can't do VHT.\n");
+
+			/* Can't be EFUSE_HW_CAP_IGNORE and can't be
+			 * EFUSE_HW_CAP_PTCL_VHT, so make it 1.
+			 */
+			efuse->hw_cap.ptcl = 1;
+			efuse->hw_cap.bw &= ~BIT(RTW_CHANNEL_WIDTH_80);
+		}
+	}
+
+print_hw_cap:
+	rtw_dbg(rtwdev, RTW_DBG_EFUSE,
+		"hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n",
+		efuse->hw_cap.hci, efuse->hw_cap.bw, efuse->hw_cap.ptcl,
+		efuse->hw_cap.ant_num, efuse->hw_cap.nss);
+}
+
+int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw88xxa_efuse *map;
+	int i;
+
+	if (chip->id == RTW_CHIP_TYPE_8812A)
+		rtwdev->hal.cut_version += 1;
+
+	if (rtw_dbg_is_enabled(rtwdev, RTW_DBG_EFUSE))
+		print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1,
+			       log_map, chip->log_efuse_size, true);
+
+	map = (struct rtw88xxa_efuse *)log_map;
+
+	efuse->rf_board_option = map->rf_board_option;
+	efuse->crystal_cap = map->xtal_k;
+	if (efuse->crystal_cap == 0xff)
+		efuse->crystal_cap = 0x20;
+	efuse->pa_type_2g = map->pa_type;
+	efuse->pa_type_5g = map->pa_type;
+	efuse->lna_type_2g = map->lna_type_2g;
+	efuse->lna_type_5g = map->lna_type_5g;
+	if (chip->id == RTW_CHIP_TYPE_8812A) {
+		rtw8812a_read_amplifier_type(rtwdev);
+		rtw8812a_read_rfe_type(rtwdev, map);
+
+		efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(1));
+	}
+	efuse->channel_plan = map->channel_plan;
+	efuse->country_code[0] = map->country_code[0];
+	efuse->country_code[1] = map->country_code[1];
+	efuse->bt_setting = map->rf_bt_setting;
+	efuse->regd = map->rf_board_option & 0x7;
+	efuse->thermal_meter[0] = map->thermal_meter;
+	efuse->thermal_meter[1] = map->thermal_meter;
+	efuse->thermal_meter_k = map->thermal_meter;
+	efuse->tx_bb_swing_setting_2g = map->tx_bb_swing_setting_2g;
+	efuse->tx_bb_swing_setting_5g = map->tx_bb_swing_setting_5g;
+
+	rtw88xxa_read_usb_type(rtwdev);
+
+	if (chip->id == RTW_CHIP_TYPE_8821A)
+		efuse->btcoex = rtw_read32_mask(rtwdev, REG_WL_BT_PWR_CTRL,
+						BIT_BT_FUNC_EN);
+	else
+		efuse->btcoex = (map->rf_board_option & 0xe0) == 0x20;
+	efuse->share_ant = !!(efuse->bt_setting & BIT(0));
+
+	/* No antenna diversity because it's disabled in the vendor driver */
+	efuse->ant_div_cfg = 0;
+
+	efuse->ant_div_type = map->rf_antenna_option;
+	if (efuse->ant_div_type == 0xff)
+		efuse->ant_div_type = 0x3;
+
+	for (i = 0; i < 4; i++)
+		efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i];
+
+	switch (rtw_hci_type(rtwdev)) {
+	case RTW_HCI_TYPE_USB:
+		if (chip->id == RTW_CHIP_TYPE_8821A)
+			ether_addr_copy(efuse->addr, map->rtw8821au.mac_addr);
+		else
+			ether_addr_copy(efuse->addr, map->rtw8812au.mac_addr);
+		break;
+	case RTW_HCI_TYPE_PCIE:
+	case RTW_HCI_TYPE_SDIO:
+	default:
+		/* unsupported now */
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rtw88xxa_read_efuse);
+
+static void rtw88xxa_reset_8051(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	u8 val8;
+
+	/* Reset MCU IO Wrapper */
+	rtw_write8_clr(rtwdev, REG_RSV_CTRL, BIT(1));
+	if (chip->id == RTW_CHIP_TYPE_8812A)
+		rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT(3));
+	else
+		rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT(0));
+
+	val8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN + 1);
+	rtw_write8(rtwdev, REG_SYS_FUNC_EN + 1, val8 & ~BIT(2));
+
+	/* Enable MCU IO Wrapper */
+	rtw_write8_clr(rtwdev, REG_RSV_CTRL, BIT(1));
+	if (chip->id == RTW_CHIP_TYPE_8812A)
+		rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT(3));
+	else
+		rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT(0));
+
+	rtw_write8(rtwdev, REG_SYS_FUNC_EN + 1, val8 | BIT(2));
+}
+
+/* A lightweight deinit function */
+static void rtw88xxau_hw_reset(struct rtw_dev *rtwdev)
+{
+	u8 val8;
+
+	if (!(rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_RAM_DL_SEL))
+		return;
+
+	rtw88xxa_reset_8051(rtwdev);
+	rtw_write8(rtwdev, REG_MCUFW_CTRL, 0x00);
+
+	/* before BB reset should do clock gated */
+	rtw_write32_set(rtwdev, REG_FPGA0_XCD_RF_PARA, BIT(6));
+
+	/* reset BB */
+	rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN, BIT(0) | BIT(1));
+
+	/* reset RF */
+	rtw_write8(rtwdev, REG_RF_CTRL, 0);
+
+	/* reset TRX path */
+	rtw_write16(rtwdev, REG_CR, 0);
+
+	/* reset MAC, reg0x5[1], auto FSM off */
+	rtw_write8_set(rtwdev, REG_APS_FSMCO + 1, APS_FSMCO_MAC_OFF >> 8);
+
+	/* check if reg0x5[1] auto cleared */
+	if (read_poll_timeout_atomic(rtw_read8, val8,
+				     !(val8 & (APS_FSMCO_MAC_OFF >> 8)),
+				     1, 5000, false,
+				     rtwdev, REG_APS_FSMCO + 1))
+		rtw_err(rtwdev, "%s: timed out waiting for 0x5[1]\n", __func__);
+
+	/* reg0x5[0], auto FSM on */
+	val8 |= APS_FSMCO_MAC_ENABLE >> 8;
+	rtw_write8(rtwdev, REG_APS_FSMCO + 1, val8);
+
+	rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT(4) | BIT(7));
+	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT(4) | BIT(7));
+}
+
+static int rtw88xxau_init_power_on(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	u16 val16;
+	int ret;
+
+	ret = rtw_pwr_seq_parser(rtwdev, chip->pwr_on_seq);
+	if (ret) {
+		rtw_err(rtwdev, "power on flow failed\n");
+		return ret;
+	}
+
+	rtw_write16(rtwdev, REG_CR, 0);
+	val16 = BIT_HCI_TXDMA_EN | BIT_HCI_RXDMA_EN | BIT_TXDMA_EN |
+		BIT_RXDMA_EN | BIT_PROTOCOL_EN | BIT_SCHEDULE_EN |
+		BIT_MAC_SEC_EN | BIT_32K_CAL_TMR_EN;
+	rtw_write16_set(rtwdev, REG_CR, val16);
+
+	if (chip->id == RTW_CHIP_TYPE_8821A) {
+		if (rtw_read8(rtwdev, REG_SYS_CFG1 + 3) & BIT(0))
+			rtw_write8_set(rtwdev, REG_LDO_SWR_CTRL, BIT(6));
+	}
+
+	return ret;
+}
+
+static int rtw88xxa_llt_write(struct rtw_dev *rtwdev, u32 address, u32 data)
+{
+	u32 value = BIT_LLT_WRITE_ACCESS | (address << 8) | data;
+	int count = 0;
+
+	rtw_write32(rtwdev, REG_LLT_INIT, value);
+
+	do {
+		if (!rtw_read32_mask(rtwdev, REG_LLT_INIT, BIT(31) | BIT(30)))
+			break;
+
+		if (count > 20) {
+			rtw_err(rtwdev, "Failed to poll write LLT done at %d!\n",
+				address);
+			return -EBUSY;
+		}
+	} while (++count);
+
+	return 0;
+}
+
+static int rtw88xxa_llt_init(struct rtw_dev *rtwdev, u32 boundary)
+{
+	u32 last_entry = 255;
+	int status = 0;
+	u32 i;
+
+	for (i = 0; i < boundary - 1; i++) {
+		status = rtw88xxa_llt_write(rtwdev, i, i + 1);
+		if (status)
+			return status;
+	}
+
+	status = rtw88xxa_llt_write(rtwdev, boundary - 1, 0xFF);
+	if (status)
+		return status;
+
+	for (i = boundary; i < last_entry; i++) {
+		status = rtw88xxa_llt_write(rtwdev, i, i + 1);
+		if (status)
+			return status;
+	}
+
+	status = rtw88xxa_llt_write(rtwdev, last_entry, boundary);
+
+	return status;
+}
+
+static void rtw88xxau_init_queue_reserved_page(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
+	const struct rtw_page_table *pg_tbl = NULL;
+	u16 pubq_num;
+	u32 val32;
+
+	switch (rtw_hci_type(rtwdev)) {
+	case RTW_HCI_TYPE_PCIE:
+		pg_tbl = &chip->page_table[1];
+		break;
+	case RTW_HCI_TYPE_USB:
+		if (rtwdev->hci.bulkout_num == 2)
+			pg_tbl = &chip->page_table[2];
+		else if (rtwdev->hci.bulkout_num == 3)
+			pg_tbl = &chip->page_table[3];
+		else if (rtwdev->hci.bulkout_num == 4)
+			pg_tbl = &chip->page_table[4];
+		break;
+	case RTW_HCI_TYPE_SDIO:
+		pg_tbl = &chip->page_table[0];
+		break;
+	default:
+		break;
+	}
+
+	pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num -
+		   pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num;
+
+	val32 = BIT_RQPN_NE(pg_tbl->nq_num, pg_tbl->exq_num);
+	rtw_write32(rtwdev, REG_RQPN_NPQ, val32);
+
+	val32 = BIT_RQPN_HLP(pg_tbl->hq_num, pg_tbl->lq_num, pubq_num);
+	rtw_write32(rtwdev, REG_RQPN, val32);
+}
+
+static void rtw88xxau_init_tx_buffer_boundary(struct rtw_dev *rtwdev)
+{
+	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
+
+	rtw_write8(rtwdev, REG_BCNQ_BDNY, fifo->rsvd_boundary);
+	rtw_write8(rtwdev, REG_MGQ_BDNY, fifo->rsvd_boundary);
+	rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, fifo->rsvd_boundary);
+	rtw_write8(rtwdev, REG_TRXFF_BNDY, fifo->rsvd_boundary);
+	rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, fifo->rsvd_boundary);
+}
+
+static int rtw88xxau_init_queue_priority(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	u8 bulkout_num = rtwdev->hci.bulkout_num;
+	const struct rtw_rqpn *rqpn = NULL;
+	u16 txdma_pq_map;
+
+	switch (rtw_hci_type(rtwdev)) {
+	case RTW_HCI_TYPE_PCIE:
+		rqpn = &chip->rqpn_table[1];
+		break;
+	case RTW_HCI_TYPE_USB:
+		if (bulkout_num == 2)
+			rqpn = &chip->rqpn_table[2];
+		else if (bulkout_num == 3)
+			rqpn = &chip->rqpn_table[3];
+		else if (bulkout_num == 4)
+			rqpn = &chip->rqpn_table[4];
+		else
+			return -EINVAL;
+		break;
+	case RTW_HCI_TYPE_SDIO:
+		rqpn = &chip->rqpn_table[0];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	rtwdev->fifo.rqpn = rqpn;
+
+	txdma_pq_map = rtw_read16(rtwdev, REG_TXDMA_PQ_MAP) & 0x7;
+	txdma_pq_map |= BIT_TXDMA_HIQ_MAP(rqpn->dma_map_hi);
+	txdma_pq_map |= BIT_TXDMA_MGQ_MAP(rqpn->dma_map_mg);
+	txdma_pq_map |= BIT_TXDMA_BKQ_MAP(rqpn->dma_map_bk);
+	txdma_pq_map |= BIT_TXDMA_BEQ_MAP(rqpn->dma_map_be);
+	txdma_pq_map |= BIT_TXDMA_VIQ_MAP(rqpn->dma_map_vi);
+	txdma_pq_map |= BIT_TXDMA_VOQ_MAP(rqpn->dma_map_vo);
+	rtw_write16(rtwdev, REG_TXDMA_PQ_MAP, txdma_pq_map);
+
+	/* Packet in Hi Queue Tx immediately (No constraint for ATIM Period). */
+	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && bulkout_num == 4)
+		rtw_write8(rtwdev, REG_HIQ_NO_LMT_EN, 0xff);
+
+	return 0;
+}
+
+static void rtw88xxa_init_wmac_setting(struct rtw_dev *rtwdev)
+{
+	rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff);
+	rtw_write16(rtwdev, REG_RXFLTMAP1, 0x0400);
+	rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff);
+
+	rtw_write32(rtwdev, REG_MAR, 0xffffffff);
+	rtw_write32(rtwdev, REG_MAR + 4, 0xffffffff);
+}
+
+static void rtw88xxa_init_adaptive_ctrl(struct rtw_dev *rtwdev)
+{
+	rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xffff1);
+	rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030);
+}
+
+static void rtw88xxa_init_edca(struct rtw_dev *rtwdev)
+{
+	rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a);
+
+	rtw_write16(rtwdev, REG_SIFS, 0x100a);
+	rtw_write16(rtwdev, REG_SIFS + 2, 0x100a);
+
+	rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B);
+	rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F);
+	rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324);
+	rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226);
+
+	rtw_write8(rtwdev, REG_USTIME_TSF, 0x50);
+	rtw_write8(rtwdev, REG_USTIME_EDCA, 0x50);
+}
+
+static void rtw88xxau_tx_aggregation(struct rtw_dev *rtwdev)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+
+	rtw_write32_mask(rtwdev, REG_DWBCN0_CTRL, 0xf0,
+			 chip->usb_tx_agg_desc_num);
+
+	if (chip->id == RTW_CHIP_TYPE_8821A)
+		rtw_write8(rtwdev, REG_DWBCN1_CTRL,
+			   chip->usb_tx_agg_desc_num << 1);
+}
+
+static void rtw88xxa_init_beacon_parameters(struct rtw_dev *rtwdev)
+{
+	u16 val16;
+
+	val16 = (BIT_DIS_TSF_UDT << 8) | BIT_DIS_TSF_UDT;
+	if (rtwdev->efuse.btcoex)
+		val16 |= BIT_EN_BCN_FUNCTION;
+	rtw_write16(rtwdev, REG_BCN_CTRL, val16);
+
+	rtw_write32_mask(rtwdev, REG_TBTT_PROHIBIT, 0xfffff, WLAN_TBTT_TIME);
+	rtw_write8(rtwdev, REG_DRVERLYINT, 0x05);
+	rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME);
+	rtw_write16(rtwdev, REG_BCNTCFG, 0x4413);
+}
+
+static void rtw88xxa_phy_bb_config(struct rtw_dev *rtwdev)
+{
+	u8 val8, crystal_cap;
+
+	/* power on BB/RF domain */
+	val8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
+	val8 |= BIT_FEN_USBA;
+	rtw_write8(rtwdev, REG_SYS_FUNC_EN, val8);
+
+	/* toggle BB reset */
+	val8 |= BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST;
+	rtw_write8(rtwdev, REG_SYS_FUNC_EN, val8);
+
+	rtw_write8(rtwdev, REG_RF_CTRL,
+		   BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB);
+	rtw_write8(rtwdev, REG_RF_B_CTRL,
+		   BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB);
+
+	rtw_load_table(rtwdev, rtwdev->chip->bb_tbl);
+	rtw_load_table(rtwdev, rtwdev->chip->agc_tbl);
+
+	crystal_cap = rtwdev->efuse.crystal_cap & 0x3F;
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
+		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x7FF80000,
+				 crystal_cap | (crystal_cap << 6));
+	else
+		rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x00FFF000,
+				 crystal_cap | (crystal_cap << 6));
+}
+
+static void rtw88xxa_phy_rf_config(struct rtw_dev *rtwdev)
+{
+	u8 rf_path;
+
+	for (rf_path = 0; rf_path < rtwdev->hal.rf_path_num; rf_path++)
+		rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[rf_path]);
+}
+
+static void rtw8812a_config_1t(struct rtw_dev *rtwdev)
+{
+	/* BB OFDM RX Path_A */
+	rtw_write32_mask(rtwdev, REG_RXPSEL, 0xff, 0x11);
+
+	/* BB OFDM TX Path_A */
+	rtw_write32_mask(rtwdev, REG_TXPSEL, MASKLWORD, 0x1111);
+
+	/* BB CCK R/Rx Path_A */
+	rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0c000000, 0x0);
+
+	/* MCS support */
+	rtw_write32_mask(rtwdev, REG_RX_MCS_LIMIT, 0xc0000060, 0x4);
+
+	/* RF Path_B HSSI OFF */
+	rtw_write32_mask(rtwdev, REG_3WIRE_SWB, 0xf, 0x4);
+
+	/* RF Path_B Power Down */
+	rtw_write32_mask(rtwdev, REG_LSSI_WRITE_B, MASKDWORD, 0);
+
+	/* ADDA Path_B OFF */
+	rtw_write32_mask(rtwdev, REG_AFE_PWR1_B, MASKDWORD, 0);
+	rtw_write32_mask(rtwdev, REG_AFE_PWR2_B, MASKDWORD, 0);
+}
+
+static const u32 rtw88xxa_txscale_tbl[] = {
+	0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8,
+	0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180,
+	0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab,
+	0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe
+};
+
+static u32 rtw88xxa_get_bb_swing(struct rtw_dev *rtwdev, u8 band, u8 path)
+{
+	static const u32 swing2setting[4] = {0x200, 0x16a, 0x101, 0x0b6};
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	u8 tx_bb_swing;
+
+	if (band == RTW_BAND_2G)
+		tx_bb_swing = efuse->tx_bb_swing_setting_2g;
+	else
+		tx_bb_swing = efuse->tx_bb_swing_setting_5g;
+
+	if (path == RF_PATH_B)
+		tx_bb_swing >>= 2;
+	tx_bb_swing &= 0x3;
+
+	return swing2setting[tx_bb_swing];
+}
+
+static u8 rtw88xxa_get_swing_index(struct rtw_dev *rtwdev)
+{
+	u32 swing, table_value;
+	u8 i;
+
+	swing = rtw88xxa_get_bb_swing(rtwdev, rtwdev->hal.current_band_type,
+				      RF_PATH_A);
+
+	for (i = 0; i < ARRAY_SIZE(rtw88xxa_txscale_tbl); i++) {
+		table_value = rtw88xxa_txscale_tbl[i];
+		if (swing == table_value)
+			return i;
+	}
+
+	return 24;
+}
+
+static void rtw88xxa_pwrtrack_init(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u8 path;
+
+	dm_info->default_ofdm_index = rtw88xxa_get_swing_index(rtwdev);
+
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8821A)
+		dm_info->default_cck_index = 0;
+	else
+		dm_info->default_cck_index = 24;
+
+	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+		ewma_thermal_init(&dm_info->avg_thermal[path]);
+		dm_info->delta_power_index[path] = 0;
+		dm_info->delta_power_index_last[path] = 0;
+	}
+
+	dm_info->pwr_trk_triggered = false;
+	dm_info->pwr_trk_init_trigger = true;
+	dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k;
+}
+
+void rtw88xxa_power_off(struct rtw_dev *rtwdev,
+			const struct rtw_pwr_seq_cmd *const *enter_lps_flow)
+{
+	struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
+	enum usb_device_speed speed = rtwusb->udev->speed;
+	u16 ori_fsmc0;
+	u8 reg_cr;
+
+	reg_cr = rtw_read8(rtwdev, REG_CR);
+
+	/* Already powered off */
+	if (reg_cr == 0 || reg_cr == 0xEA)
+		return;
+
+	rtw_hci_stop(rtwdev);
+
+	if (!rtwdev->efuse.btcoex)
+		rtw_write16_clr(rtwdev, REG_GPIO_MUXCFG, BIT_EN_SIC);
+
+	/* set Reg 0xf008[3:4] to 2'11 to enable U1/U2 Mode in USB3.0. */
+	if (speed == USB_SPEED_SUPER)
+		rtw_write8_set(rtwdev, REG_USB_MOD, 0x18);
+
+	rtw_write32(rtwdev, REG_HISR0, 0xffffffff);
+	rtw_write32(rtwdev, REG_HISR1, 0xffffffff);
+	rtw_write32(rtwdev, REG_HIMR0, 0);
+	rtw_write32(rtwdev, REG_HIMR1, 0);
+
+	if (rtwdev->efuse.btcoex)
+		rtw_coex_power_off_setting(rtwdev);
+
+	ori_fsmc0 = rtw_read16(rtwdev, REG_APS_FSMCO);
+	rtw_write16(rtwdev, REG_APS_FSMCO, ori_fsmc0 & ~APS_FSMCO_HW_POWERDOWN);
+
+	/* Stop Tx Report Timer. */
+	rtw_write8_clr(rtwdev, REG_TX_RPT_CTRL, BIT(1));
+
+	/* Stop Rx */
+	rtw_write8(rtwdev, REG_CR, 0);
+
+	rtw_pwr_seq_parser(rtwdev, enter_lps_flow);
+
+	if (rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_RAM_DL_SEL)
+		rtw88xxa_reset_8051(rtwdev);
+
+	rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT(2));
+	rtw_write8(rtwdev, REG_MCUFW_CTRL, 0);
+
+	rtw_pwr_seq_parser(rtwdev, rtwdev->chip->pwr_off_seq);
+
+	if (ori_fsmc0 & APS_FSMCO_HW_POWERDOWN)
+		rtw_write16_set(rtwdev, REG_APS_FSMCO, APS_FSMCO_HW_POWERDOWN);
+
+	clear_bit(RTW_FLAG_POWERON, rtwdev->flags);
+}
+EXPORT_SYMBOL(rtw88xxa_power_off);
+
+static void rtw88xxa_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 band)
+{
+	rtw_write32_mask(rtwdev, REG_TXSCALE_A, BB_SWING_MASK,
+			 rtw88xxa_get_bb_swing(rtwdev, band, RF_PATH_A));
+	rtw_write32_mask(rtwdev, REG_TXSCALE_B, BB_SWING_MASK,
+			 rtw88xxa_get_bb_swing(rtwdev, band, RF_PATH_B));
+	rtw88xxa_pwrtrack_init(rtwdev);
+}
+
+static void rtw8821a_set_ext_band_switch(struct rtw_dev *rtwdev, u8 band)
+{
+	rtw_write32_mask(rtwdev, REG_LED_CFG, BIT_DPDT_SEL_EN, 0);
+	rtw_write32_mask(rtwdev, REG_LED_CFG, BIT_DPDT_WL_SEL, 1);
+	rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0xf, 7);
+	rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0xf0, 7);
+
+	if (band == RTW_BAND_2G)
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(29) | BIT(28), 1);
+	else
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(29) | BIT(28), 2);
+}
+
+static void rtw8821a_phy_set_rfe_reg_24g(struct rtw_dev *rtwdev)
+{
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+
+	/* Turn off RF PA and LNA */
+
+	/* 0xCB0[15:12] = 0x7 (LNA_On)*/
+	rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF000, 0x7);
+	/* 0xCB0[7:4] = 0x7 (PAPE_A)*/
+	rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF0, 0x7);
+
+	if (efuse->ext_lna_2g) {
+		/* Turn on 2.4G External LNA */
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(20), 1);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(22), 0);
+		rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(2, 0), 0x2);
+		rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(10, 8), 0x2);
+	} else {
+		/* Bypass 2.4G External LNA */
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(20), 0);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(22), 0);
+		rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(2, 0), 0x7);
+		rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(10, 8), 0x7);
+	}
+}
+
+static void rtw8821a_phy_set_rfe_reg_5g(struct rtw_dev *rtwdev)
+{
+	/* Turn ON RF PA and LNA */
+
+	/* 0xCB0[15:12] = 0x7 (LNA_On)*/
+	rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF000, 0x5);
+	/* 0xCB0[7:4] = 0x7 (PAPE_A)*/
+	rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xF0, 0x4);
+
+	/* Bypass 2.4G External LNA */
+	rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(20), 0);
+	rtw_write32_mask(rtwdev, REG_RFE_INV_A, BIT(22), 0);
+	rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(2, 0), 0x7);
+	rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, GENMASK(10, 8), 0x7);
+}
+
+static void rtw8812a_phy_set_rfe_reg_24g(struct rtw_dev *rtwdev)
+{
+	switch (rtwdev->efuse.rfe_option) {
+	case 0:
+	case 2:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x000);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
+		break;
+	case 1:
+		if (rtwdev->efuse.btcoex) {
+			rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xffffff, 0x777777);
+			rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0x33f00000, 0x000);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
+		} else {
+			rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
+			rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x000);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
+		}
+		break;
+	case 3:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x54337770);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x54337770);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
+		rtw_write32_mask(rtwdev, REG_ANTSEL_SW, 0x00000303, 0x1);
+		break;
+	case 4:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x001);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x001);
+		break;
+	case 5:
+		rtw_write8(rtwdev, REG_RFE_PINMUX_A + 2, 0x77);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777);
+		rtw_write8_clr(rtwdev, REG_RFE_INV_A + 3, BIT(0));
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
+		break;
+	case 6:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x07772770);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x07772770);
+		rtw_write32(rtwdev, REG_RFE_INV_A, 0x00000077);
+		rtw_write32(rtwdev, REG_RFE_INV_B, 0x00000077);
+		break;
+	default:
+		break;
+	}
+}
+
+static void rtw8812a_phy_set_rfe_reg_5g(struct rtw_dev *rtwdev)
+{
+	switch (rtwdev->efuse.rfe_option) {
+	case 0:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77337717);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337717);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
+		break;
+	case 1:
+		if (rtwdev->efuse.btcoex) {
+			rtw_write32_mask(rtwdev, REG_RFE_PINMUX_A, 0xffffff, 0x337717);
+			rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337717);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_A, 0x33f00000, 0x000);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
+		} else {
+			rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77337717);
+			rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337717);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x000);
+			rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x000);
+		}
+		break;
+	case 2:
+	case 4:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77337777);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337777);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
+		break;
+	case 3:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x54337717);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x54337717);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_A, RFE_INV_MASK, 0x010);
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
+		rtw_write32_mask(rtwdev, REG_ANTSEL_SW, 0x00000303, 0x1);
+		break;
+	case 5:
+		rtw_write8(rtwdev, REG_RFE_PINMUX_A + 2, 0x33);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77337777);
+		rtw_write8_set(rtwdev, REG_RFE_INV_A + 3, BIT(0));
+		rtw_write32_mask(rtwdev, REG_RFE_INV_B, RFE_INV_MASK, 0x010);
+		break;
+	case 6:
+		rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x07737717);
+		rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x07737717);
+		rtw_write32(rtwdev, REG_RFE_INV_A, 0x00000077);
+		rtw_write32(rtwdev, REG_RFE_INV_B, 0x00000077);
+		break;
+	default:
+		break;
+	}
+}
+
+static void rtw88xxa_switch_band(struct rtw_dev *rtwdev, u8 new_band, u8 bw)
+{
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	u16 basic_rates, reg_41a;
+
+	/* 8811au one antenna module doesn't support antenna div, so driver must
+	 * control antenna band, otherwise one of the band will have issue
+	 */
+	if (chip->id == RTW_CHIP_TYPE_8821A && !rtwdev->efuse.btcoex &&
+	    rtwdev->efuse.ant_div_cfg == 0)
+		rtw8821a_set_ext_band_switch(rtwdev, new_band);
+
+	if (new_band == RTW_BAND_2G) {
+		rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST);
+
+		if (chip->id == RTW_CHIP_TYPE_8821A) {
+			rtw8821a_phy_set_rfe_reg_24g(rtwdev);
+
+			rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 0);
+		} else {
+			rtw_write32_mask(rtwdev, REG_BWINDICATION, 0x3, 0x1);
+			rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(17, 13), 0x17);
+
+			if (bw == RTW_CHANNEL_WIDTH_20 &&
+			    rtwdev->hal.rf_type == RF_1T1R &&
+			    !rtwdev->efuse.ext_lna_2g)
+				rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(3, 1), 0x02);
+			else
+				rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(3, 1), 0x04);
+
+			rtw_write32_mask(rtwdev, REG_CCASEL, 0x3, 0);
+
+			rtw8812a_phy_set_rfe_reg_24g(rtwdev);
+		}
+
+		rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x1);
+		rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x1);
+
+		basic_rates = BIT(DESC_RATE1M) | BIT(DESC_RATE2M) |
+			      BIT(DESC_RATE5_5M) | BIT(DESC_RATE11M) |
+			      BIT(DESC_RATE6M) | BIT(DESC_RATE12M) |
+			      BIT(DESC_RATE24M);
+		rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, basic_rates);
+
+		rtw_write8_clr(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN);
+	} else { /* RTW_BAND_5G */
+		if (chip->id == RTW_CHIP_TYPE_8821A)
+			rtw8821a_phy_set_rfe_reg_5g(rtwdev);
+
+		rtw_write8_set(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN);
+
+		read_poll_timeout_atomic(rtw_read16, reg_41a, (reg_41a & 0x30) == 0x30,
+					 50, 2500, false, rtwdev, REG_TXPKT_EMPTY);
+
+		rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST);
+
+		if (chip->id == RTW_CHIP_TYPE_8821A) {
+			rtw_write32_mask(rtwdev, REG_TXSCALE_A, 0xf00, 1);
+		} else {
+			rtw_write32_mask(rtwdev, REG_BWINDICATION, 0x3, 0x2);
+			rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(17, 13), 0x15);
+			rtw_write32_mask(rtwdev, REG_PDMFTH, GENMASK(3, 1), 0x04);
+
+			rtw_write32_mask(rtwdev, REG_CCASEL, 0x3, 1);
+
+			rtw8812a_phy_set_rfe_reg_5g(rtwdev);
+		}
+
+		rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0);
+		rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0xf);
+
+		basic_rates = BIT(DESC_RATE6M) | BIT(DESC_RATE12M) |
+			      BIT(DESC_RATE24M);
+		rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, basic_rates);
+	}
+
+	rtw88xxa_set_channel_bb_swing(rtwdev, new_band);
+}
+
+int rtw88xxa_power_on(struct rtw_dev *rtwdev)
+{
+	struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	struct rtw_hal *hal = &rtwdev->hal;
+	int ret;
+
+	if (test_bit(RTW_FLAG_POWERON, rtwdev->flags))
+		return 0;
+
+	/* Override rtw_chip_efuse_info_setup() */
+	if (chip->id == RTW_CHIP_TYPE_8821A)
+		efuse->btcoex = rtw_read32_mask(rtwdev, REG_WL_BT_PWR_CTRL,
+						BIT_BT_FUNC_EN);
+
+	/* Override rtw_chip_efuse_info_setup() */
+	if (chip->id == RTW_CHIP_TYPE_8812A)
+		rtw8812a_read_amplifier_type(rtwdev);
+
+	ret = rtw_hci_setup(rtwdev);
+	if (ret) {
+		rtw_err(rtwdev, "failed to setup hci\n");
+		goto err;
+	}
+
+	/* Revise for U2/U3 switch we can not update RF-A/B reset.
+	 * Reset after MAC power on to prevent RF R/W error.
+	 * Is it a right method?
+	 */
+	if (chip->id == RTW_CHIP_TYPE_8812A) {
+		rtw_write8(rtwdev, REG_RF_CTRL, 5);
+		rtw_write8(rtwdev, REG_RF_CTRL, 7);
+		rtw_write8(rtwdev, REG_RF_B_CTRL, 5);
+		rtw_write8(rtwdev, REG_RF_B_CTRL, 7);
+	}
+
+	/* If HW didn't go through a complete de-initial procedure,
+	 * it probably occurs some problem for double initial
+	 * procedure.
+	 */
+	rtw88xxau_hw_reset(rtwdev);
+
+	ret = rtw88xxau_init_power_on(rtwdev);
+	if (ret) {
+		rtw_err(rtwdev, "failed to power on\n");
+		goto err;
+	}
+
+	ret = rtw_set_trx_fifo_info(rtwdev);
+	if (ret) {
+		rtw_err(rtwdev, "failed to set trx fifo info\n");
+		goto err;
+	}
+
+	ret = rtw88xxa_llt_init(rtwdev, rtwdev->fifo.rsvd_boundary);
+	if (ret) {
+		rtw_err(rtwdev, "failed to init llt\n");
+		goto err;
+	}
+
+	rtw_write32_set(rtwdev, REG_TXDMA_OFFSET_CHK, BIT_DROP_DATA_EN);
+
+	ret = rtw_wait_firmware_completion(rtwdev);
+	if (ret) {
+		rtw_err(rtwdev, "failed to wait firmware completion\n");
+		goto err_off;
+	}
+
+	ret = rtw_download_firmware(rtwdev, &rtwdev->fw);
+	if (ret) {
+		rtw_err(rtwdev, "failed to download firmware\n");
+		goto err_off;
+	}
+
+	rtw_write8(rtwdev, REG_HMETFR, 0xf);
+
+	rtw_load_table(rtwdev, chip->mac_tbl);
+
+	rtw88xxau_init_queue_reserved_page(rtwdev);
+	rtw88xxau_init_tx_buffer_boundary(rtwdev);
+	rtw88xxau_init_queue_priority(rtwdev);
+
+	rtw_write16(rtwdev, REG_TRXFF_BNDY + 2,
+		    chip->rxff_size - REPORT_BUF - 1);
+
+	if (chip->id == RTW_CHIP_TYPE_8812A)
+		rtw_write8(rtwdev, REG_PBP,
+			   u8_encode_bits(PBP_512, PBP_TX_MASK) |
+			   u8_encode_bits(PBP_64, PBP_RX_MASK));
+
+	rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE);
+
+	rtw_write32(rtwdev, REG_HIMR0, 0);
+	rtw_write32(rtwdev, REG_HIMR1, 0);
+
+	rtw_write32_mask(rtwdev, REG_CR, 0x30000, 0x2);
+
+	rtw88xxa_init_wmac_setting(rtwdev);
+	rtw88xxa_init_adaptive_ctrl(rtwdev);
+	rtw88xxa_init_edca(rtwdev);
+
+	rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	rtw_write8(rtwdev, REG_ACKTO, 0x80);
+
+	rtw88xxau_tx_aggregation(rtwdev);
+
+	rtw88xxa_init_beacon_parameters(rtwdev);
+	rtw_write8(rtwdev, REG_BCN_MAX_ERR, 0xff);
+
+	rtw_hci_interface_cfg(rtwdev);
+
+	/* usb3 rx interval */
+	rtw_write8(rtwdev, REG_USB3_RXITV, 0x01);
+
+	/* burst length=4, set 0x3400 for burst length=2 */
+	rtw_write16(rtwdev, REG_RXDMA_STATUS, 0x7400);
+	rtw_write8(rtwdev, REG_RXDMA_STATUS + 1, 0xf5);
+
+	/* 0x456 = 0x70, sugguested by Zhilin */
+	if (chip->id == RTW_CHIP_TYPE_8821A)
+		rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, 0x5e);
+	else
+		rtw_write8(rtwdev, REG_AMPDU_MAX_TIME, 0x70);
+
+	rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, 0xffffffff);
+	rtw_write8(rtwdev, REG_USTIME_TSF, 0x50);
+	rtw_write8(rtwdev, REG_USTIME_EDCA, 0x50);
+
+	if (rtwusb->udev->speed == USB_SPEED_SUPER)
+		/* Disable U1/U2 Mode to avoid 2.5G spur in USB3.0. */
+		rtw_write8_clr(rtwdev, REG_USB_MOD, BIT(4) | BIT(3));
+
+	rtw_write8_set(rtwdev, REG_SINGLE_AMPDU_CTRL, BIT_EN_SINGLE_APMDU);
+
+	/* for VHT packet length 11K */
+	rtw_write8(rtwdev, REG_RX_PKT_LIMIT, 0x18);
+
+	rtw_write8(rtwdev, REG_PIFS, 0x00);
+
+	if (chip->id == RTW_CHIP_TYPE_8821A) {
+		/* 0x0a0a too small, it can't pass AC logo. change to 0x1f1f */
+		rtw_write16(rtwdev, REG_MAX_AGGR_NUM, 0x1f1f);
+		rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL, 0x80);
+		rtw_write32(rtwdev, REG_FAST_EDCA_CTRL, 0x03087777);
+	} else {
+		rtw_write16(rtwdev, REG_MAX_AGGR_NUM, 0x1f1f);
+		rtw_write8_clr(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7));
+	}
+
+	 /* to prevent mac is reseted by bus. */
+	rtw_write8_set(rtwdev, REG_RSV_CTRL, BIT(5) | BIT(6));
+
+	/* ARFB table 9 for 11ac 5G 2SS */
+	rtw_write32(rtwdev, REG_ARFR0, 0x00000010);
+	rtw_write32(rtwdev, REG_ARFRH0, 0xfffff000);
+
+	/* ARFB table 10 for 11ac 5G 1SS */
+	rtw_write32(rtwdev, REG_ARFR1_V1, 0x00000010);
+	rtw_write32(rtwdev, REG_ARFRH1_V1, 0x003ff000);
+
+	/* ARFB table 11 for 11ac 24G 1SS */
+	rtw_write32(rtwdev, REG_ARFR2_V1, 0x00000015);
+	rtw_write32(rtwdev, REG_ARFRH2_V1, 0x003ff000);
+
+	/* ARFB table 12 for 11ac 24G 2SS */
+	rtw_write32(rtwdev, REG_ARFR3_V1, 0x00000015);
+	rtw_write32(rtwdev, REG_ARFRH3_V1, 0xffcff000);
+
+	rtw_write8_set(rtwdev, REG_CR, BIT_MACTXEN | BIT_MACRXEN);
+
+	rtw88xxa_phy_bb_config(rtwdev);
+	rtw88xxa_phy_rf_config(rtwdev);
+
+	if (chip->id == RTW_CHIP_TYPE_8812A && hal->rf_path_num == 1)
+		rtw8812a_config_1t(rtwdev);
+
+	rtw88xxa_switch_band(rtwdev, RTW_BAND_2G, RTW_CHANNEL_WIDTH_20);
+
+	rtw_write32(rtwdev, RTW_SEC_CMD_REG, BIT(31) | BIT(30));
+
+	rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xff);
+	rtw_write32(rtwdev, REG_BAR_MODE_CTRL, 0x0201ffff);
+	rtw_write8(rtwdev, REG_NAV_CTRL + 2, 0);
+
+	rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(5));
+
+	rtw_phy_init(rtwdev);
+
+	rtw88xxa_pwrtrack_init(rtwdev);
+
+	/* 0x4c6[3] 1: RTS BW = Data BW
+	 * 0: RTS BW depends on CCA / secondary CCA result.
+	 */
+	rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT(3));
+
+	/* enable Tx report. */
+	rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, 0x0f);
+
+	/* Pretx_en, for WEP/TKIP SEC */
+	rtw_write8(rtwdev, REG_EARLY_MODE_CONTROL + 3, 0x01);
+
+	rtw_write16(rtwdev, REG_TX_RPT_TIME, 0x3df0);
+
+	/* Reset USB mode switch setting */
+	rtw_write8(rtwdev, REG_SYS_SDIO_CTRL, 0x0);
+	rtw_write8(rtwdev, REG_ACLK_MON, 0x0);
+
+	rtw_write8(rtwdev, REG_USB_HRPWM, 0);
+
+	/* ack for xmit mgmt frames. */
+	rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(12));
+
+	hal->cck_high_power = rtw_read32_mask(rtwdev, REG_CCK_RPT_FORMAT,
+					      BIT_CCK_RPT_FORMAT);
+
+	ret = rtw_hci_start(rtwdev);
+	if (ret) {
+		rtw_err(rtwdev, "failed to start hci\n");
+		goto err_off;
+	}
+
+	if (efuse->btcoex) {
+		rtw_coex_power_on_setting(rtwdev);
+		rtw_coex_init_hw_config(rtwdev, false);
+	}
+
+	set_bit(RTW_FLAG_POWERON, rtwdev->flags);
+
+	return 0;
+
+err_off:
+	chip->ops->power_off(rtwdev);
+
+err:
+	return ret;
+}
+EXPORT_SYMBOL(rtw88xxa_power_on);
+
+u32 rtw88xxa_phy_read_rf(struct rtw_dev *rtwdev,
+			 enum rtw_rf_path rf_path, u32 addr, u32 mask)
+{
+	static const u32 pi_addr[2] = { REG_3WIRE_SWA, REG_3WIRE_SWB };
+	static const u32 read_addr[2][2] = {
+		{ REG_SI_READ_A, REG_SI_READ_B },
+		{ REG_PI_READ_A, REG_PI_READ_B }
+	};
+	const struct rtw_chip_info *chip = rtwdev->chip;
+	const struct rtw_hal *hal = &rtwdev->hal;
+	bool set_cca, pi_mode;
+	u32 val;
+
+	if (rf_path >= hal->rf_phy_num) {
+		rtw_err(rtwdev, "unsupported rf path (%d)\n", rf_path);
+		return INV_RF_DATA;
+	}
+
+	/* CCA off to avoid reading the wrong value.
+	 * Toggling CCA would affect RF 0x0, skip it.
+	 */
+	set_cca = addr != 0x0 && chip->id == RTW_CHIP_TYPE_8812A &&
+		  hal->cut_version != RTW_CHIP_VER_CUT_C;
+
+	if (set_cca)
+		rtw_write32_set(rtwdev, REG_CCA2ND, BIT(3));
+
+	addr &= 0xff;
+
+	pi_mode = rtw_read32_mask(rtwdev, pi_addr[rf_path], 0x4);
+
+	rtw_write32_mask(rtwdev, REG_HSSI_READ, MASKBYTE0, addr);
+
+	if (chip->id == RTW_CHIP_TYPE_8821A ||
+	    hal->cut_version == RTW_CHIP_VER_CUT_C)
+		udelay(20);
+
+	val = rtw_read32_mask(rtwdev, read_addr[pi_mode][rf_path], mask);
+
+	/* CCA on */
+	if (set_cca)
+		rtw_write32_clr(rtwdev, REG_CCA2ND, BIT(3));
+
+	return val;
+}
+EXPORT_SYMBOL(rtw88xxa_phy_read_rf);
+
+static void rtw8812a_phy_fix_spur(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	/* C cut Item12 ADC FIFO CLOCK */
+	if (rtwdev->hal.cut_version == RTW_CHIP_VER_CUT_C) {
+		if (bw == RTW_CHANNEL_WIDTH_40 && channel == 11)
+			rtw_write32_mask(rtwdev, REG_ADCCLK, 0xC00, 0x3);
+		else
+			rtw_write32_mask(rtwdev, REG_ADCCLK, 0xC00, 0x2);
+
+		/* A workaround to resolve 2480Mhz spur by setting ADC clock
+		 * as 160M.
+		 */
+		if (bw == RTW_CHANNEL_WIDTH_20 && (channel == 13 || channel == 14)) {
+			rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x3);
+			rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 1);
+		} else if (bw == RTW_CHANNEL_WIDTH_40 && channel == 11) {
+			rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 1);
+		} else if (bw != RTW_CHANNEL_WIDTH_80) {
+			rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x2);
+			rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0);
+		}
+	} else {
+		/* A workaround to resolve 2480Mhz spur by setting ADC clock
+		 * as 160M.
+		 */
+		if (bw == RTW_CHANNEL_WIDTH_20 && (channel == 13 || channel == 14))
+			rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x3);
+		else if (channel <= 14) /* 2.4G only */
+			rtw_write32_mask(rtwdev, REG_ADCCLK, 0x300, 0x2);
+	}
+}
+
+static void rtw88xxa_switch_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u32 fc_area, rf_mod_ag;
+	u8 path;
+
+	switch (channel) {
+	case 36 ... 48:
+		fc_area = 0x494;
+		break;
+	case 50 ... 64:
+		fc_area = 0x453;
+		break;
+	case 100 ... 116:
+		fc_area = 0x452;
+		break;
+	default:
+		if (channel >= 118)
+			fc_area = 0x412;
+		else
+			fc_area = 0x96a;
+		break;
+	}
+
+	rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, fc_area);
+
+	for (path = 0; path < hal->rf_path_num; path++) {
+		switch (channel) {
+		case 36 ... 64:
+			rf_mod_ag = 0x101;
+			break;
+		case 100 ... 140:
+			rf_mod_ag = 0x301;
+			break;
+		default:
+			if (channel > 140)
+				rf_mod_ag = 0x501;
+			else
+				rf_mod_ag = 0x000;
+			break;
+		}
+
+		rtw_write_rf(rtwdev, path, RF_CFGCH,
+			     RF18_RFSI_MASK | RF18_BAND_MASK, rf_mod_ag);
+
+		if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
+			rtw8812a_phy_fix_spur(rtwdev, channel, bw);
+
+		rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_CHANNEL_MASK, channel);
+	}
+}
+
+static void rtw88xxa_set_reg_bw(struct rtw_dev *rtwdev, u8 bw)
+{
+	u16 val16 = rtw_read16(rtwdev, REG_WMAC_TRXPTCL_CTL);
+
+	val16 &= ~BIT_RFMOD;
+	if (bw == RTW_CHANNEL_WIDTH_80)
+		val16 |= BIT_RFMOD_80M;
+	else if (bw == RTW_CHANNEL_WIDTH_40)
+		val16 |= BIT_RFMOD_40M;
+
+	rtw_write16(rtwdev, REG_WMAC_TRXPTCL_CTL, val16);
+}
+
+static void rtw88xxa_post_set_bw_mode(struct rtw_dev *rtwdev, u8 channel,
+				      u8 bw, u8 primary_chan_idx)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u8 txsc40 = 0, txsc20, txsc;
+	u8 reg_837, l1pkval;
+
+	rtw88xxa_set_reg_bw(rtwdev, bw);
+
+	txsc20 = primary_chan_idx;
+	if (bw == RTW_CHANNEL_WIDTH_80) {
+		if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST)
+			txsc40 = RTW_SC_40_UPPER;
+		else
+			txsc40 = RTW_SC_40_LOWER;
+	}
+
+	txsc = BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40);
+	rtw_write8(rtwdev, REG_DATA_SC, txsc);
+
+	reg_837 = rtw_read8(rtwdev, REG_BWINDICATION + 3);
+
+	switch (bw) {
+	default:
+	case RTW_CHANNEL_WIDTH_20:
+		rtw_write32_mask(rtwdev, REG_ADCCLK, 0x003003C3, 0x00300200);
+		rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0);
+
+		if (hal->rf_type == RF_2T2R)
+			rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, 7);
+		else
+			rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, 8);
+
+		break;
+	case RTW_CHANNEL_WIDTH_40:
+		rtw_write32_mask(rtwdev, REG_ADCCLK, 0x003003C3, 0x00300201);
+		rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 0);
+		rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3C, txsc);
+		rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf0000000, txsc);
+
+		if (reg_837 & BIT(2)) {
+			l1pkval = 6;
+		} else {
+			if (hal->rf_type == RF_2T2R)
+				l1pkval = 7;
+			else
+				l1pkval = 8;
+		}
+
+		rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, l1pkval);
+
+		if (txsc == RTW_SC_20_UPPER)
+			rtw_write32_set(rtwdev, REG_RXSB, BIT(4));
+		else
+			rtw_write32_clr(rtwdev, REG_RXSB, BIT(4));
+
+		break;
+	case RTW_CHANNEL_WIDTH_80:
+		rtw_write32_mask(rtwdev, REG_ADCCLK, 0x003003C3, 0x00300202);
+		rtw_write32_mask(rtwdev, REG_ADC160, BIT(30), 1);
+		rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3C, txsc);
+		rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf0000000, txsc);
+
+		if (reg_837 & BIT(2)) {
+			l1pkval = 5;
+		} else {
+			if (hal->rf_type == RF_2T2R)
+				l1pkval = 6;
+			else
+				l1pkval = 7;
+		}
+
+		rtw_write32_mask(rtwdev, REG_L1PKTH, 0x03C00000, l1pkval);
+
+		break;
+	}
+}
+
+static void rtw88xxa_set_channel_rf(struct rtw_dev *rtwdev, u8 channel, u8 bw)
+{
+	u8 path;
+
+	for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) {
+		switch (bw) {
+		case RTW_CHANNEL_WIDTH_5:
+		case RTW_CHANNEL_WIDTH_10:
+		case RTW_CHANNEL_WIDTH_20:
+		default:
+			rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 3);
+			break;
+		case RTW_CHANNEL_WIDTH_40:
+			rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 1);
+			break;
+		case RTW_CHANNEL_WIDTH_80:
+			rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 0);
+			break;
+		}
+	}
+}
+
+void rtw88xxa_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw,
+			  u8 primary_chan_idx)
+{
+	u8 old_band, new_band;
+
+	if (rtw_read8(rtwdev, REG_CCK_CHECK) & BIT_CHECK_CCK_EN)
+		old_band = RTW_BAND_5G;
+	else
+		old_band = RTW_BAND_2G;
+
+	if (channel > 14)
+		new_band = RTW_BAND_5G;
+	else
+		new_band = RTW_BAND_2G;
+
+	if (new_band != old_band)
+		rtw88xxa_switch_band(rtwdev, new_band, bw);
+
+	rtw88xxa_switch_channel(rtwdev, channel, bw);
+
+	rtw88xxa_post_set_bw_mode(rtwdev, channel, bw, primary_chan_idx);
+
+	if (rtwdev->chip->id == RTW_CHIP_TYPE_8812A)
+		rtw8812a_phy_fix_spur(rtwdev, channel, bw);
+
+	rtw88xxa_set_channel_rf(rtwdev, channel, bw);
+}
+EXPORT_SYMBOL(rtw88xxa_set_channel);
+
+void rtw88xxa_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
+			       struct rtw_rx_pkt_stat *pkt_stat,
+			       s8 (*cck_rx_pwr)(u8 lna_idx, u8 vga_idx))
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_jaguar_phy_status_rpt *rpt;
+	u8 gain[RTW_RF_PATH_MAX], rssi, i;
+	s8 rx_pwr_db, power_a, power_b;
+	const s8 min_rx_power = -120;
+	u8 lna_idx, vga_idx;
+
+	rpt = (struct rtw_jaguar_phy_status_rpt *)phy_status;
+
+	if (pkt_stat->rate <= DESC_RATE11M) {
+		lna_idx = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_LNA_IDX);
+		vga_idx = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_VGA_IDX);
+
+		rx_pwr_db = cck_rx_pwr(lna_idx, vga_idx);
+
+		pkt_stat->rx_power[RF_PATH_A] = rx_pwr_db;
+		pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1);
+		dm_info->rssi[RF_PATH_A] = pkt_stat->rssi;
+		pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
+		pkt_stat->signal_power = rx_pwr_db;
+	} else { /* OFDM rate */
+		gain[RF_PATH_A] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_A);
+		gain[RF_PATH_B] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_B);
+
+		for (i = RF_PATH_A; i < rtwdev->hal.rf_path_num; i++) {
+			pkt_stat->rx_power[i] = gain[i] - 110;
+			rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[i], 1);
+			dm_info->rssi[i] = rssi;
+		}
+
+		pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power,
+							 rtwdev->hal.rf_path_num);
+
+		power_a = pkt_stat->rx_power[RF_PATH_A];
+		power_b = pkt_stat->rx_power[RF_PATH_B];
+		if (rtwdev->hal.rf_path_num == 1)
+			power_b = power_a;
+
+		pkt_stat->signal_power = max3(power_a, power_b, min_rx_power);
+	}
+}
+EXPORT_SYMBOL(rtw88xxa_query_phy_status);
+
+static void
+rtw88xxa_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path,
+				    u8 rs, u32 *phy_pwr_idx)
+{
+	static const u32 offset_txagc[2] = {
+		REG_TX_AGC_A_CCK_11_CCK_1, REG_TX_AGC_B_CCK_11_CCK_1
+	};
+	u8 rate, rate_idx, pwr_index, shift;
+	struct rtw_hal *hal = &rtwdev->hal;
+	bool write_1ss_mcs9;
+	u32 mask;
+	int j;
+
+	for (j = 0; j < rtw_rate_size[rs]; j++) {
+		rate = rtw_rate_section[rs][j];
+
+		pwr_index = hal->tx_pwr_tbl[path][rate];
+
+		shift = rate & 0x3;
+		*phy_pwr_idx |= ((u32)pwr_index << (shift * 8));
+
+		write_1ss_mcs9 = rate == DESC_RATEVHT1SS_MCS9 &&
+				 hal->rf_path_num == 1;
+
+		if (write_1ss_mcs9)
+			mask = MASKLWORD;
+		else
+			mask = MASKDWORD;
+
+		if (shift == 0x3 || write_1ss_mcs9) {
+			rate_idx = rate & 0xfc;
+			if (rate >= DESC_RATEVHT1SS_MCS0)
+				rate_idx -= 0x10;
+
+			rtw_write32_mask(rtwdev, offset_txagc[path] + rate_idx,
+					 mask, *phy_pwr_idx);
+
+			*phy_pwr_idx = 0;
+		}
+	}
+}
+
+static void rtw88xxa_tx_power_training(struct rtw_dev *rtwdev, u8 bw,
+				       u8 channel, u8 path)
+{
+	static const u32 write_offset[] = {
+		REG_TX_PWR_TRAINING_A, REG_TX_PWR_TRAINING_B,
+	};
+	u32 power_level, write_data;
+	u8 i;
+
+	power_level = rtwdev->hal.tx_pwr_tbl[path][DESC_RATEMCS7];
+	write_data = 0;
+
+	for (i = 0; i < 3; i++) {
+		if (i == 0)
+			power_level -= 10;
+		else if (i == 1)
+			power_level -= 8;
+		else
+			power_level -= 6;
+
+		write_data |= max_t(u32, power_level, 2) << (i * 8);
+	}
+
+	rtw_write32_mask(rtwdev, write_offset[path], 0xffffff, write_data);
+}
+
+void rtw88xxa_set_tx_power_index(struct rtw_dev *rtwdev)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u32 phy_pwr_idx = 0;
+	int rs, path;
+
+	for (path = 0; path < hal->rf_path_num; path++) {
+		for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) {
+			if (hal->rf_path_num == 1 &&
+			    (rs == RTW_RATE_SECTION_HT_2S ||
+			     rs == RTW_RATE_SECTION_VHT_2S))
+				continue;
+
+			if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags) &&
+			    rs > RTW_RATE_SECTION_OFDM)
+				continue;
+
+			if (hal->current_band_type == RTW_BAND_5G &&
+			    rs == RTW_RATE_SECTION_CCK)
+				continue;
+
+			rtw88xxa_set_tx_power_index_by_rate(rtwdev, path, rs,
+							    &phy_pwr_idx);
+		}
+
+		rtw88xxa_tx_power_training(rtwdev, hal->current_band_width,
+					   hal->current_channel, path);
+	}
+}
+EXPORT_SYMBOL(rtw88xxa_set_tx_power_index);
+
+void rtw88xxa_false_alarm_statistics(struct rtw_dev *rtwdev)
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u32 cck_fa_cnt, ofdm_fa_cnt;
+	u32 crc32_cnt, cca32_cnt;
+	u32 cck_enable;
+
+	cck_enable = rtw_read32(rtwdev, REG_RXPSEL) & BIT(28);
+	cck_fa_cnt = rtw_read16(rtwdev, REG_FA_CCK);
+	ofdm_fa_cnt = rtw_read16(rtwdev, REG_FA_OFDM);
+
+	dm_info->cck_fa_cnt = cck_fa_cnt;
+	dm_info->ofdm_fa_cnt = ofdm_fa_cnt;
+	dm_info->total_fa_cnt = ofdm_fa_cnt;
+	if (cck_enable)
+		dm_info->total_fa_cnt += cck_fa_cnt;
+
+	crc32_cnt = rtw_read32(rtwdev, REG_CRC_CCK);
+	dm_info->cck_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+	dm_info->cck_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+	crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM);
+	dm_info->ofdm_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+	dm_info->ofdm_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+	crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT);
+	dm_info->ht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+	dm_info->ht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+	crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT);
+	dm_info->vht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD);
+	dm_info->vht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD);
+
+	cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM);
+	dm_info->ofdm_cca_cnt = u32_get_bits(cca32_cnt, MASKHWORD);
+	dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt;
+	if (cck_enable) {
+		cca32_cnt = rtw_read32(rtwdev, REG_CCA_CCK);
+		dm_info->cck_cca_cnt = u32_get_bits(cca32_cnt, MASKLWORD);
+		dm_info->total_cca_cnt += dm_info->cck_cca_cnt;
+	}
+
+	rtw_write32_set(rtwdev, REG_FAS, BIT(17));
+	rtw_write32_clr(rtwdev, REG_FAS, BIT(17));
+	rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT, BIT(15));
+	rtw_write32_set(rtwdev, REG_CCK0_FAREPORT, BIT(15));
+	rtw_write32_set(rtwdev, REG_CNTRST, BIT(0));
+	rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0));
+}
+EXPORT_SYMBOL(rtw88xxa_false_alarm_statistics);
+
+void rtw88xxa_iqk_backup_mac_bb(struct rtw_dev *rtwdev,
+				u32 *macbb_backup,
+				const u32 *backup_macbb_reg,
+				u32 macbb_num)
+{
+	u32 i;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	/* save MACBB default value */
+	for (i = 0; i < macbb_num; i++)
+		macbb_backup[i] = rtw_read32(rtwdev, backup_macbb_reg[i]);
+}
+EXPORT_SYMBOL(rtw88xxa_iqk_backup_mac_bb);
+
+void rtw88xxa_iqk_backup_afe(struct rtw_dev *rtwdev, u32 *afe_backup,
+			     const u32 *backup_afe_reg, u32 afe_num)
+{
+	u32 i;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	/* Save AFE Parameters */
+	for (i = 0; i < afe_num; i++)
+		afe_backup[i] = rtw_read32(rtwdev, backup_afe_reg[i]);
+}
+EXPORT_SYMBOL(rtw88xxa_iqk_backup_afe);
+
+void rtw88xxa_iqk_restore_mac_bb(struct rtw_dev *rtwdev,
+				 u32 *macbb_backup,
+				 const u32 *backup_macbb_reg,
+				 u32 macbb_num)
+{
+	u32 i;
+
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	/* Reload MacBB Parameters */
+	for (i = 0; i < macbb_num; i++)
+		rtw_write32(rtwdev, backup_macbb_reg[i], macbb_backup[i]);
+}
+EXPORT_SYMBOL(rtw88xxa_iqk_restore_mac_bb);
+
+void rtw88xxa_iqk_configure_mac(struct rtw_dev *rtwdev)
+{
+	/* [31] = 0 --> Page C */
+	rtw_write32_mask(rtwdev, REG_CCASEL, BIT(31), 0x0);
+
+	rtw_write8(rtwdev, REG_TXPAUSE, 0x3f);
+	rtw_write32_mask(rtwdev, REG_BCN_CTRL,
+			 (BIT_EN_BCN_FUNCTION << 8) | BIT_EN_BCN_FUNCTION, 0x0);
+
+	/* RX ante off */
+	rtw_write8(rtwdev, REG_RXPSEL, 0x00);
+
+	/* CCA off */
+	rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf, 0xc);
+
+	/* CCK RX path off */
+	rtw_write8(rtwdev, REG_CCK_RX + 3, 0xf);
+}
+EXPORT_SYMBOL(rtw88xxa_iqk_configure_mac);
+
+bool rtw88xxa_iqk_finish(int average, int threshold,
+			 int *x_temp, int *y_temp, int *x, int *y,
+			 bool break_inner, bool break_outer)
+{
+	bool finish = false;
+	int i, ii, dx, dy;
+
+	for (i = 0; i < average; i++) {
+		for (ii = i + 1; ii < average; ii++) {
+			dx = abs_diff(x_temp[i] >> 21, x_temp[ii] >> 21);
+			dy = abs_diff(y_temp[i] >> 21, y_temp[ii] >> 21);
+
+			if (dx < threshold && dy < threshold) {
+				*x = ((x_temp[i] >> 21) + (x_temp[ii] >> 21));
+				*y = ((y_temp[i] >> 21) + (y_temp[ii] >> 21));
+
+				*x /= 2;
+				*y /= 2;
+
+				finish = true;
+
+				if (break_inner)
+					break;
+			}
+		}
+
+		if (finish && break_outer)
+			break;
+	}
+
+	return finish;
+}
+EXPORT_SYMBOL(rtw88xxa_iqk_finish);
+
+static void rtw88xxa_pwrtrack_set(struct rtw_dev *rtwdev, u8 tx_rate, u8 path)
+{
+	static const u32 reg_txscale[2] = { REG_TXSCALE_A, REG_TXSCALE_B };
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	u8 cck_swing_idx, ofdm_swing_idx;
+	u8 pwr_tracking_limit;
+
+	switch (tx_rate) {
+	case DESC_RATE1M ... DESC_RATE11M:
+		pwr_tracking_limit = 32;
+		break;
+	case DESC_RATE6M ... DESC_RATE48M:
+	case DESC_RATEMCS3 ... DESC_RATEMCS4:
+	case DESC_RATEMCS11 ... DESC_RATEMCS12:
+	case DESC_RATEVHT1SS_MCS3 ... DESC_RATEVHT1SS_MCS4:
+	case DESC_RATEVHT2SS_MCS3 ... DESC_RATEVHT2SS_MCS4:
+		pwr_tracking_limit = 30;
+		break;
+	case DESC_RATE54M:
+	case DESC_RATEMCS5 ... DESC_RATEMCS7:
+	case DESC_RATEMCS13 ... DESC_RATEMCS15:
+	case DESC_RATEVHT1SS_MCS5 ... DESC_RATEVHT1SS_MCS6:
+	case DESC_RATEVHT2SS_MCS5 ... DESC_RATEVHT2SS_MCS6:
+		pwr_tracking_limit = 28;
+		break;
+	case DESC_RATEMCS0 ... DESC_RATEMCS2:
+	case DESC_RATEMCS8 ... DESC_RATEMCS10:
+	case DESC_RATEVHT1SS_MCS0 ... DESC_RATEVHT1SS_MCS2:
+	case DESC_RATEVHT2SS_MCS0 ... DESC_RATEVHT2SS_MCS2:
+		pwr_tracking_limit = 34;
+		break;
+	case DESC_RATEVHT1SS_MCS7:
+	case DESC_RATEVHT2SS_MCS7:
+		pwr_tracking_limit = 26;
+		break;
+	default:
+	case DESC_RATEVHT1SS_MCS8:
+	case DESC_RATEVHT2SS_MCS8:
+		pwr_tracking_limit = 24;
+		break;
+	case DESC_RATEVHT1SS_MCS9:
+	case DESC_RATEVHT2SS_MCS9:
+		pwr_tracking_limit = 22;
+		break;
+	}
+
+	cck_swing_idx = dm_info->delta_power_index[path] + dm_info->default_cck_index;
+	ofdm_swing_idx = dm_info->delta_power_index[path] + dm_info->default_ofdm_index;
+
+	if (ofdm_swing_idx > pwr_tracking_limit) {
+		if (path == RF_PATH_A)
+			dm_info->txagc_remnant_cck = cck_swing_idx - pwr_tracking_limit;
+		dm_info->txagc_remnant_ofdm[path] = ofdm_swing_idx - pwr_tracking_limit;
+
+		ofdm_swing_idx = pwr_tracking_limit;
+	} else if (ofdm_swing_idx == 0) {
+		if (path == RF_PATH_A)
+			dm_info->txagc_remnant_cck = cck_swing_idx;
+		dm_info->txagc_remnant_ofdm[path] = ofdm_swing_idx;
+	} else {
+		if (path == RF_PATH_A)
+			dm_info->txagc_remnant_cck = 0;
+		dm_info->txagc_remnant_ofdm[path] = 0;
+	}
+
+	rtw_write32_mask(rtwdev, reg_txscale[path], GENMASK(31, 21),
+			 rtw88xxa_txscale_tbl[ofdm_swing_idx]);
+}
+
+void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
+			   void (*do_lck)(struct rtw_dev *rtwdev),
+			   void (*do_iqk)(struct rtw_dev *rtwdev))
+{
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+	struct rtw_hal *hal = &rtwdev->hal;
+	struct rtw_swing_table swing_table;
+	s8 remnant_pre[RTW_RF_PATH_MAX];
+	u8 thermal_value, delta, path;
+	bool need_iqk;
+
+	rtw_phy_config_swing_table(rtwdev, &swing_table);
+
+	if (rtwdev->efuse.thermal_meter[0] == 0xff) {
+		pr_err_once("efuse thermal meter is 0xff\n");
+		return;
+	}
+
+	thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00);
+
+	rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A);
+
+	need_iqk = rtw_phy_pwrtrack_need_iqk(rtwdev);
+
+	if (need_iqk && do_lck)
+		do_lck(rtwdev);
+
+	if (dm_info->pwr_trk_init_trigger)
+		dm_info->pwr_trk_init_trigger = false;
+	else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value,
+						   RF_PATH_A))
+		goto iqk;
+
+	delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A);
+
+	for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
+		remnant_pre[path] = dm_info->txagc_remnant_ofdm[path];
+
+		dm_info->delta_power_index[path] =
+			rtw_phy_pwrtrack_get_pwridx(rtwdev, &swing_table, path,
+						    RF_PATH_A, delta);
+
+		if (dm_info->delta_power_index[path] !=
+		    dm_info->delta_power_index_last[path]) {
+			dm_info->delta_power_index_last[path] =
+				dm_info->delta_power_index[path];
+
+			rtw88xxa_pwrtrack_set(rtwdev, dm_info->tx_rate, path);
+		}
+	}
+
+	for (path = RF_PATH_A; path < hal->rf_path_num; path++) {
+		if (remnant_pre[path] != dm_info->txagc_remnant_ofdm[path]) {
+			rtw_phy_set_tx_power_level(rtwdev,
+						   hal->current_channel);
+			break;
+		}
+	}
+
+iqk:
+	if (need_iqk)
+		do_iqk(rtwdev);
+}
+EXPORT_SYMBOL(rtw88xxa_phy_pwrtrack);
+
+void rtw88xxa_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl)
+{
+	static const u8 pd[CCK_PD_LV_MAX] = {0x40, 0x83, 0xcd, 0xdd, 0xed};
+	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
+
+	/* Override rtw_phy_cck_pd_lv_link(). It implements something
+	 * like type 2/3/4. We need type 1 here.
+	 */
+	if (rtw_is_assoc(rtwdev)) {
+		if (dm_info->min_rssi > 60) {
+			new_lvl = CCK_PD_LV3;
+		} else if (dm_info->min_rssi > 35) {
+			new_lvl = CCK_PD_LV2;
+		} else if (dm_info->min_rssi > 20) {
+			if (dm_info->cck_fa_avg > 500)
+				new_lvl = CCK_PD_LV2;
+			else if (dm_info->cck_fa_avg < 250)
+				new_lvl = CCK_PD_LV1;
+			else
+				return;
+		} else {
+			new_lvl = CCK_PD_LV1;
+		}
+	}
+
+	rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d)\n",
+		dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A], new_lvl);
+
+	if (dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] == new_lvl)
+		return;
+
+	dm_info->cck_fa_avg = CCK_FA_AVG_RESET;
+	dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] = new_lvl;
+
+	rtw_write8(rtwdev, REG_CCK_PD_TH, pd[new_lvl]);
+}
+EXPORT_SYMBOL(rtw88xxa_phy_cck_pd_set);
+
+MODULE_AUTHOR("Realtek Corporation");
+MODULE_DESCRIPTION("Realtek 802.11ac wireless 8821a/8811a/8812a common code");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.h b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
new file mode 100644
index 000000000000..09a45c1a4129
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.h
@@ -0,0 +1,175 @@ 
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
+/* Copyright(c) 2024  Realtek Corporation
+ */
+
+#ifndef __RTW88XXA_H__
+#define __RTW88XXA_H__
+
+#include <asm/byteorder.h>
+#include "reg.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;
+
+struct rtw88xxa_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 rtw88xxa_efuse) == 512);
+
+#define WLAN_BCN_DMA_TIME			0x02
+#define WLAN_TBTT_PROHIBIT			0x04
+#define WLAN_TBTT_HOLD_TIME			0x064
+#define WLAN_TBTT_TIME	(WLAN_TBTT_PROHIBIT |\
+			(WLAN_TBTT_HOLD_TIME << BIT_SHIFT_TBTT_HOLD_TIME_AP))
+
+struct rtw_jaguar_phy_status_rpt {
+	__le32 w0;
+	__le32 w1;
+	__le32 w2;
+	__le32 w3;
+	__le32 w4;
+	__le32 w5;
+	__le32 w6;
+} __packed;
+
+#define RTW_JGRPHY_W0_GAIN_A		GENMASK(6, 0)
+#define RTW_JGRPHY_W0_TRSW_A		BIT(7)
+#define RTW_JGRPHY_W0_GAIN_B		GENMASK(14, 8)
+#define RTW_JGRPHY_W0_TRSW_B		BIT(15)
+#define RTW_JGRPHY_W0_CHL_NUM		GENMASK(25, 16)
+#define RTW_JGRPHY_W0_SUB_CHNL		GENMASK(29, 26)
+#define RTW_JGRPHY_W0_R_RFMOD		GENMASK(31, 30)
+
+/* CCK: */
+#define RTW_JGRPHY_W1_SIG_QUAL		GENMASK(7, 0)
+#define RTW_JGRPHY_W1_AGC_RPT_VGA_IDX	GENMASK(12, 8)
+#define RTW_JGRPHY_W1_AGC_RPT_LNA_IDX	GENMASK(15, 13)
+#define RTW_JGRPHY_W1_BB_POWER		GENMASK(23, 16)
+/* OFDM: */
+#define RTW_JGRPHY_W1_PWDB_ALL		GENMASK(7, 0)
+#define RTW_JGRPHY_W1_CFO_SHORT_A	GENMASK(15, 8)	/* s8 */
+#define RTW_JGRPHY_W1_CFO_SHORT_B	GENMASK(23, 16)	/* s8 */
+#define RTW_JGRPHY_W1_BT_RF_CH_MSB	GENMASK(31, 30)
+
+#define RTW_JGRPHY_W2_ANT_DIV_SW_A	BIT(0)
+#define RTW_JGRPHY_W2_ANT_DIV_SW_B	BIT(1)
+#define RTW_JGRPHY_W2_BT_RF_CH_LSB	GENMASK(7, 2)
+#define RTW_JGRPHY_W2_CFO_TAIL_A	GENMASK(15, 8)	/* s8 */
+#define RTW_JGRPHY_W2_CFO_TAIL_B	GENMASK(23, 16)	/* s8 */
+#define RTW_JGRPHY_W2_PCTS_MSK_RPT_0	GENMASK(31, 24)
+
+#define RTW_JGRPHY_W3_PCTS_MSK_RPT_1	GENMASK(7, 0)
+/* Stream 1 and 2 RX EVM: */
+#define RTW_JGRPHY_W3_RXEVM_1		GENMASK(15, 8)	/* s8 */
+#define RTW_JGRPHY_W3_RXEVM_2		GENMASK(23, 16)	/* s8 */
+#define RTW_JGRPHY_W3_RXSNR_A		GENMASK(31, 24)	/* s8 */
+
+#define RTW_JGRPHY_W4_RXSNR_B		GENMASK(7, 0)	/* s8 */
+#define RTW_JGRPHY_W4_PCTS_MSK_RPT_2	GENMASK(21, 8)
+#define RTW_JGRPHY_W4_PCTS_RPT_VALID	BIT(22)
+#define RTW_JGRPHY_W4_RXEVM_3		GENMASK(31, 24)	/* s8 */
+
+#define RTW_JGRPHY_W5_RXEVM_4		GENMASK(7, 0)	/* s8 */
+/* 8812a, stream 1 and 2 CSI: */
+#define RTW_JGRPHY_W5_CSI_CURRENT_1	GENMASK(15, 8)
+#define RTW_JGRPHY_W5_CSI_CURRENT_2	GENMASK(23, 16)
+/* 8814a: */
+#define RTW_JGRPHY_W5_RXSNR_C		GENMASK(15, 8)	/* s8 */
+#define RTW_JGRPHY_W5_RXSNR_D		GENMASK(23, 16)	/* s8 */
+#define RTW_JGRPHY_W5_GAIN_C		GENMASK(30, 24)
+#define RTW_JGRPHY_W5_TRSW_C		BIT(31)
+
+#define RTW_JGRPHY_W6_GAIN_D		GENMASK(6, 0)
+#define RTW_JGRPHY_W6_TRSW_D		BIT(7)
+#define RTW_JGRPHY_W6_SIGEVM		GENMASK(15, 8)	/* s8 */
+#define RTW_JGRPHY_W6_ANTIDX_ANTC	GENMASK(18, 16)
+#define RTW_JGRPHY_W6_ANTIDX_ANTD	GENMASK(21, 19)
+#define RTW_JGRPHY_W6_DPDT_CTRL_KEEP	BIT(22)
+#define RTW_JGRPHY_W6_GNT_BT_KEEP	BIT(23)
+#define RTW_JGRPHY_W6_ANTIDX_ANTA	GENMASK(26, 24)
+#define RTW_JGRPHY_W6_ANTIDX_ANTB	GENMASK(29, 27)
+#define RTW_JGRPHY_W6_HW_ANTSW_OCCUR	GENMASK(31, 30)
+
+#define RF18_BW_MASK		(BIT(11) | BIT(10))
+
+void rtw88xxa_efuse_grant(struct rtw_dev *rtwdev, bool on);
+int rtw88xxa_read_efuse(struct rtw_dev *rtwdev, u8 *log_map);
+void rtw88xxa_power_off(struct rtw_dev *rtwdev,
+			const struct rtw_pwr_seq_cmd *const *enter_lps_flow);
+int rtw88xxa_power_on(struct rtw_dev *rtwdev);
+u32 rtw88xxa_phy_read_rf(struct rtw_dev *rtwdev,
+			 enum rtw_rf_path rf_path, u32 addr, u32 mask);
+void rtw88xxa_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw,
+			  u8 primary_chan_idx);
+void rtw88xxa_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status,
+			       struct rtw_rx_pkt_stat *pkt_stat,
+			       s8 (*cck_rx_pwr)(u8 lna_idx, u8 vga_idx));
+void rtw88xxa_set_tx_power_index(struct rtw_dev *rtwdev);
+void rtw88xxa_false_alarm_statistics(struct rtw_dev *rtwdev);
+void rtw88xxa_iqk_backup_mac_bb(struct rtw_dev *rtwdev,
+				u32 *macbb_backup,
+				const u32 *backup_macbb_reg,
+				u32 macbb_num);
+void rtw88xxa_iqk_backup_afe(struct rtw_dev *rtwdev, u32 *afe_backup,
+			     const u32 *backup_afe_reg, u32 afe_num);
+void rtw88xxa_iqk_restore_mac_bb(struct rtw_dev *rtwdev,
+				 u32 *macbb_backup,
+				 const u32 *backup_macbb_reg,
+				 u32 macbb_num);
+void rtw88xxa_iqk_configure_mac(struct rtw_dev *rtwdev);
+bool rtw88xxa_iqk_finish(int average, int threshold,
+			 int *x_temp, int *y_temp, int *x, int *y,
+			 bool break_inner, bool break_outer);
+void rtw88xxa_phy_pwrtrack(struct rtw_dev *rtwdev,
+			   void (*do_lck)(struct rtw_dev *rtwdev),
+			   void (*do_iqk)(struct rtw_dev *rtwdev));
+void rtw88xxa_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl);
+
+#endif