diff mbox series

[3/5] rtw88: add dump firmware fifo support

Message ID 20200925061219.23754-4-tehuang@realtek.com (mailing list archive)
State Accepted
Commit 0fbc2f0f34cc57e8d6076631733e0095ac031995
Delegated to: Kalle Valo
Headers show
Series rtw88: add firmware crash recovery and some fixes | expand

Commit Message

Andy Huang Sept. 25, 2020, 6:12 a.m. UTC
From: Tzu-En Huang <tehuang@realtek.com>

Rtw88 currently has a function to dump reserved page section of the
firmware fifo. Reserved page is just part of the firmware fifo, there
are multiple sections in the firmware fifo for different usages, such as
firmware rx fifo and tx fifo.
This commit adds a function to check not only the reserved page section
but also other parts of the firmware fifo. In addition, we need to dump
firmware fifo to dump the debug log message if firmware crashes.

Signed-off-by: Tzu-En Huang <tehuang@realtek.com>
---
 drivers/net/wireless/realtek/rtw88/debug.c    |  3 +-
 drivers/net/wireless/realtek/rtw88/fw.c       | 77 +++++++++++++++----
 drivers/net/wireless/realtek/rtw88/fw.h       |  3 +-
 drivers/net/wireless/realtek/rtw88/main.h     | 14 ++++
 drivers/net/wireless/realtek/rtw88/rtw8822b.c |  3 +
 drivers/net/wireless/realtek/rtw88/rtw8822c.c |  3 +
 6 files changed, 85 insertions(+), 18 deletions(-)

Comments

