diff mbox series

[RFC,v2,06/12] rtw88: fw and efuse files

Message ID 1538553748-26364-7-git-send-email-yhchuang@realtek.com (mailing list archive)
State RFC
Delegated to: Kalle Valo
Headers show
Series rtw88: mac80211 driver for Realtek 802.11ac wireless network chips | expand

Commit Message

Tony Chuang Oct. 3, 2018, 8:02 a.m. UTC
From: Yan-Hsuan Chuang <yhchuang@realtek.com>

fw and efuse files for Realtek 802.11ac wireless network chips

Signed-off-by: Yan-Hsuan Chuang <yhchuang@realtek.com>
---
 drivers/net/wireless/realtek/rtw88/efuse.c | 150 +++++++
 drivers/net/wireless/realtek/rtw88/efuse.h |  53 +++
 drivers/net/wireless/realtek/rtw88/fw.c    | 638 +++++++++++++++++++++++++++++
 drivers/net/wireless/realtek/rtw88/fw.h    | 182 ++++++++
 4 files changed, 1023 insertions(+)
 create mode 100644 drivers/net/wireless/realtek/rtw88/efuse.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/efuse.h
 create mode 100644 drivers/net/wireless/realtek/rtw88/fw.c
 create mode 100644 drivers/net/wireless/realtek/rtw88/fw.h

Comments

Stanislaw Gruszka Oct. 4, 2018, 10:49 a.m. UTC | #1
Hi

On Wed, Oct 03, 2018 at 04:02:22PM +0800, yhchuang@realtek.com wrote:
> +void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para)
> +{
> +	u8 h2c_pkt[H2C_PKT_SIZE] = {0};

Not sure if '= {0}' work as expected for arrays, you
want to nulify first byte or whole h2c_pkt ?

> +void rtw_add_rsvd_page(struct rtw_dev *rtwdev, enum rtw_rsvd_packet_type type,
> +		       bool txdesc)
> +{
> +	struct rtw_rsvd_page *rsvd_pkt;
> +
> +	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) {
> +		if (rsvd_pkt->type == type)
> +			return;
> +	}
> +
> +	rsvd_pkt = kmalloc(sizeof(*rsvd_pkt),
> +			   in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);

It's never called from interrupt. You have to check kmalloc output value.

> +	rsvd_pkt->type = type;
> +	rsvd_pkt->add_txdesc = txdesc;
> +	list_add_tail(&rsvd_pkt->list, &rtwdev->rsvd_page_list);
<snip>
> +
> +void rtw_reset_rsvd_page(struct rtw_dev *rtwdev)
> +{
> +	struct rtw_rsvd_page *rsvd_pkt, *tmp;
> +
> +	list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list, list) {
> +		if (rsvd_pkt->type == RSVD_BEACON)
> +			continue;
> +		list_del(&rsvd_pkt->list);
> +		kfree(rsvd_pkt);
> +	}

It also not clear how the list is protected agains concurrent access.
However seems like much simpler solution would be use array instead of
the list. For example above code could be replaced by:

for (i = RSVD_BEACON + 1; i <= RSVD_QOS_NULL ; i++) {
	kfree(rwdev->rsvd_page_arr[i]));
	rsvd_page_arr[i] = NULL;
}

And other operations like adding / removing / checking if present could
be simplified as well.

Also RSVD_PROBE_RESP page is never created.

> +	*size = (total_page - 1) * page_size + page_margin;
> +	buf = kzalloc(*size, GFP_KERNEL | GFP_ATOMIC);

Those GPF flags contradict to each other :-)
and buf not checked agains NULL.

Thanks
Stanislaw
Tony Chuang Oct. 5, 2018, 9:19 a.m. UTC | #2
> -----Original Message-----
> From: Stanislaw Gruszka [mailto:sgruszka@redhat.com]
> Sent: Thursday, October 04, 2018 6:50 PM
> To: Tony Chuang
> Cc: kvalo@codeaurora.org; Larry.Finger@lwfinger.net; Pkshih; Andy Huang;
> linux-wireless@vger.kernel.org
> Subject: Re: [RFC v2 06/12] rtw88: fw and efuse files
> 
> Hi
> 
> On Wed, Oct 03, 2018 at 04:02:22PM +0800, yhchuang@realtek.com wrote:
> > +void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para)
> > +{
> > +	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
> 
> Not sure if '= {0}' work as expected for arrays, you
> want to nulify first byte or whole h2c_pkt ?

I want to nullify the whole h2c_pkt.
And I think it works as expected.

> 
> > +void rtw_add_rsvd_page(struct rtw_dev *rtwdev, enum
> rtw_rsvd_packet_type type,
> > +		       bool txdesc)
> > +{
> > +	struct rtw_rsvd_page *rsvd_pkt;
> > +
> > +	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) {
> > +		if (rsvd_pkt->type == type)
> > +			return;
> > +	}
> > +
> > +	rsvd_pkt = kmalloc(sizeof(*rsvd_pkt),
> > +			   in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
> 
> It's never called from interrupt. You have to check kmalloc output value.
> 

OK, it's better.

> > +	rsvd_pkt->type = type;
> > +	rsvd_pkt->add_txdesc = txdesc;
> > +	list_add_tail(&rsvd_pkt->list, &rtwdev->rsvd_page_list);
> <snip>
> > +
> > +void rtw_reset_rsvd_page(struct rtw_dev *rtwdev)
> > +{
> > +	struct rtw_rsvd_page *rsvd_pkt, *tmp;
> > +
> > +	list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list, list) {
> > +		if (rsvd_pkt->type == RSVD_BEACON)
> > +			continue;
> > +		list_del(&rsvd_pkt->list);
> > +		kfree(rsvd_pkt);
> > +	}
> 
> It also not clear how the list is protected agains concurrent access.
> However seems like much simpler solution would be use array instead of
> the list. For example above code could be replaced by:


It should be protected bu rtwdev->mutex, but indeed it is not clear
Would have to add lockdep for it.


> 
> for (i = RSVD_BEACON + 1; i <= RSVD_QOS_NULL ; i++) {
> 	kfree(rwdev->rsvd_page_arr[i]));
> 	rsvd_page_arr[i] = NULL;
> }
> 
> And other operations like adding / removing / checking if present could
> be simplified as well.
> 
> Also RSVD_PROBE_RESP page is never created.

RSVD_PROBE_RESP will be used for AP/GO mode,
so I think I might leave it here.

And change rsvd_list to array is not a good idea if there is more than
one interfaces running. For this case will need to download more rsvd
packets, and the order can be changed. Array seems not enough to
handle that.

> 
> > +	*size = (total_page - 1) * page_size + page_margin;
> > +	buf = kzalloc(*size, GFP_KERNEL | GFP_ATOMIC);
> 
> Those GPF flags contradict to each other :-)
> and buf not checked agains NULL.