Nathan Chancellor Oct. 1, 2020, 7:06 p.m. UTC | #1
On Fri, Sep 25, 2020 at 02:12:17PM +0800, tehuang@realtek.com wrote:
> From: Tzu-En Huang <tehuang@realtek.com>
> 
> Rtw88 currently has a function to dump reserved page section of the
> firmware fifo. Reserved page is just part of the firmware fifo, there
> are multiple sections in the firmware fifo for different usages, such as
> firmware rx fifo and tx fifo.
> This commit adds a function to check not only the reserved page section
> but also other parts of the firmware fifo. In addition, we need to dump
> firmware fifo to dump the debug log message if firmware crashes.
> 
> Signed-off-by: Tzu-En Huang <tehuang@realtek.com>
> ---
>  drivers/net/wireless/realtek/rtw88/debug.c    |  3 +-
>  drivers/net/wireless/realtek/rtw88/fw.c       | 77 +++++++++++++++----
>  drivers/net/wireless/realtek/rtw88/fw.h       |  3 +-
>  drivers/net/wireless/realtek/rtw88/main.h     | 14 ++++
>  drivers/net/wireless/realtek/rtw88/rtw8822b.c |  3 +
>  drivers/net/wireless/realtek/rtw88/rtw8822c.c |  3 +
>  6 files changed, 85 insertions(+), 18 deletions(-)
> 
> diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c
> index 985cf5d60615..bff6ce19345a 100644
> --- a/drivers/net/wireless/realtek/rtw88/debug.c
> +++ b/drivers/net/wireless/realtek/rtw88/debug.c
> @@ -229,7 +229,8 @@ static int rtw_debugfs_get_rsvd_page(struct seq_file *m, void *v)
>  	if (!buf)
>  		return -ENOMEM;
>  
> -	ret = rtw_dump_drv_rsvd_page(rtwdev, offset, buf_size, (u32 *)buf);
> +	ret = rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RSVD_PAGE, offset,
> +			       buf_size, (u32 *)buf);
>  	if (ret) {
>  		rtw_err(rtwdev, "failed to dump rsvd page\n");
>  		vfree(buf);
> diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
> index 6a50bb993caf..042015bc8055 100644
> --- a/drivers/net/wireless/realtek/rtw88/fw.c
> +++ b/drivers/net/wireless/realtek/rtw88/fw.c
> @@ -1413,29 +1413,16 @@ int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev)
>  	return ret;
>  }
>  
> -int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
> -			   u32 offset, u32 size, u32 *buf)
> +static void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size,
> +				  u32 *buf, u32 residue, u16 start_pg)
>  {
> -	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
> -	u32 residue, i;
> -	u16 start_pg;
> +	u32 i;
>  	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));
>  
> @@ -1457,6 +1444,64 @@ int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
>  out:
>  	rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl);
>  	rtw_write8(rtwdev, REG_RCR + 2, rcr);
> +}
> +
> +static void rtw_fw_read_fifo(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel,
> +			     u32 offset, u32 size, u32 *buf)
> +{
> +	struct rtw_chip_info *chip = rtwdev->chip;
> +	u32 start_pg, residue;
> +
> +	if (sel >= RTW_FW_FIFO_MAX) {
> +		rtw_dbg(rtwdev, RTW_DBG_FW, "wrong fw fifo sel\n");
> +		return;
> +	}
> +	if (sel == RTW_FW_FIFO_SEL_RSVD_PAGE)
> +		offset += rtwdev->fifo.rsvd_boundary << TX_PAGE_SIZE_SHIFT;
> +	residue = offset & (FIFO_PAGE_SIZE - 1);
> +	start_pg = (offset >> FIFO_PAGE_SIZE_SHIFT) + chip->fw_fifo_addr[sel];
> +
> +	rtw_fw_read_fifo_page(rtwdev, offset, size, buf, residue, start_pg);
> +}
> +
> +static bool rtw_fw_dump_check_size(struct rtw_dev *rtwdev,
> +				   enum rtw_fw_fifo_sel sel,
> +				   u32 start_addr, u32 size)
> +{
> +	switch (sel) {
> +	case RTW_FW_FIFO_SEL_TX:
> +	case RTW_FW_FIFO_SEL_RX:
> +		if ((start_addr + size) > rtwdev->chip->fw_fifo_addr[sel])
> +			return false;
> +		/*fall through*/
> +	default:
> +		return true;
> +	}
> +}
> +
> +int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
> +		     u32 *buffer)
> +{
> +	if (!rtwdev->chip->fw_fifo_addr) {

This causes a clang warning, which points out it is probably not doing
what you think it is:

drivers/net/wireless/realtek/rtw88/fw.c:1485:21: warning: address of
array 'rtwdev->chip->fw_fifo_addr' will always evaluate to 'true'
[-Wpointer-bool-conversion]
        if (!rtwdev->chip->fw_fifo_addr) {
            ~~~~~~~~~~~~~~~^~~~~~~~~~~~
1 warning generated.

Was fw_fifo_addr[0] intended or should the check just be deleted?

Cheers,
Nathan

> +		rtw_dbg(rtwdev, RTW_DBG_FW, "chip not support dump fw fifo\n");
> +		return -ENOTSUPP;
> +	}
> +
> +	if (size == 0 || !buffer)
> +		return -EINVAL;
> +
> +	if (size & 0x3) {
> +		rtw_dbg(rtwdev, RTW_DBG_FW, "not 4byte alignment\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!rtw_fw_dump_check_size(rtwdev, fifo_sel, addr, size)) {
> +		rtw_dbg(rtwdev, RTW_DBG_FW, "fw fifo dump size overflow\n");
> +		return -EINVAL;
> +	}
> +
> +	rtw_fw_read_fifo(rtwdev, fifo_sel, addr, size, buffer);
> +
>  	return 0;
>  }
>  
> diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
> index b4e3f755e8fb..9c4863c011ba 100644
> --- a/drivers/net/wireless/realtek/rtw88/fw.h
> +++ b/drivers/net/wireless/realtek/rtw88/fw.h
> @@ -16,7 +16,6 @@
>  
>  #define FIFO_PAGE_SIZE_SHIFT		12
>  #define FIFO_PAGE_SIZE			4096
> -#define RSVD_PAGE_START_ADDR		0x780
>  #define FIFO_DUMP_ADDR			0x8000
>  
>  #define DLFW_PAGE_SIZE_SHIFT_LEGACY	12
> @@ -565,5 +564,7 @@ void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev,
>  void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable);
>  void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c);
>  void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev);
> +int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
> +		     u32 *buffer);
>  
>  #endif
> diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
> index 292336387b89..06bdc68555e7 100644
> --- a/drivers/net/wireless/realtek/rtw88/main.h
> +++ b/drivers/net/wireless/realtek/rtw88/main.h
> @@ -1083,6 +1083,17 @@ enum rtw_wlan_cpu {
>  	RTW_WCPU_11N,
>  };
>  
> +enum rtw_fw_fifo_sel {
> +	RTW_FW_FIFO_SEL_TX,
> +	RTW_FW_FIFO_SEL_RX,
> +	RTW_FW_FIFO_SEL_RSVD_PAGE,
> +	RTW_FW_FIFO_SEL_REPORT,
> +	RTW_FW_FIFO_SEL_LLT,
> +	RTW_FW_FIFO_SEL_RXBUF_FW,
> +
> +	RTW_FW_FIFO_MAX,
> +};
> +
>  /* hardware configuration for each IC */
>  struct rtw_chip_info {
>  	struct rtw_chip_ops *ops;
> @@ -1099,6 +1110,7 @@ struct rtw_chip_info {
>  	u32 ptct_efuse_size;
>  	u32 txff_size;
>  	u32 rxff_size;
> +	u32 fw_rxff_size;
>  	u8 band;
>  	u8 page_size;
>  	u8 csi_buf_pg_num;
> @@ -1109,6 +1121,8 @@ struct rtw_chip_info {
>  	bool rx_ldpc;
>  	u8 max_power_index;
>  
> +	u16 fw_fifo_addr[RTW_FW_FIFO_MAX];
> +
>  	bool ht_supported;
>  	bool vht_supported;
>  	u8 lps_deep_mode_supported;
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
> index b7a98dbbb09c..22d0dd640ac9 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
> @@ -2442,6 +2442,7 @@ struct rtw_chip_info rtw8822b_hw_spec = {
>  	.ptct_efuse_size = 96,
>  	.txff_size = 262144,
>  	.rxff_size = 24576,
> +	.fw_rxff_size = 12288,
>  	.txgi_factor = 1,
>  	.is_pwr_by_rate_dec = true,
>  	.max_power_index = 0x3f,
> @@ -2504,6 +2505,8 @@ struct rtw_chip_info rtw8822b_hw_spec = {
>  
>  	.coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822b),
>  	.coex_info_hw_regs = coex_info_hw_regs_8822b,
> +
> +	.fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680},
>  };
>  EXPORT_SYMBOL(rtw8822b_hw_spec);
>  
> diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
> index dd216a23fc99..e37300e98517 100644
> --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
> +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
> @@ -4294,6 +4294,7 @@ struct rtw_chip_info rtw8822c_hw_spec = {
>  	.ptct_efuse_size = 124,
>  	.txff_size = 262144,
>  	.rxff_size = 24576,
> +	.fw_rxff_size = 12288,
>  	.txgi_factor = 2,
>  	.is_pwr_by_rate_dec = false,
>  	.max_power_index = 0x7f,
> @@ -4364,6 +4365,8 @@ struct rtw_chip_info rtw8822c_hw_spec = {
>  
>  	.coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822c),
>  	.coex_info_hw_regs = coex_info_hw_regs_8822c,
> +
> +	.fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680},
>  };
>  EXPORT_SYMBOL(rtw8822c_hw_spec);
>  
> -- 
> 2.17.1
>
Kalle Valo Oct. 2, 2020, 8:14 a.m. UTC | #2
+ arnd

Nathan Chancellor <natechancellor@gmail.com> writes:

>> +int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
>> +		     u32 *buffer)
>> +{
>> +	if (!rtwdev->chip->fw_fifo_addr) {
>
> This causes a clang warning, which points out it is probably not doing
> what you think it is:
>
> drivers/net/wireless/realtek/rtw88/fw.c:1485:21: warning: address of
> array 'rtwdev->chip->fw_fifo_addr' will always evaluate to 'true'
> [-Wpointer-bool-conversion]
>         if (!rtwdev->chip->fw_fifo_addr) {
>             ~~~~~~~~~~~~~~~^~~~~~~~~~~~
> 1 warning generated.
>
> Was fw_fifo_addr[0] intended or should the check just be deleted?

BTW what is the easiest way to install clang for build testing the
kernel? For GCC I use crosstool[1] which is awesome as it makes the
installation so simple, do we have something similar for clang?

Just supporting x86 would be fine, as my use case would be just to
reproduce build warnings.

[1] https://mirrors.edge.kernel.org/pub/tools/crosstool/
Nathan Chancellor Oct. 2, 2020, 8:43 a.m. UTC | #3
On Fri, Oct 02, 2020 at 11:14:29AM +0300, Kalle Valo wrote:
> + arnd
> 
> Nathan Chancellor <natechancellor@gmail.com> writes:
> 
> >> +int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
> >> +		     u32 *buffer)
> >> +{
> >> +	if (!rtwdev->chip->fw_fifo_addr) {
> >
> > This causes a clang warning, which points out it is probably not doing
> > what you think it is:
> >
> > drivers/net/wireless/realtek/rtw88/fw.c:1485:21: warning: address of
> > array 'rtwdev->chip->fw_fifo_addr' will always evaluate to 'true'
> > [-Wpointer-bool-conversion]
> >         if (!rtwdev->chip->fw_fifo_addr) {
> >             ~~~~~~~~~~~~~~~^~~~~~~~~~~~
> > 1 warning generated.
> >
> > Was fw_fifo_addr[0] intended or should the check just be deleted?
> 
> BTW what is the easiest way to install clang for build testing the
> kernel? For GCC I use crosstool[1] which is awesome as it makes the
> installation so simple, do we have something similar for clang?
> 
> Just supporting x86 would be fine, as my use case would be just to
> reproduce build warnings.
> 
> [1] https://mirrors.edge.kernel.org/pub/tools/crosstool/
> 
> -- 
> https://patchwork.kernel.org/project/linux-wireless/list/
> 
> https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

Unfortunately, we do not have anything for clang right now. It is on my
TODO list but being a hobbyist, I have less time than I would like...

If you do not mind building it from source, I maintain a Python script
that tries to optimize building LLVM as much as possible by turning off
things that the kernel does not care about so that the build is quick
and it does not intrude or interfere with the host environment.

Something like this should work to give you a stable clang toolchain
that should work well for compiling the kernel:

$ git clone https://github.com/ClangBuiltLinux/tc-build
$ tc-build/build-llvm.py \
--branch llvmorg-11.0.0-rc5 \
--projects "clang;lld"
$ tc-build/install/bin/clang --version | head -1
ClangBuiltLinux clang version 11.0.0 (https://github.com/llvm/llvm-project 60a25202a7dd1e00067fcfce512086ebf3788537)

The script by default does a 2-stage build for optimization purposes; if
you cannot spare many cycles, feel free to add

--build-stage1-only --install-stage1-only

to the build-llvm.py invocation. The toolchain is installed to "install"
within the tc-build repo and it only requires a few external
dependencies (outlined in the README) that it lets you know about before
doing anything. Feel free to give it a shot and let me know if anything
is broken. Otherwise, as long as your distribution has clang 10.0.1 or
newer, it should be fine for compiling the kernel.

Cheers,
Nathan
Kalle Valo Oct. 6, 2020, 5:53 a.m. UTC | #4
Nathan Chancellor <natechancellor@gmail.com> writes:

> On Fri, Oct 02, 2020 at 11:14:29AM +0300, Kalle Valo wrote:
>> + arnd
>> 
>> Nathan Chancellor <natechancellor@gmail.com> writes:
>> 
>> >> +int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
>> >> +		     u32 *buffer)
>> >> +{
>> >> +	if (!rtwdev->chip->fw_fifo_addr) {
>> >
>> > This causes a clang warning, which points out it is probably not doing
>> > what you think it is:
>> >
>> > drivers/net/wireless/realtek/rtw88/fw.c:1485:21: warning: address of
>> > array 'rtwdev->chip->fw_fifo_addr' will always evaluate to 'true'
>> > [-Wpointer-bool-conversion]
>> >         if (!rtwdev->chip->fw_fifo_addr) {
>> >             ~~~~~~~~~~~~~~~^~~~~~~~~~~~
>> > 1 warning generated.
>> >
>> > Was fw_fifo_addr[0] intended or should the check just be deleted?
>> 
>> BTW what is the easiest way to install clang for build testing the
>> kernel? For GCC I use crosstool[1] which is awesome as it makes the
>> installation so simple, do we have something similar for clang?
>> 
>> Just supporting x86 would be fine, as my use case would be just to
>> reproduce build warnings.
>> 
>> [1] https://mirrors.edge.kernel.org/pub/tools/crosstool/
>> 
>> -- 
>> https://patchwork.kernel.org/project/linux-wireless/list/
>> 
>> https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches
>
> Unfortunately, we do not have anything for clang right now. It is on my
> TODO list but being a hobbyist, I have less time than I would like...
>
> If you do not mind building it from source, I maintain a Python script
> that tries to optimize building LLVM as much as possible by turning off
> things that the kernel does not care about so that the build is quick
> and it does not intrude or interfere with the host environment.
>
> Something like this should work to give you a stable clang toolchain
> that should work well for compiling the kernel:
>
> $ git clone https://github.com/ClangBuiltLinux/tc-build
> $ tc-build/build-llvm.py \
> --branch llvmorg-11.0.0-rc5 \
> --projects "clang;lld"
> $ tc-build/install/bin/clang --version | head -1
> ClangBuiltLinux clang version 11.0.0
> (https://github.com/llvm/llvm-project
> 60a25202a7dd1e00067fcfce512086ebf3788537)
>
> The script by default does a 2-stage build for optimization purposes; if
> you cannot spare many cycles, feel free to add
>
> --build-stage1-only --install-stage1-only
>
> to the build-llvm.py invocation. The toolchain is installed to "install"
> within the tc-build repo and it only requires a few external
> dependencies (outlined in the README) that it lets you know about before
> doing anything. Feel free to give it a shot and let me know if anything
> is broken.

Thanks, I'll try that when I have some free time.

> Otherwise, as long as your distribution has clang 10.0.1 or newer, it
> should be fine for compiling the kernel.

Good to know. My distro is old so I only have clang-8 available :)
diff mbox series

Patch

diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c
index 985cf5d60615..bff6ce19345a 100644
--- a/drivers/net/wireless/realtek/rtw88/debug.c
+++ b/drivers/net/wireless/realtek/rtw88/debug.c
@@ -229,7 +229,8 @@  static int rtw_debugfs_get_rsvd_page(struct seq_file *m, void *v)
 	if (!buf)
 		return -ENOMEM;
 
-	ret = rtw_dump_drv_rsvd_page(rtwdev, offset, buf_size, (u32 *)buf);
+	ret = rtw_fw_dump_fifo(rtwdev, RTW_FW_FIFO_SEL_RSVD_PAGE, offset,
+			       buf_size, (u32 *)buf);
 	if (ret) {
 		rtw_err(rtwdev, "failed to dump rsvd page\n");
 		vfree(buf);
diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c
index 6a50bb993caf..042015bc8055 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.c
+++ b/drivers/net/wireless/realtek/rtw88/fw.c
@@ -1413,29 +1413,16 @@  int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev)
 	return ret;
 }
 
-int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
-			   u32 offset, u32 size, u32 *buf)
+static void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size,
+				  u32 *buf, u32 residue, u16 start_pg)
 {
-	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
-	u32 residue, i;
-	u16 start_pg;
+	u32 i;
 	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));
 
@@ -1457,6 +1444,64 @@  int rtw_dump_drv_rsvd_page(struct rtw_dev *rtwdev,
 out:
 	rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl);
 	rtw_write8(rtwdev, REG_RCR + 2, rcr);
+}
+
+static void rtw_fw_read_fifo(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel,
+			     u32 offset, u32 size, u32 *buf)
+{
+	struct rtw_chip_info *chip = rtwdev->chip;
+	u32 start_pg, residue;
+
+	if (sel >= RTW_FW_FIFO_MAX) {
+		rtw_dbg(rtwdev, RTW_DBG_FW, "wrong fw fifo sel\n");
+		return;
+	}
+	if (sel == RTW_FW_FIFO_SEL_RSVD_PAGE)
+		offset += rtwdev->fifo.rsvd_boundary << TX_PAGE_SIZE_SHIFT;
+	residue = offset & (FIFO_PAGE_SIZE - 1);
+	start_pg = (offset >> FIFO_PAGE_SIZE_SHIFT) + chip->fw_fifo_addr[sel];
+
+	rtw_fw_read_fifo_page(rtwdev, offset, size, buf, residue, start_pg);
+}
+
+static bool rtw_fw_dump_check_size(struct rtw_dev *rtwdev,
+				   enum rtw_fw_fifo_sel sel,
+				   u32 start_addr, u32 size)
+{
+	switch (sel) {
+	case RTW_FW_FIFO_SEL_TX:
+	case RTW_FW_FIFO_SEL_RX:
+		if ((start_addr + size) > rtwdev->chip->fw_fifo_addr[sel])
+			return false;
+		/*fall through*/
+	default:
+		return true;
+	}
+}
+
+int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
+		     u32 *buffer)
+{
+	if (!rtwdev->chip->fw_fifo_addr) {
+		rtw_dbg(rtwdev, RTW_DBG_FW, "chip not support dump fw fifo\n");
+		return -ENOTSUPP;
+	}
+
+	if (size == 0 || !buffer)
+		return -EINVAL;
+
+	if (size & 0x3) {
+		rtw_dbg(rtwdev, RTW_DBG_FW, "not 4byte alignment\n");
+		return -EINVAL;
+	}
+
+	if (!rtw_fw_dump_check_size(rtwdev, fifo_sel, addr, size)) {
+		rtw_dbg(rtwdev, RTW_DBG_FW, "fw fifo dump size overflow\n");
+		return -EINVAL;
+	}
+
+	rtw_fw_read_fifo(rtwdev, fifo_sel, addr, size, buffer);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h
index b4e3f755e8fb..9c4863c011ba 100644
--- a/drivers/net/wireless/realtek/rtw88/fw.h
+++ b/drivers/net/wireless/realtek/rtw88/fw.h
@@ -16,7 +16,6 @@ 
 
 #define FIFO_PAGE_SIZE_SHIFT		12
 #define FIFO_PAGE_SIZE			4096
-#define RSVD_PAGE_START_ADDR		0x780
 #define FIFO_DUMP_ADDR			0x8000
 
 #define DLFW_PAGE_SIZE_SHIFT_LEGACY	12
@@ -565,5 +564,7 @@  void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev,
 void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable);
 void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c);
 void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev);