fixed

> 
> Thanks
> Stanislaw
> 

Thanks
Yan-Hsuan Chuang
Kalle Valo Oct. 6, 2018, 12:34 p.m. UTC | #3
Stanislaw Gruszka <sgruszka@redhat.com> writes:

> On Wed, Oct 03, 2018 at 04:02:22PM +0800, yhchuang@realtek.com wrote:
>
>> +void rtw_add_rsvd_page(struct rtw_dev *rtwdev, enum rtw_rsvd_packet_type type,
>> +		       bool txdesc)
>> +{
>> +	struct rtw_rsvd_page *rsvd_pkt;
>> +
>> +	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) {
>> +		if (rsvd_pkt->type == type)
>> +			return;
>> +	}
>> +
>> +	rsvd_pkt = kmalloc(sizeof(*rsvd_pkt),
>> +			   in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
>
> It's never called from interrupt. You have to check kmalloc output value.

My recommendation is to always avoid in_interrupt().
Stanislaw Gruszka Oct. 8, 2018, 9:15 a.m. UTC | #4
On Fri, Oct 05, 2018 at 09:19:31AM +0000, Tony Chuang wrote:
> > On Wed, Oct 03, 2018 at 04:02:22PM +0800, yhchuang@realtek.com wrote:
> > > +void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para)
> > > +{
> > > +	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
> > 
> > Not sure if '= {0}' work as expected for arrays, you
> > want to nulify first byte or whole h2c_pkt ?
> 
> I want to nullify the whole h2c_pkt.
> And I think it works as expected.

Yes indeed. I confused with struct initialization which do
not nullify whole struct when ={0} is used.

> > for (i = RSVD_BEACON + 1; i <= RSVD_QOS_NULL ; i++) {
> > 	kfree(rwdev->rsvd_page_arr[i]));
> > 	rsvd_page_arr[i] = NULL;
> > }
> > 
> > And other operations like adding / removing / checking if present could
> > be simplified as well.
> > 
> > Also RSVD_PROBE_RESP page is never created.
> 
> RSVD_PROBE_RESP will be used for AP/GO mode,
> so I think I might leave it here.
> 
> And change rsvd_list to array is not a good idea if there is more than
> one interfaces running. For this case will need to download more rsvd
> packets, and the order can be changed. Array seems not enough to
> handle that.

I guess this will be changed in the future, because for now order 
is always the same:

	rtw_add_rsvd_page(rtwdev, RSVD_PS_POLL, true);
	rtw_add_rsvd_page(rtwdev, RSVD_QOS_NULL, true);
	rtw_add_rsvd_page(rtwdev, RSVD_NULL, true);

and we can not add more of the same pages because of check 
                
	if (rsvd_pkt->type == type)
		return;

in rtw_add_rsvd_page(). In general this interface does not seem
to be workable with multi vif. But I assume this will be fixed
in the future, so list seems to be fine.

Thanks
Stanislaw
diff mbox series

Patch

diff --git a/drivers/net/wireless/realtek/rtw88/efuse.c b/drivers/net/wireless/realtek/rtw88/efuse.c
new file mode 100644
index 0000000..7c1b782
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/efuse.c
@@ -0,0 +1,150 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2018  Realtek Corporation.
+ */
+
+#include "main.h"
+#include "efuse.h"
+#include "reg.h"
+#include "debug.h"
+
+#define RTW_EFUSE_BANK_WIFI		0x0
+
+static void switch_efuse_bank(struct rtw_dev *rtwdev)
+{
+	rtw_write32_mask(rtwdev, REG_LDO_EFUSE_CTRL, BIT_MASK_EFUSE_BANK_SEL,
+			 RTW_EFUSE_BANK_WIFI);
+}
+
+static int rtw_dump_logical_efuse_map(struct rtw_dev *rtwdev, u8 *phy_map,
+				      u8 *log_map)
+{
+	u32 physical_size = rtwdev->efuse.physical_size;
+	u32 protect_size = rtwdev->efuse.protect_size;
+	u32 logical_size = rtwdev->efuse.logical_size;
+	u32 phy_idx, log_idx;
+	u8 hdr1, hdr2;
+	u8 blk_idx;
+	u8 valid;
+	u8 word_en;
+	int i;
+
+	phy_idx = 0;
+
+	do {
+		hdr1 = *(phy_map + phy_idx);
+		if ((hdr1 & 0x1f) == 0xf) {
+			phy_idx++;
+			hdr2 = *(phy_map + phy_idx);
+			if (hdr2 == 0xff)
+				break;
+			blk_idx = ((hdr2 & 0xf0) >> 1) | ((hdr1 >> 5) & 0x07);
+			word_en = hdr2 & 0x0f;
+		} else {
+			blk_idx = (hdr1 & 0xf0) >> 4;
+			word_en = hdr1 & 0x0f;
+		}
+
+		if (hdr1 == 0xff)
+			break;
+
+		phy_idx++;
+		for (i = 0; i < 4; i++) {
+			valid = (~(word_en >> i)) & 0x1;
+			if (valid != 0x1)
+				continue;
+			log_idx = (blk_idx << 3) + (i << 1);
+			*(log_map + log_idx) = *(phy_map + phy_idx);
+			log_idx++;
+			phy_idx++;
+			*(log_map + log_idx) = *(phy_map + phy_idx);
+			phy_idx++;
+			if (phy_idx > physical_size - protect_size ||
+			    log_idx > logical_size)
+				return -EINVAL;
+		}
+	} while (1);
+
+	return 0;
+}
+
+static int rtw_dump_physical_efuse_map(struct rtw_dev *rtwdev, u8 *map)
+{
+	struct rtw_chip_info *chip = rtwdev->chip;
+	u32 size = rtwdev->efuse.physical_size;
+	u32 efuse_ctl;
+	u32 addr;
+	u32 cnt;
+
+	switch_efuse_bank(rtwdev);
+
+	/* disable 2.5V LDO */
+	chip->ops->cfg_ldo25(rtwdev, false);
+
+	efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL);
+
+	for (addr = 0; addr < size; addr++) {
+		efuse_ctl &= ~(BIT_MASK_EF_DATA | BITS_EF_ADDR);
+		efuse_ctl |= (addr & BIT_MASK_EF_ADDR) << BIT_SHIFT_EF_ADDR;
+		rtw_write32(rtwdev, REG_EFUSE_CTRL, efuse_ctl & (~BIT_EF_FLAG));
+
+		cnt = 1000000;
+		do {
+			udelay(1);
+			efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL);
+			if (--cnt == 0)
+				return -EBUSY;
+		} while (!(efuse_ctl & BIT_EF_FLAG));
+
+		*(map + addr) = (u8)(efuse_ctl & BIT_MASK_EF_DATA);
+	}
+
+	return 0;
+}
+
+int rtw_parse_efuse_map(struct rtw_dev *rtwdev)
+{
+	struct rtw_chip_info *chip = rtwdev->chip;
+	struct rtw_efuse *efuse = &rtwdev->efuse;
+	u32 phy_size = efuse->physical_size;
+	u32 log_size = efuse->logical_size;
+	u8 *phy_map = NULL;
+	u8 *log_map = NULL;
+	int ret = 0;
+
+	phy_map = kmalloc(phy_size, GFP_KERNEL);
+	log_map = kmalloc(log_size, GFP_KERNEL);
+	if (!phy_map || !log_map) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	ret = rtw_dump_physical_efuse_map(rtwdev, phy_map);
+	if (ret) {
+		rtw_err(rtwdev, "failed to dump efuse physical map\n");
+		goto out_free;
+	}
+
+	memset(log_map, 0xff, log_size);
+	ret = rtw_dump_logical_efuse_map(rtwdev, phy_map, log_map);
+	if (ret) {
+		rtw_err(rtwdev, "failed to dump efuse logical map\n");
+		goto out_free;
+	}
+
+	print_hex_dump_bytes("efuse: ", DUMP_PREFIX_OFFSET, log_map, log_size);
+
+	efuse->x3d7 = phy_map[0x3d7];
+	efuse->x3d8 = phy_map[0x3d8];
+
+	ret = chip->ops->read_efuse(rtwdev, log_map);
+	if (ret) {
+		rtw_err(rtwdev, "failed to read efuse map\n");
+		goto out_free;
+	}
+
+out_free:
+	kfree(log_map);
+	kfree(phy_map);
+
+	return ret;
+}
diff --git a/drivers/net/wireless/realtek/rtw88/efuse.h b/drivers/net/wireless/realtek/rtw88/efuse.h
new file mode 100644
index 0000000..3635d08
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/efuse.h
@@ -0,0 +1,53 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2018  Realtek Corporation.
+ */
+
+#ifndef __RTW_EFUSE_H__
+#define __RTW_EFUSE_H__
+
+#define EFUSE_HW_CAP_IGNORE		0
+#define EFUSE_HW_CAP_PTCL_VHT		3
+#define EFUSE_HW_CAP_SUPP_BW80		7
+#define EFUSE_HW_CAP_SUPP_BW40		6
+
+struct efuse_hw_cap {
+	u8 rsvd_0;
+	u8 rsvd_1;
+	u8 rsvd_2;
+	u8 rsvd_3;
+#ifdef __LITTLE_ENDIAN
+	u8 hci:4;
+	u8 rsvd_4:4;
+#else
+	u8 rsvd_4:4;
+	u8 hci:4;
+#endif
+	u8 rsvd_5;
+#ifdef __LITTLE_ENDIAN
+	u8 bw:3;
+	u8 nss:2;
+	u8 ant_num:3;
+#else
+	u8 ant_num:3;
+	u8 nss:2;
+	u8 bw:3;
+#endif
+#ifdef __LITTLE_ENDIAN
+	u8 rsvd_7_1:2;
+	u8 ptcl:2;
+	u8 rsvd_7_2:4;
+#else
+	u8 rsvd_7_2:4;
+	u8 ptcl:2;
+	u8 rsvd_7_1:2;
+#endif
+	u8 rsvd_8;
+	u8 rsvd_9;
+	u8 rsvd_10;
+	u8 rsvd_11;
+	u8 rsvd_12;
+};
+
+int rtw_parse_efuse_map(struct rtw_dev *rtwdev);
+
+#endif
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
new file mode 100644
index 0000000..d22ca08
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -0,0 +1,638 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2018  Realtek Corporation.
+ */
+
+#include "main.h"
+#include "fw.h"
+#include "tx.h"
+#include "reg.h"
+#include "debug.h"
+
+void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb)
+{
+	struct rtw_c2h_cmd *c2h;
+	u32 pkt_offset;
+	u8 len;
+
+	pkt_offset = *((u32 *)skb->cb);
+	c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset);
+	len = skb->len - pkt_offset - 2;
+
+	rtw_dbg(rtwdev, "recv C2H, id=0x%02x, seq=0x%02x, len=%d\n",
+		c2h->id, c2h->seq, len);
+
+	switch (c2h->id) {
+	case C2H_HALMAC:
+		/* halmac needs rx_desc + c2h payload */
+		break;
+	default:
+		break;
+	}
+}
+
+void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, u8 cmd, u8 *h2c)
+{
+	u8 box;
+	u8 box_state;
+	u32 box_reg, box_ex_reg;
+	u32 h2c_wait;
+	int idx;
+
+	rtw_dbg(rtwdev,
+		"send H2C 0x%02x, content %02x%02x%02x%02x %02x%02x%02x%02x\n",
+		cmd, h2c[3], h2c[2], h2c[1], h2c[0],
+		h2c[7], h2c[6], h2c[5], h2c[4]);
+
+	spin_lock(&rtwdev->h2c.lock);
+
+	box = rtwdev->h2c.last_box_num;
+	switch (box) {
+	case 0:
+		box_reg = REG_HMEBOX0;
+		box_ex_reg = REG_HMEBOX0_EX;
+		break;
+	case 1:
+		box_reg = REG_HMEBOX1;
+		box_ex_reg = REG_HMEBOX1_EX;
+		break;
+	case 2:
+		box_reg = REG_HMEBOX2;
+		box_ex_reg = REG_HMEBOX2_EX;
+		break;
+	case 3:
+		box_reg = REG_HMEBOX3;
+		box_ex_reg = REG_HMEBOX3_EX;
+		break;
+	default:
+		WARN(1, "invalid h2c mail box number\n");
+		goto out;
+	}
+
+	h2c_wait = 20;
+	do {
+		box_state = rtw_read8(rtwdev, REG_HMETFR);
+	} while ((box_state >> box) & 0x1 && --h2c_wait > 0);
+
+	if (!h2c_wait) {
+		rtw_err(rtwdev, "fail sending h2c");
+		goto out;
+	}
+
+	for (idx = 0; idx < 4; idx++)
+		rtw_write8(rtwdev, box_reg + idx, h2c[idx]);
+	for (idx = 0; idx < 4; idx++)
+		rtw_write8(rtwdev, box_ex_reg + idx, h2c[idx + 4]);
+
+	if (++rtwdev->h2c.last_box_num >= 4)
+		rtwdev->h2c.last_box_num = 0;
+
+out:
+	spin_unlock(&rtwdev->h2c.lock);
+}
+
+static void rtw_fw_send_h2c_packet(struct rtw_dev *rtwdev, u8 *h2c)
+{
+	int ret;
+
+	spin_lock(&rtwdev->h2c.lock);
+
+	FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c, rtwdev->h2c.seq);
+	ret = rtw_hci_write_data_h2c(rtwdev, h2c, H2C_PKT_SIZE);
+	if (ret)
+		rtw_err(rtwdev, "failed to send h2c packet\n");
+	rtwdev->h2c.seq++;
+
+	spin_unlock(&rtwdev->h2c.lock);
+}
+
+void
+rtw_fw_send_general_info(struct rtw_dev *rtwdev, struct rtw_general_info *info)
+{
+	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+	u16 total_size = H2C_PKT_HDR_SIZE + 4;
+	u16 sub_cmd_id = SUB_CMD_ID_GENERAL_INFO;
+
+	GENERAL_INFO_SET_FW_TX_BOUNDARY(h2c_pkt,
+					fifo->rsvd_fw_txbuf_addr -
+					fifo->rsvd_boundary);
+	FW_OFFLOAD_H2C_SET_TOTAL_LEN(h2c_pkt, total_size);
+	FW_OFFLOAD_H2C_SET_SUB_CMD_ID(h2c_pkt, sub_cmd_id);
+
+	FW_OFFLOAD_H2C_SET_CATEGORY(h2c_pkt, 0x01);
+	FW_OFFLOAD_H2C_SET_CMD_ID(h2c_pkt, 0xFF);
+
+	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
+}
+
+void
+rtw_fw_send_phydm_info(struct rtw_dev *rtwdev, struct rtw_general_info *info)
+{
+	struct rtw_hal *hal = &rtwdev->hal;
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+	u16 total_size = H2C_PKT_HDR_SIZE + 8;
+	u16 sub_cmd_id = SUB_CMD_ID_PHYDM_INFO;
+
+	PHYDM_INFO_SET_REF_TYPE(h2c_pkt, info->rfe_type);
+	PHYDM_INFO_SET_RF_TYPE(h2c_pkt, info->rf_type);
+	PHYDM_INFO_SET_CUT_VER(h2c_pkt, hal->chip_version);
+	PHYDM_INFO_SET_RX_ANT_STATUS(h2c_pkt, info->rx_ant_status);
+	PHYDM_INFO_SET_TX_ANT_STATUS(h2c_pkt, info->tx_ant_status);
+
+	FW_OFFLOAD_H2C_SET_TOTAL_LEN(h2c_pkt, total_size);
+	FW_OFFLOAD_H2C_SET_SUB_CMD_ID(h2c_pkt, sub_cmd_id);
+
+	FW_OFFLOAD_H2C_SET_CATEGORY(h2c_pkt, 0x01);
+	FW_OFFLOAD_H2C_SET_CMD_ID(h2c_pkt, 0xFF);
+
+	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
+}
+
+void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para)
+{
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+	u16 total_size = H2C_PKT_HDR_SIZE + 1;
+	u16 sub_cmd_id = SUB_CMD_ID_IQK;
+
+	IQK_SET_CLEAR(h2c_pkt, para->clear);
+	IQK_SET_SEGMENT_IQK(h2c_pkt, para->segment_iqk);
+
+	FW_OFFLOAD_H2C_SET_TOTAL_LEN(h2c_pkt, total_size);
+	FW_OFFLOAD_H2C_SET_SUB_CMD_ID(h2c_pkt, sub_cmd_id);
+
+	FW_OFFLOAD_H2C_SET_CATEGORY(h2c_pkt, 0x01);
+	FW_OFFLOAD_H2C_SET_CMD_ID(h2c_pkt, 0xFF);
+
+	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
+}
+
+void rtw_fw_fill_h2c_cmd(struct rtw_dev *rtwdev, u8 cmd, u8 len, u8 *buf)
+{
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+
+	if (WARN(len > H2C_PKT_SIZE, "invalid h2c command length"))
+		return;
+
+	/* The buffer does not contain ID of the H2C, appends it */
+	memcpy(h2c_pkt, &cmd, 1);
+	memcpy(h2c_pkt + 1, buf, len);
+
+	rtw_fw_send_h2c_command(rtwdev, cmd, h2c_pkt);
+}
+
+void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si)
+{
+	u8 cmd_id = CMD_ID_RSSI_MONITOR;
+	u8 cmd_class = CLASS_RSSI_MONITOR;
+	u8 cmd = (cmd_class << RTW_H2C_CLASS_OFFSET) | cmd_id;
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+	u8 rssi = ewma_rssi_read(&si->avg_rssi);
+	bool stbc_en = si->stbc_en ? true : false;
+
+	h2c_pkt[0] = cmd;
+	h2c_pkt[1] = si->mac_id;
+	h2c_pkt[3] = rssi;
+	h2c_pkt[4] = stbc_en << 1;
+
+	rtw_fw_send_h2c_command(rtwdev, cmd, h2c_pkt);
+}
+
+void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si)
+{
+	u8 cmd_id = CMD_ID_RA_INFO;
+	u8 cmd_class = CLASS_RA_INFO;
+	u8 cmd = (cmd_class << RTW_H2C_CLASS_OFFSET) | cmd_id;
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+	bool no_update = si->updated;
+	bool disable_pt = true;
+
+	h2c_pkt[0] = cmd;
+	h2c_pkt[1] = si->mac_id;
+	h2c_pkt[2] = (si->rate_id & 0x1f) | ((si->init_ra_lv & 0x3) << 5) |
+		     (si->sgi_enable << 7);
+	h2c_pkt[3] = (si->bw_mode & 0x03) | ((si->ldpc_en ? 1 : 2) << 2) |
+		     (no_update << 3) | (si->vht_enable << 4) |
+		     (disable_pt << 6);
+	h2c_pkt[4] = (si->ra_mask & 0xff);
+	h2c_pkt[5] = (si->ra_mask & 0xff00) >> 8;
+	h2c_pkt[6] = (si->ra_mask & 0xff0000) >> 16;
+	h2c_pkt[7] = (si->ra_mask & 0xff000000) >> 24;
+
+	si->init_ra_lv = 0;
+	si->updated = true;
+
+	rtw_fw_send_h2c_command(rtwdev, cmd, h2c_pkt);
+}
+
+void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect)
+{
+	u8 cmd_id = CMD_ID_MEDIA_STATUS_RPT;
+	u8 cmd_class = CLASS_MEDIA_STATUS_RPT;
+	u8 cmd = (cmd_class << RTW_H2C_CLASS_OFFSET) | cmd_id;
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+
+	MEDIA_STATUS_RPT_SET_CMD_ID(h2c_pkt, cmd_id);
+	MEDIA_STATUS_RPT_SET_CLASS(h2c_pkt, cmd_class);
+	MEDIA_STATUS_RPT_SET_OP_MODE(h2c_pkt, connect);
+	MEDIA_STATUS_RPT_SET_MACID(h2c_pkt, mac_id);
+
+	rtw_fw_send_h2c_command(rtwdev, cmd, h2c_pkt);
+}
+
+void rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev)
+{
+	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
+	u8 cmd_id = CMD_ID_SET_PWR_MODE;
+	u8 cmd_class = CLASS_SET_PWR_MODE;
+	u8 cmd = (cmd_class << RTW_H2C_CLASS_OFFSET) | cmd_id;
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+
+	SET_PWR_MODE_SET_CMD_ID(h2c_pkt, cmd_id);
+	SET_PWR_MODE_SET_CLASS(h2c_pkt, cmd_class);
+	SET_PWR_MODE_SET_MODE(h2c_pkt, conf->mode);
+	SET_PWR_MODE_SET_RLBM(h2c_pkt, conf->rlbm);
+	SET_PWR_MODE_SET_SMART_PS(h2c_pkt, conf->smart_ps);
+	SET_PWR_MODE_SET_AWAKE_INTERVAL(h2c_pkt, conf->awake_interval);
+	SET_PWR_MODE_SET_PORT_ID(h2c_pkt, conf->port_id);
+	SET_PWR_MODE_SET_PWR_STATE(h2c_pkt, conf->state);
+
+	rtw_fw_send_h2c_command(rtwdev, cmd, h2c_pkt);
+}
+
+static void rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev)
+{
+	struct rtw_rsvd_page *rsvd_pkt;
+	enum rtw_rsvd_packet_type type;
+	u8 cmd_id = CMD_ID_RSVD_PAGE;
+	u8 cmd_class = CLASS_RSVD_PAGE;
+	u8 cmd = (cmd_class << RTW_H2C_CLASS_OFFSET) | cmd_id;
+	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
+
+	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) {
+		type = rsvd_pkt->type;
+		switch (type) {
+		case RSVD_PROBE_RESP:
+			*(h2c_pkt + 1) = rsvd_pkt->page;
+			rtw_dbg(rtwdev, "RSVD_PROBE_RESP Loc: %d",
+				rsvd_pkt->page);
+			break;
+		case RSVD_PS_POLL:
+			*(h2c_pkt + 2) = rsvd_pkt->page;
+			rtw_dbg(rtwdev, "RSVD_PS_POLL Loc: %d", rsvd_pkt->page);
+			break;
+		case RSVD_NULL:
+			*(h2c_pkt + 3) = rsvd_pkt->page;
+			rtw_dbg(rtwdev, "RSVD_NULL Loc: %d", rsvd_pkt->page);
+			break;
+		case RSVD_QOS_NULL:
+			*(h2c_pkt + 4) = rsvd_pkt->page;
+			rtw_dbg(rtwdev, "RSVD_QOS_NULL Loc: %d\n",
+				rsvd_pkt->page);
+			break;
+		default:
+			break;
+		}
+	}
+	RSVD_PAGE_SET_CMD_ID(h2c_pkt, cmd_id);
+	RSVD_PAGE_SET_CLASS(h2c_pkt, cmd_class);
+
+	rtw_fw_send_h2c_command(rtwdev, cmd, h2c_pkt);
+}
+
+static struct sk_buff *
+rtw_beacon_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct sk_buff *skb_new;
+
+	if (vif->type != NL80211_IFTYPE_AP &&
+	    vif->type != NL80211_IFTYPE_ADHOC &&
+	    !ieee80211_vif_is_mesh(vif)) {
+		skb_new = alloc_skb(1, GFP_KERNEL);
+		skb_put(skb_new, 1);
+	} else {
+		skb_new = ieee80211_beacon_get(hw, vif);
+	}
+
+	return skb_new;
+}
+
+static struct sk_buff *rtw_get_rsvd_page_skb(struct ieee80211_hw *hw,
+					     struct ieee80211_vif *vif,
+					     enum rtw_rsvd_packet_type type)
+{
+	struct sk_buff *skb_new;
+
+	switch (type) {
+	case RSVD_BEACON:
+		skb_new = rtw_beacon_get(hw, vif);
+		break;
+	case RSVD_PS_POLL:
+		skb_new = ieee80211_pspoll_get(hw, vif);
+		break;
+	case RSVD_PROBE_RESP:
+		skb_new = ieee80211_proberesp_get(hw, vif);
+		break;
+	case RSVD_NULL:
+		skb_new = ieee80211_nullfunc_get(hw, vif, false);
+		break;
+	case RSVD_QOS_NULL:
+		skb_new = ieee80211_nullfunc_get(hw, vif, true);
+		break;
+	default:
+		return NULL;
+	}
+
+	if (!skb_new)
+		return NULL;
+
+	return skb_new;
+}
+
+static void rtw_fill_rsvd_page_desc(struct rtw_dev *rtwdev, struct sk_buff *skb)
+{
+	struct rtw_tx_pkt_info pkt_info;
+	struct rtw_chip_info *chip = rtwdev->chip;
+	u8 *pkt_desc;
+
+	memset(&pkt_info, 0, sizeof(pkt_info));
+	rtw_rsvd_page_pkt_info_update(rtwdev, &pkt_info, skb);
+	pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
+	memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
+	rtw_tx_fill_tx_desc(&pkt_info, skb);
+}
+
+static inline u8 rtw_len_to_page(unsigned int len, u8 page_size)
+{
+	return DIV_ROUND_UP(len, page_size);
+}
+
+static void rtw_rsvd_page_list_to_buf(struct rtw_dev *rtwdev, u8 page_size,
+				      u8 page_margin, u32 page, u8 *buf,
+				      struct rtw_rsvd_page *rsvd_pkt)
+{
+	struct sk_buff *skb = rsvd_pkt->skb;
+
+	if (rsvd_pkt->add_txdesc)
+		rtw_fill_rsvd_page_desc(rtwdev, skb);
+
+	if (page >= 1)
+		memcpy(buf + page_margin + page_size * (page - 1),
+		       skb->data, skb->len);
+	else
+		memcpy(buf, skb->data, skb->len);
+}
+
+void rtw_add_rsvd_page(struct rtw_dev *rtwdev, enum rtw_rsvd_packet_type type,
+		       bool txdesc)
+{
+	struct rtw_rsvd_page *rsvd_pkt;
+
+	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) {
+		if (rsvd_pkt->type == type)
+			return;
+	}
+
+	rsvd_pkt = kmalloc(sizeof(*rsvd_pkt),
+			   in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	rsvd_pkt->type = type;
+	rsvd_pkt->add_txdesc = txdesc;
+	list_add_tail(&rsvd_pkt->list, &rtwdev->rsvd_page_list);
+}
+
+void rtw_reset_rsvd_page(struct rtw_dev *rtwdev)
+{
+	struct rtw_rsvd_page *rsvd_pkt, *tmp;
+
+	list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list, list) {
+		if (rsvd_pkt->type == RSVD_BEACON)
+			continue;
+		list_del(&rsvd_pkt->list);
+		kfree(rsvd_pkt);
+	}
+}
+
+int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
+				u8 *buf, u32 size)
+{
+	u8 bckp[2];
+	u8 val;
+	u16 rsvd_pg_head;
+	u32 cnt;
+	int ret;
+
+	if (!size)
+		return -EINVAL;
+
+	pg_addr &= BIT_MASK_BCN_HEAD_1_V1;
+	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, pg_addr | BIT(15));
+
+	val = rtw_read8(rtwdev, REG_CR + 1);
+	bckp[0] = val;
+	val |= BIT(0);
+	rtw_write8(rtwdev, REG_CR + 1, val);
+
+	val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2);
+	bckp[1] = val;
+	val &= ~BIT(6);
+	rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, val);
+
+	ret = rtw_hci_write_data_rsvd_page(rtwdev, buf, size);
+	if (ret) {
+		rtw_err(rtwdev, "failed to write data to rsvd page\n");
+		goto restore;
+	}
+
+	cnt = 1000;
+	while (!(rtw_read8(rtwdev, REG_FIFOPAGE_CTRL_2 + 1) & BIT(7))) {
+		udelay(10);
+		cnt--;
+		if (cnt == 0) {
+			rtw_err(rtwdev, "error beacon valid\n");
+			ret = -EBUSY;
+			break;
+		}
+	}
+
+restore:
+	rsvd_pg_head = rtwdev->fifo.rsvd_boundary;
+	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, rsvd_pg_head | BIT(15));
+	rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]);
+	rtw_write8(rtwdev, REG_CR + 1, bckp[0]);
+
+	return ret;
+}
+
+static int rtw_download_drv_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size)
+{
+	u32 pg_size;
+	u32 pg_num = 0;
+	u16 pg_addr = 0;
+
+	pg_size = rtwdev->chip->page_size;
+	pg_num = size / pg_size + ((size & (pg_size - 1)) ? 1 : 0);
+	if (pg_num > rtwdev->fifo.rsvd_drv_pg_num)
+		return -ENOMEM;
+
+	pg_addr = rtwdev->fifo.rsvd_drv_addr;
+
+	return rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size);
+}
+
+static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev,
+			       struct ieee80211_vif *vif, u32 *size)
+{
+	struct ieee80211_hw *hw = rtwdev->hw;
+	struct rtw_chip_info *chip = rtwdev->chip;
+	struct sk_buff *iter;
+	struct rtw_rsvd_page *rsvd_pkt;
+	u32 page = 0;
+	u8 total_page = 0;
+	u8 page_size, page_margin, tx_desc_sz;
+	u8 *buf;
+
+	page_size = chip->page_size;
+	tx_desc_sz = chip->tx_pkt_desc_sz;
+	page_margin = page_size - tx_desc_sz;
+
+	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) {
+		iter = rtw_get_rsvd_page_skb(hw, vif, rsvd_pkt->type);
+		if (!iter) {
+			rtw_err(rtwdev, "fail to build rsvd packet\n");
+			goto release_skb;
+		}
+		rsvd_pkt->skb = iter;
+		rsvd_pkt->page = total_page;
+		if (rsvd_pkt->add_txdesc)
+			total_page += rtw_len_to_page(iter->len + tx_desc_sz,
+						      page_size);
+		else
+			total_page += rtw_len_to_page(iter->len, page_size);
+	}
+
+	if (total_page > rtwdev->fifo.rsvd_drv_pg_num) {
+		rtw_err(rtwdev, "rsvd page over size: %d\n", total_page);
+		goto release_skb;
+	}
+
+	*size = (total_page - 1) * page_size + page_margin;
+	buf = kzalloc(*size, GFP_KERNEL | GFP_ATOMIC);
+
+	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list) {
+		rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin,
+					  page, buf, rsvd_pkt);
+		page += rtw_len_to_page(rsvd_pkt->skb->len, page_size);
+	}
+	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list)
+		kfree_skb(rsvd_pkt->skb);
+
+	return buf;
+
+release_skb:
+	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, list)
+		kfree_skb(rsvd_pkt->skb);
+
+	return NULL;
+}
+
+static int
+rtw_download_beacon(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)
+{
+	struct ieee80211_hw *hw = rtwdev->hw;
+	struct sk_buff *skb;
+	int ret = 0;
+
+	skb = rtw_beacon_get(hw, vif);
+	if (!skb) {
+		rtw_err(rtwdev, "failed to get beacon skb\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = rtw_download_drv_rsvd_page(rtwdev, skb->data, skb->len);
+	if (ret)
+		rtw_err(rtwdev, "failed to download drv rsvd page\n");
+
+	dev_kfree_skb(skb);
+
+out:
+	return ret;
+}
+
+int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev, struct ieee80211_vif *vif)
+{
+	u8 *buf;
+	u32 size;
+	int ret;
+
+	buf = rtw_build_rsvd_page(rtwdev, vif, &size);
+	if (!buf) {
+		rtw_err(rtwdev, "failed to build rsvd page pkt\n");
+		return -ENOMEM;
+	}
+
+	ret = rtw_download_drv_rsvd_page(rtwdev, buf, size);
+	if (ret) {
+		rtw_err(rtwdev, "failed to download drv rsvd page\n");
+		goto free;
+	}
+
+	ret = rtw_download_beacon(rtwdev, vif);
+	if (ret) {
+		rtw_err(rtwdev, "failed to download beacon\n");
+		goto free;
+	}
+
+	rtw_send_rsvd_page_h2c(rtwdev);
+
+free:
+	kfree(buf);
+
+	return ret;
+}
+
+int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
+			   u32 offset, u32 size, u32 *buf)
+{
+	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
+	u32 residue, i;
+	u16 start_pg;
+	u16 idx = 0;
+	u16 ctl;
+	u8 rcr;
+
+	if (size & 0x3) {
+		rtw_warn(rtwdev, "should be 4-byte aligned\n");
+		return -EINVAL;
+	}
+
+	offset += fifo->rsvd_boundary << TX_PAGE_SIZE_SHIFT;
+	residue = offset & (FIFO_PAGE_SIZE - 1);
+	start_pg = offset >> FIFO_PAGE_SIZE_SHIFT;
+	start_pg += RSVD_PAGE_START_ADDR;
+
+	rcr = rtw_read8(rtwdev, REG_RCR + 2);
+	ctl = rtw_read16(rtwdev, REG_PKTBUF_DBG_CTRL) & 0xf000;
+
+	/* disable rx clock gate */
+	rtw_write8(rtwdev, REG_RCR, rcr | BIT(3));
+
+	do {
+		rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, start_pg | ctl);
+
+		for (i = FIFO_DUMP_ADDR + residue;
+		     i < FIFO_DUMP_ADDR + FIFO_PAGE_SIZE; i += 4) {
+			buf[idx++] = rtw_read32(rtwdev, i);
+			size -= 4;
+			if (size == 0)
+				goto out;
+		}
+
+		residue = 0;
+		start_pg++;
+	} while (size);
+
+out:
+	rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl);
+	rtw_write8(rtwdev, REG_RCR + 2, rcr);
+	return 0;
+}
diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
new file mode 100644
index 0000000..c063ffd
--- /dev/null
+++ b/drivers/net/wireless/realtek/rtw88/fw.h
@@ -0,0 +1,182 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright(c) 2018  Realtek Corporation.
+ */
+
+#ifndef __RTW_FW_H_
+#define __RTW_FW_H_
+
+#define RTW_H2C_CLASS_OFFSET	5
+
+#define H2C_PKT_SIZE		32
+#define H2C_PKT_HDR_SIZE	8
+
+/* FW bin information */
+#define FW_HDR_SIZE			64
+#define FW_HDR_CHKSUM_SIZE		8
+#define FW_HDR_VERSION			4
+#define FW_HDR_SUBVERSION		6
+#define FW_HDR_SUBINDEX			7
+#define FW_HDR_MONTH			16
+#define FW_HDR_DATE			17
+#define FW_HDR_HOUR			18
+#define FW_HDR_MIN			19
+#define FW_HDR_YEAR			20
+#define FW_HDR_MEM_USAGE		24
+#define FW_HDR_H2C_FMT_VER		28
+#define FW_HDR_DMEM_ADDR		32
+#define FW_HDR_DMEM_SIZE		36
+#define FW_HDR_IMEM_SIZE		48
+#define FW_HDR_EMEM_SIZE		52
+#define FW_HDR_EMEM_ADDR		56
+#define FW_HDR_IMEM_ADDR		60
+
+#define FIFO_PAGE_SIZE_SHIFT		12
+#define FIFO_PAGE_SIZE			4096
+#define RSVD_PAGE_START_ADDR		0x780
+#define FIFO_DUMP_ADDR			0x8000
+
+enum rtw_c2h_cmd_id {
+	C2H_BT_INFO = 0x09,
+	C2H_HW_FEATURE_REPORT = 0x19,
+	C2H_HW_FEATURE_DUMP = 0xfd,
+	C2H_HALMAC = 0xff,
+};
+
+struct rtw_c2h_cmd {
+	u8 id;
+	u8 seq;
+	u8 payload[0];
+} __packed;
+
+enum rtw_rsvd_packet_type {
+	RSVD_BEACON,
+	RSVD_PS_POLL,
+	RSVD_PROBE_RESP,
+	RSVD_NULL,
+	RSVD_QOS_NULL,
+};
+
+enum rtw_fw_rf_type {
+	FW_RF_1T2R = 0,
+	FW_RF_2T4R = 1,
+	FW_RF_2T2R = 2,
+	FW_RF_2T3R = 3,
+	FW_RF_1T1R = 4,
+	FW_RF_2T2R_GREEN = 5,
+	FW_RF_3T3R = 6,
+	FW_RF_3T4R = 7,
+	FW_RF_4T4R = 8,
+	FW_RF_MAX_TYPE = 0xF,
+};
+
+struct rtw_general_info {
+	u8 rfe_type;
+	u8 rf_type;
+	u8 tx_ant_status;
+	u8 rx_ant_status;
+};
+
+struct rtw_iqk_para {
+	u8 clear;
+	u8 segment_iqk;
+};
+
+struct rtw_rsvd_page {
+	struct list_head list;
+	struct sk_buff *skb;
+	enum rtw_rsvd_packet_type type;
+	u8 page;
+	bool add_txdesc;
+};
+
+#define SUB_CMD_ID_GENERAL_INFO 0X0D
+#define SUB_CMD_ID_PHYDM_INFO 0X11
+#define SUB_CMD_ID_IQK 0X0E
+
+#define CMD_ID_RSSI_MONITOR	0x02
+#define CLASS_RSSI_MONITOR	0x02
+#define CMD_ID_RA_INFO		0x00
+#define CLASS_RA_INFO		0x02
+#define CMD_ID_MEDIA_STATUS_RPT	0X01
+#define CLASS_MEDIA_STATUS_RPT	0X0
+#define CMD_ID_SET_PWR_MODE	0X00
+#define CLASS_SET_PWR_MODE	0X01
+#define CMD_ID_RSVD_PAGE	0X0
+#define CLASS_RSVD_PAGE		0X0
+
+#define FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, value)                             \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X04, 16, 16, value)
+#define GENERAL_INFO_SET_FW_TX_BOUNDARY(h2c_pkt, value)                        \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 16, 8, value)
+#define FW_OFFLOAD_H2C_SET_TOTAL_LEN(h2c_pkt, value)                           \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X04, 0, 16, value)
+#define FW_OFFLOAD_H2C_SET_SUB_CMD_ID(h2c_pkt, value)                          \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 16, 16, value)
+#define FW_OFFLOAD_H2C_SET_CATEGORY(h2c_pkt, value)                            \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 0, 7, value)
+#define FW_OFFLOAD_H2C_SET_CMD_ID(h2c_pkt, value)                              \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 8, 8, value)
+#define PHYDM_INFO_SET_REF_TYPE(h2c_pkt, value)                                \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 0, 8, value)
+#define PHYDM_INFO_SET_RF_TYPE(h2c_pkt, value)                                 \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 8, 8, value)
+#define PHYDM_INFO_SET_CUT_VER(h2c_pkt, value)                                 \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 16, 8, value)
+#define PHYDM_INFO_SET_RX_ANT_STATUS(h2c_pkt, value)                           \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 24, 4, value)
+#define PHYDM_INFO_SET_TX_ANT_STATUS(h2c_pkt, value)                           \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 28, 4, value)
+#define IQK_SET_CLEAR(h2c_pkt, value)                                          \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 0, 1, value)
+#define IQK_SET_SEGMENT_IQK(h2c_pkt, value)                                    \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X08, 1, 1, value)
+#define MEDIA_STATUS_RPT_SET_CMD_ID(h2c_pkt, value)                            \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 0, 5, value)
+#define MEDIA_STATUS_RPT_SET_CLASS(h2c_pkt, value)                             \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 5, 3, value)
+#define MEDIA_STATUS_RPT_SET_OP_MODE(h2c_pkt, value)                           \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 8, 1, value)
+#define MEDIA_STATUS_RPT_SET_MACID(h2c_pkt, value)                             \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 16, 8, value)
+#define SET_PWR_MODE_SET_CMD_ID(h2c_pkt, value)                                \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 0, 5, value)
+#define SET_PWR_MODE_SET_CLASS(h2c_pkt, value)                                 \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 5, 3, value)
+#define SET_PWR_MODE_SET_MODE(h2c_pkt, value)                                  \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 8, 7, value)
+#define SET_PWR_MODE_SET_RLBM(h2c_pkt, value)                                  \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 16, 4, value)
+#define SET_PWR_MODE_SET_SMART_PS(h2c_pkt, value)                              \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 20, 4, value)
+#define SET_PWR_MODE_SET_AWAKE_INTERVAL(h2c_pkt, value)                        \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 24, 8, value)
+#define SET_PWR_MODE_SET_PORT_ID(h2c_pkt, value)                               \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X04, 5, 3, value)
+#define SET_PWR_MODE_SET_PWR_STATE(h2c_pkt, value)                             \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X04, 8, 8, value)
+#define RSVD_PAGE_SET_CMD_ID(h2c_pkt, value)                                   \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 0, 5, value)
+#define RSVD_PAGE_SET_CLASS(h2c_pkt, value)                                    \
+	SET_BITS_TO_LE_4BYTE((h2c_pkt) + 0X00, 5, 3, value)
+
+void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb);
+void rtw_fw_send_general_info(struct rtw_dev *rtwdev,
+			      struct rtw_general_info *info);
+void rtw_fw_send_phydm_info(struct rtw_dev *rtwdev,
+			    struct rtw_general_info *info);
+void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para);
+void rtw_fw_fill_h2c_cmd(struct rtw_dev *rtwdev, u8 cmd, u8 len, u8 *buf);
+void rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev);
+void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si);
+void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si);
+void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool conn);
+void rtw_add_rsvd_page(struct rtw_dev *rtwdev, enum rtw_rsvd_packet_type type,
+		       bool txdesc);
+int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
+				u8 *buf, u32 size);
+void rtw_reset_rsvd_page(struct rtw_dev *rtwdev);
+int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev,
+			      struct ieee80211_vif *vif);
+int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
+			   u32 offset, u32 size, u32 *buf);
+#endif