+int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
+		     u32 *buffer);
 
 #endif
diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h
index 292336387b89..06bdc68555e7 100644
--- a/drivers/net/wireless/realtek/rtw88/main.h
+++ b/drivers/net/wireless/realtek/rtw88/main.h
@@ -1083,6 +1083,17 @@  enum rtw_wlan_cpu {
 	RTW_WCPU_11N,
 };
 
+enum rtw_fw_fifo_sel {
+	RTW_FW_FIFO_SEL_TX,
+	RTW_FW_FIFO_SEL_RX,
+	RTW_FW_FIFO_SEL_RSVD_PAGE,
+	RTW_FW_FIFO_SEL_REPORT,
+	RTW_FW_FIFO_SEL_LLT,
+	RTW_FW_FIFO_SEL_RXBUF_FW,
+
+	RTW_FW_FIFO_MAX,
+};
+
 /* hardware configuration for each IC */
 struct rtw_chip_info {
 	struct rtw_chip_ops *ops;
@@ -1099,6 +1110,7 @@  struct rtw_chip_info {
 	u32 ptct_efuse_size;
 	u32 txff_size;
 	u32 rxff_size;
+	u32 fw_rxff_size;
 	u8 band;
 	u8 page_size;
 	u8 csi_buf_pg_num;
@@ -1109,6 +1121,8 @@  struct rtw_chip_info {
 	bool rx_ldpc;
 	u8 max_power_index;
 
+	u16 fw_fifo_addr[RTW_FW_FIFO_MAX];
+
 	bool ht_supported;
 	bool vht_supported;
 	u8 lps_deep_mode_supported;
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
index b7a98dbbb09c..22d0dd640ac9 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c
@@ -2442,6 +2442,7 @@  struct rtw_chip_info rtw8822b_hw_spec = {
 	.ptct_efuse_size = 96,
 	.txff_size = 262144,
 	.rxff_size = 24576,
+	.fw_rxff_size = 12288,
 	.txgi_factor = 1,
 	.is_pwr_by_rate_dec = true,
 	.max_power_index = 0x3f,
@@ -2504,6 +2505,8 @@  struct rtw_chip_info rtw8822b_hw_spec = {
 
 	.coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822b),
 	.coex_info_hw_regs = coex_info_hw_regs_8822b,
+
+	.fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680},
 };
 EXPORT_SYMBOL(rtw8822b_hw_spec);
 
diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
index dd216a23fc99..e37300e98517 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -4294,6 +4294,7 @@  struct rtw_chip_info rtw8822c_hw_spec = {
 	.ptct_efuse_size = 124,
 	.txff_size = 262144,
 	.rxff_size = 24576,
+	.fw_rxff_size = 12288,
 	.txgi_factor = 2,
 	.is_pwr_by_rate_dec = false,
 	.max_power_index = 0x7f,
@@ -4364,6 +4365,8 @@  struct rtw_chip_info rtw8822c_hw_spec = {
 
 	.coex_info_hw_regs_num = ARRAY_SIZE(coex_info_hw_regs_8822c),
 	.coex_info_hw_regs = coex_info_hw_regs_8822c,
+
+	.fw_fifo_addr = {0x780, 0x700, 0x780, 0x660, 0x650, 0x680},
 };
 EXPORT_SYMBOL(rtw8822c_hw_spec);