Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH 02/14] firmware: qcom: Add a generic PAS service
From: Sumit Garg @ 2026-03-10  4:58 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
	netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
	konradybcio, robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo,
	lumag, abhinav.kumar, jesszhan0024, marijn.suijten, airlied,
	simona, vikash.garodia, dikshita.agarwal, bod, mchehab, elder,
	andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
	mathieu.poirier, trilokkumar.soni, mukesh.ojha, pavan.kondeti,
	jorge.ramirez, tonyh, vignesh.viswanathan, srinivas.kandagatla,
	amirreza.zarrabi, jens.wiklander, op-tee, apurupa, skare,
	Sumit Garg
In-Reply-To: <aa5z23EtUF9k1MTy@sumit-xelite>

On Mon, Mar 09, 2026 at 12:46:43PM +0530, Sumit Garg wrote:
> On Mon, Mar 09, 2026 at 08:10:02AM +0100, Krzysztof Kozlowski wrote:
> > On 09/03/2026 05:55, Sumit Garg wrote:
> > > On Fri, Mar 06, 2026 at 12:15:01PM +0100, Krzysztof Kozlowski wrote:
> > >> On 06/03/2026 11:50, Sumit Garg wrote:
> > >>> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
> > >>>
> > >>> Qcom platforms has the legacy of using non-standard SCM calls
> > >>> splintered over the various kernel drivers. These SCM calls aren't
> > >>> compliant with the standard SMC calling conventions which is a
> > >>> prerequisite to enable migration to the FF-A specifications from
> > >>> Arm.
> > >>>
> > >>> OP-TEE as an alternative trusted OS to QTEE can't support these non-
> > >>> standard SCM calls. And even for newer architectures QTEE won't be able
> > >>> to support SCM calls either with FF-A requirements coming in. And with
> > >>> both OP-TEE and QTEE drivers well integrated in the TEE subsystem, it
> > >>> makes further sense to reuse the TEE bus client drivers infrastructure.
> > >>>
> > >>> The added benefit of TEE bus infrastructure is that there is support
> > >>> for discoverable/enumerable services. With that client drivers don't
> > >>> have to manually invoke a special SCM call to know the service status.
> > >>>
> > >>> So enable the generic Peripheral Authentication Service (PAS) provided
> > >>> by the firmware. It acts as the common layer with different TZ
> > >>> backends plugged in whether it's an SCM implementation or a proper
> > >>> TEE bus based PAS service implementation.
> > >>>
> > >>> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> > >>> ---
> > >>>  drivers/firmware/qcom/Kconfig          |   8 +
> > >>>  drivers/firmware/qcom/Makefile         |   1 +
> > >>>  drivers/firmware/qcom/qcom_pas.c       | 295 +++++++++++++++++++++++++
> > >>>  drivers/firmware/qcom/qcom_pas.h       |  53 +++++
> > >>>  include/linux/firmware/qcom/qcom_pas.h |  41 ++++
> > >>>  5 files changed, 398 insertions(+)
> > >>>  create mode 100644 drivers/firmware/qcom/qcom_pas.c
> > >>>  create mode 100644 drivers/firmware/qcom/qcom_pas.h
> > >>>  create mode 100644 include/linux/firmware/qcom/qcom_pas.h
> > >>>
> > >>> diff --git a/drivers/firmware/qcom/Kconfig b/drivers/firmware/qcom/Kconfig
> > >>> index b477d54b495a..8653639d06db 100644
> > >>> --- a/drivers/firmware/qcom/Kconfig
> > >>> +++ b/drivers/firmware/qcom/Kconfig
> > >>> @@ -6,6 +6,14 @@
> > >>>  
> > >>>  menu "Qualcomm firmware drivers"
> > >>>  
> > >>> +config QCOM_PAS
> > >>> +	tristate
> > >>> +	help
> > >>> +	  Enable the generic Peripheral Authentication Service (PAS) provided
> > >>> +	  by the firmware. It acts as the common layer with different TZ
> > >>> +	  backends plugged in whether it's an SCM implementation or a proper
> > >>> +	  TEE bus based PAS service implementation.
> > >>> +
> > >>>  config QCOM_SCM
> > >>>  	select QCOM_TZMEM
> > >>>  	tristate
> > >>> diff --git a/drivers/firmware/qcom/Makefile b/drivers/firmware/qcom/Makefile
> > >>> index 0be40a1abc13..dc5ab45f906a 100644
> > >>> --- a/drivers/firmware/qcom/Makefile
> > >>> +++ b/drivers/firmware/qcom/Makefile
> > >>> @@ -8,3 +8,4 @@ qcom-scm-objs += qcom_scm.o qcom_scm-smc.o qcom_scm-legacy.o
> > >>>  obj-$(CONFIG_QCOM_TZMEM)	+= qcom_tzmem.o
> > >>>  obj-$(CONFIG_QCOM_QSEECOM)	+= qcom_qseecom.o
> > >>>  obj-$(CONFIG_QCOM_QSEECOM_UEFISECAPP) += qcom_qseecom_uefisecapp.o
> > >>> +obj-$(CONFIG_QCOM_PAS)		+= qcom_pas.o
> > >>> diff --git a/drivers/firmware/qcom/qcom_pas.c b/drivers/firmware/qcom/qcom_pas.c
> > >>> new file mode 100644
> > >>> index 000000000000..dc04ff1b6be0
> > >>> --- /dev/null
> > >>> +++ b/drivers/firmware/qcom/qcom_pas.c
> > >>> @@ -0,0 +1,295 @@
> > >>> +// SPDX-License-Identifier: GPL-2.0
> > >>> +/*
> > >>> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > >>> + */
> > >>> +
> > >>> +#include <linux/delay.h>
> > >>> +#include <linux/device/devres.h>
> > >>> +#include <linux/firmware/qcom/qcom_pas.h>
> > >>> +#include <linux/of.h>
> > >>> +#include <linux/kernel.h>
> > >>> +#include <linux/module.h>
> > >>> +#include <linux/slab.h>
> > >>> +
> > >>> +#include "qcom_pas.h"
> > >>> +#include "qcom_scm.h"
> > >>> +
> > >>> +static struct qcom_pas_ops *ops_ptr;
> > >>
> > >> I really dislike this singleton design. And it is not even needed! If
> > >> you were storing here some allocated instance of SCM/PAS I could
> > >> understand, but singleton for only ops? Just implement one driver (so
> > >> SCM + whatever you have here) which will decide which ops to use,
> > >> through the probe. Really, this is neither needed nor beneficial.
> > > 
> > > The motivation here is rather quite opposite to the single monolithic
> > > SCM driver design. The TZ services like PAS, ICE and so on are going to
> > > be implemented as independent discoverable devices on TEE bus which
> > > rather needs independent kernel client drivers.
> > 
> > You still have singleton here. So if you think you do opposite to
> > singleton, then drop this static.
> 
> Sure.
> 
> > 
> > > 
> > > Also, the single driver probe can't work here since the SCM driver is
> > > bound to the platform bus whereas the TEE PAS driver is bound to the TEE
> > > bus. So there is a reason for the current design.
> > > 
> > >>
> > >> It actually leads to more problems with this barrier handling, see
> > >> further comments.
> > > 
> > > The barrier handling is something that I carried over from existing
> > > implmentation but I can't see a reason why it can't be replaced with a
> > > simple mutex. See diff below for mutex.
> > > 
> > >> ...
> > >>
> > >>> +
> > >>> +/**
> > >>> + * qcom_pas_shutdown() - Shut down the remote processor
> > >>> + * @pas_id:	peripheral authentication service id
> > >>> + *
> > >>> + * Returns 0 on success.
> > >>> + */
> > >>> +int qcom_pas_shutdown(u32 pas_id)
> > >>> +{
> > >>> +	if (ops_ptr)
> > >>> +		return ops_ptr->shutdown(ops_ptr->dev, pas_id);
> > >>> +
> > >>> +	return -ENODEV;
> > >>> +}
> > >>> +EXPORT_SYMBOL_GPL(qcom_pas_shutdown);
> > >>> +
> > >>> +/**
> > >>> + * qcom_pas_supported() - Check if the peripheral authentication service is
> > >>> + *			  available for the given peripheral
> > >>> + * @pas_id:	peripheral authentication service id
> > >>> + *
> > >>> + * Returns true if PAS is supported for this peripheral, otherwise false.
> > >>> + */
> > >>> +bool qcom_pas_supported(u32 pas_id)
> > >>> +{
> > >>> +	if (ops_ptr)
> > >>
> > >> Lack of barriers here is not looking right. Existing/old code is not a
> > >> good example, I fixed only the obvious issue, but new code should be
> > >> correct from the beginning.
> > >>
> > >> Barriers should normally be always paired, unless you have some clear
> > >> path no concurrent execution can happen here, but such explanation is
> > >> missing, look:
> > > 
> > > Actually concurrent execution is rather required here since TZ can
> > > support parallel bring-up of co-processors. The synchonization is only
> > > needed when PAS client drivers are performing a deferred probe waiting
> > > for the service to be available. However, you are right explanation is
> > > missing here which I will add in the next version.
> > 
> > Hm? Existing comments are completely useless. Your comment said just
> > "barrier" basically... That's nothing useful.
> 
> Agree, following is something I plan for v2 (using mutex instead of a
> barrier):
> 
> /*
>  * The ops mutex here is only intended to synchronize when client drivers
>  * are in parallel checking for PAS service availability. However, once the
>  * PAS backend becomes available, it is allowed for multiple threads to enter
>  * TZ for parallel bringup of co-processors during boot.
>  */
> static DEFINE_MUTEX(ops_mutex);

After more testing, it came out that there are corner cases where the
registered ops structure writes aren't visible to other cores. So indeed
a data barrier is needed instead of mutex. I will add relevant code
comments.

-Sumit

^ permalink raw reply

* Re: [PATCH 11/14] media: qcom: Switch to generic PAS TZ APIs
From: Sumit Garg @ 2026-03-10  5:04 UTC (permalink / raw)
  To: Jorge Ramirez
  Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
	netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
	konradybcio, robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo,
	lumag, abhinav.kumar, jesszhan0024, marijn.suijten, airlied,
	simona, vikash.garodia, dikshita.agarwal, bod, mchehab, elder,
	andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
	mathieu.poirier, trilokkumar.soni, mukesh.ojha, pavan.kondeti,
	tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
	jens.wiklander, op-tee, apurupa, skare, Sumit Garg
In-Reply-To: <aa6O5tir4kVIe0eZ@trex>

On Mon, Mar 09, 2026 at 10:12:06AM +0100, Jorge Ramirez wrote:
> On 06/03/26 16:20:24, Sumit Garg wrote:
> > From: Sumit Garg <sumit.garg@oss.qualcomm.com>
> > 
> > Switch qcom media client drivers over to generic PAS TZ APIs. Generic PAS
> > TZ service allows to support multiple TZ implementation backends like QTEE
> > based SCM PAS service, OP-TEE based PAS service and any further future TZ
> > backend service.
> > 
> > Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
> > ---
> >  drivers/media/platform/qcom/iris/iris_firmware.c |  9 +++++----
> >  drivers/media/platform/qcom/venus/firmware.c     | 11 ++++++-----
> >  2 files changed, 11 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
> > index 5f408024e967..b3c5281aea91 100644
> > --- a/drivers/media/platform/qcom/iris/iris_firmware.c
> > +++ b/drivers/media/platform/qcom/iris/iris_firmware.c
> > @@ -4,6 +4,7 @@
> >   */
> >  
> >  #include <linux/firmware.h>
> > +#include <linux/firmware/qcom/qcom_pas.h>
> >  #include <linux/firmware/qcom/qcom_scm.h>
> >  #include <linux/of_address.h>
> >  #include <linux/of_reserved_mem.h>
> > @@ -79,7 +80,7 @@ int iris_fw_load(struct iris_core *core)
> >  		return -ENOMEM;
> >  	}
> >  
> > -	ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id);
> > +	ret = qcom_pas_auth_and_reset(core->iris_platform_data->pas_id);
> >  	if (ret)  {
> >  		dev_err(core->dev, "auth and reset failed: %d\n", ret);
> >  		return ret;
> > @@ -93,7 +94,7 @@ int iris_fw_load(struct iris_core *core)
> >  						     cp_config->cp_nonpixel_size);
> >  		if (ret) {
> >  			dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret);
> > -			qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
> > +			qcom_pas_shutdown(core->iris_platform_data->pas_id);
> >  			return ret;
> >  		}
> >  	}
> > @@ -103,10 +104,10 @@ int iris_fw_load(struct iris_core *core)
> >  
> >  int iris_fw_unload(struct iris_core *core)
> >  {
> > -	return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
> > +	return qcom_pas_shutdown(core->iris_platform_data->pas_id);
> >  }
> >
> 
> are the calls to set_remote_state required?
> 0 is not the IRIS/VENUS remote processor.
> 
> If it is legacy, maybe they can be phased out?

Vikash, Dikshita,

Do you know the background of this set_remote_state calls?

BTW, the scope of this patch-set is to not change existing subsystem
drivers behaviour but just to enable the generic PAS interface. So
any driver changes can be taken as a follow up work.

-Sumit

> 
> 
> >  int iris_set_hw_state(struct iris_core *core, bool resume)
> >  {
> > -	return qcom_scm_set_remote_state(resume, 0);
> > +	return qcom_pas_set_remote_state(resume, 0);
> >  }
> > diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
> > index 1de7436713ed..3a38ff985822 100644
> > --- a/drivers/media/platform/qcom/venus/firmware.c
> > +++ b/drivers/media/platform/qcom/venus/firmware.c
> > @@ -12,6 +12,7 @@
> >  #include <linux/of_reserved_mem.h>
> >  #include <linux/platform_device.h>
> >  #include <linux/of_device.h>
> > +#include <linux/firmware/qcom/qcom_pas.h>
> >  #include <linux/firmware/qcom/qcom_scm.h>
> >  #include <linux/sizes.h>
> >  #include <linux/soc/qcom/mdt_loader.h>
> > @@ -58,7 +59,7 @@ int venus_set_hw_state(struct venus_core *core, bool resume)
> >  	int ret;
> >  
> >  	if (core->use_tz) {
> > -		ret = qcom_scm_set_remote_state(resume, 0);
> > +		ret = qcom_pas_set_remote_state(resume, 0);
> >  		if (resume && ret == -EINVAL)
> >  			ret = 0;
> >  		return ret;
> 

^ permalink raw reply

* Re: [PATCH 02/14] firmware: qcom: Add a generic PAS service
From: Trilok Soni @ 2026-03-10  7:33 UTC (permalink / raw)
  To: Sumit Garg, Jeff Johnson
  Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
	netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
	konradybcio, robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo,
	lumag, abhinav.kumar, jesszhan0024, marijn.suijten, airlied,
	simona, vikash.garodia, dikshita.agarwal, bod, mchehab, elder,
	andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
	mathieu.poirier, mukesh.ojha, pavan.kondeti, jorge.ramirez, tonyh,
	vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
	jens.wiklander, op-tee, apurupa, skare, Sumit Garg
In-Reply-To: <aa5qdscMRq7abfwT@sumit-xelite>

On 3/8/2026 11:36 PM, Sumit Garg wrote:
> On Fri, Mar 06, 2026 at 02:00:48PM -0800, Jeff Johnson wrote:
>> On 3/6/2026 11:47 AM, Trilok Soni wrote:
>>> On 3/6/2026 2:50 AM, Sumit Garg wrote:
>>>> +MODULE_LICENSE("GPL");
>>>> +MODULE_AUTHOR("Sumit Garg <sumit.garg@oss.qualcomm.com>");
>>>
>>> What is the convention for Qualcomm authored drivers? In some drivers
>>> I find that Qualcomm doesn't add MODULE_AUTHOR. Can Qualcomm community
>>> clarify it here. I prefer consistency here for the Qualcomm submissions. 
>>
>> WLAN team was told to not have MODULE_AUTHOR(), so ath10k was the last WLAN
>> driver that had a MODULE_AUTHOR() -- ath11k and ath12k do not have one.
> 
> As I said in my other reply, it is quite subsystem specific.
> 
>>
>> And in reality it is very rare for a given module, over time, to only have a
>> single author. The git history contains the real authorship. So just for that
>> reason I'd drop it.
> 
> Sure, but you would like the driver author to be involved in future
> maintenence of the driver. In my experience that's how usually the kernel
> development process works. If a separate maintainer's entry is fine then I
> can switch to that instead.

I would prefer the maintainers entry than MODULE_AUTHOR.

---Trilok Soni

^ permalink raw reply

* [PATCH rtw-next 00/13] wifi: rtw89: update hardware settings and tweak for MLO
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang

Mainly, this patchset is to add hardware settings, and tweak code related
to hardware MLO mode value. Others are almost random fixes.

The first four patches are to configure hardware settings, which are
written according to internal patches.

Patches 5-8 are to refine hardware MLO mode.

Patch 9 is WoWLAN related, which is to add retry times to let WoWLAN
being reliable.

Patch 10 is to remove mutex lock from driver. Since driver has used
wiphy_lock, it should be not necessary.

Patch 11 is a workaround to deal with AP interoperability problem.

Patch 12 is to recalculate AMSDU length to improve performance.

The last patch is for firmware crash simulation.

Chin-Yen Lee (1):
  wifi: rtw89: wow: add retry for ensuring packet are processed

Ping-Ke Shih (4):
  wifi: rtw89: mac: finish active TX immediately without waiting for
    DMAC
  wifi: rtw89: pci: update SER parameters for suspend/resume
  wifi: rtw89: mac: remove A-die off setting for RTL8852C and RTL8922A
  wifi: rtw89: phy: limit AMPDU number for RA try rate

Po-Hao Huang (2):
  wifi: rtw89: Drop malformed AMPDU frames with abnormal PN
  wifi: rtw89: Recalculate station aggregates when AMSDU length changes
    for MLO links

Zong-Zhe Yang (6):
  wifi: rtw89: move disabling dynamic mechanism functions to core
  wifi: rtw89: tweak settings of TX power and channel for Wi-Fi 7
  wifi: rtw89: chan: simplify link handling related to ROC
  wifi: rtw89: chan: recalc MLO DBCC mode based on current entity mode
  wifi: rtw89: replace RF mutex with wiphy lock assertion
  wifi: rtw89: debug: simulate Wi-Fi 7 SER L0/L1 without PS mode

 drivers/net/wireless/realtek/rtw89/chan.c     |  72 +++++----
 drivers/net/wireless/realtek/rtw89/core.c     | 150 +++++++++++++++++-
 drivers/net/wireless/realtek/rtw89/core.h     |  29 ++--
 drivers/net/wireless/realtek/rtw89/debug.c    |  45 ++----
 drivers/net/wireless/realtek/rtw89/mac.c      |  17 +-
 drivers/net/wireless/realtek/rtw89/mac.h      |   2 +
 drivers/net/wireless/realtek/rtw89/mac80211.c |   6 +
 drivers/net/wireless/realtek/rtw89/mac_be.c   |   5 +
 drivers/net/wireless/realtek/rtw89/pci.h      |   1 +
 drivers/net/wireless/realtek/rtw89/pci_be.c   |  52 +++++-
 drivers/net/wireless/realtek/rtw89/phy.c      |  46 +++++-
 drivers/net/wireless/realtek/rtw89/phy.h      |   1 +
 drivers/net/wireless/realtek/rtw89/reg.h      |  28 ++++
 drivers/net/wireless/realtek/rtw89/rtw8852c.c |   2 +-
 drivers/net/wireless/realtek/rtw89/rtw8922a.c |   2 +-
 drivers/net/wireless/realtek/rtw89/util.h     |  17 ++
 drivers/net/wireless/realtek/rtw89/wow.c      |   2 +
 drivers/net/wireless/realtek/rtw89/wow.h      |   7 -
 18 files changed, 386 insertions(+), 98 deletions(-)


base-commit: 039cd522dc70151da13329a5e3ae19b1736f468a
-- 
2.25.1


^ permalink raw reply

* [PATCH rtw-next 01/13] wifi: rtw89: mac: finish active TX immediately without waiting for DMAC
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

Currently active TX only finishes after ensuring PCIE and DMAC become idle.
However, the waiting time might be long. Since the packet is already
transmitted over the air, update the registers to finish active TX
immediately, regardless of the PCIE/DMAC status.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/mac_be.c |  3 +++
 drivers/net/wireless/realtek/rtw89/reg.h    | 28 +++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index dc66b1ee851a..ed1ea13bb98d 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -1173,6 +1173,9 @@ static int scheduler_init_be(struct rtw89_dev *rtwdev, u8 mac_idx)
 	if (ret)
 		return ret;
 
+	reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_MISC_1, mac_idx);
+	rtw89_write32_set(rtwdev, reg, B_BE_EN_TX_FINISH_PRD_RESP);
+
 	if (chip->chip_id == RTL8922D) {
 		reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_SCH_EXT_CTRL, mac_idx);
 		rtw89_write32_set(rtwdev, reg, B_BE_CWCNT_PLUS_MODE);
diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 9b605617c3f0..66ed847c9009 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -6745,6 +6745,34 @@
 #define B_BE_MUEDCA_EN_MASK GENMASK(1, 0)
 #define B_BE_MUEDCA_EN_0 BIT(0)
 
+#define R_BE_MISC_1 0x1037C
+#define R_BE_MISC_1_C1 0x1437C
+#define B_BE_PPS_REMAIN_TIME_MODE BIT(31)
+#define B_BE_PPS_IDLE_SORT_EN BIT(30)
+#define B_BE_SR_TXOP_USE_SR_PERIOD_EN BIT(29)
+#define B_BE_SCH_CCA_PIFS_CLK_GATING_DIS BIT(28)
+#define B_BE_SR_TXOP_EN BIT(27)
+#define B_BE_SCH_ABORT_CNT_SIFS_EN BIT(26)
+#define B_BE_SCH_ABORT_CNT_TB_EN BIT(25)
+#define B_BE_SCH_ABORT_CNT_CTN_EN BIT(24)
+#define B_BE_SR_CCA_PER20_BITMAP_EN BIT(23)
+#define B_BE_SR_CCA_S80_EN BIT(22)
+#define B_BE_SR_CCA_S40_EN BIT(21)
+#define B_BE_SR_CCA_S20_EN BIT(20)
+#define B_BE_EN_TX_FINISH_PRD_RESP BIT(18)
+#define B_BE_RESP_TX_ABORT_NON_IDLE BIT(17)
+#define B_BE_RESP_TX_ABORT_QUICK_EN BIT(16)
+#define B_BE_PREBKF_CHK_LINK_BUSY BIT(15)
+#define B_BE_SCH_MSD_PRD_RST_EDCA_EN BIT(14)
+#define B_BE_LINK_BUSY_RST_EDCA_EN_MASK GENMASK(13, 12)
+#define B_BE_RX_TSFT_SYNC_BYPASS_FCS BIT(11)
+#define B_BE_RX_TSFT_DIFF_THD_MASK GENMASK(10, 8)
+#define B_BE_CAL_TBTT_OV_EN BIT(5)
+#define B_BE_SUBBCN_MS_CNT_MODE BIT(3)
+#define B_BE_CAL_ALWAYS_EN BIT(2)
+#define B_BE_SIFS_TIMER_AUTO_RST_EN BIT(1)
+#define B_BE_CHK_HAS_SIFS_TX_ABORT BIT(0)
+
 #define R_BE_CTN_DRV_TXEN 0x10398
 #define R_BE_CTN_DRV_TXEN_C1 0x14398
 #define B_BE_CTN_TXEN_TWT_3 BIT(17)
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 02/13] wifi: rtw89: pci: update SER parameters for suspend/resume
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

In suspend mode, SER timer unit is different from normal mode. Set proper
value to prevent expected SER happened during suspend.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/pci.h    |  1 +
 drivers/net/wireless/realtek/rtw89/pci_be.c | 52 +++++++++++++++++++--
 2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h
index ccfa6d33623a..dc488d9a8884 100644
--- a/drivers/net/wireless/realtek/rtw89/pci.h
+++ b/drivers/net/wireless/realtek/rtw89/pci.h
@@ -1016,6 +1016,7 @@
 #define B_BE_PL1_IGNORE_HOT_RST BIT(30)
 #define B_BE_PL1_TIMER_UNIT_MASK GENMASK(19, 17)
 #define PCIE_SER_TIMER_UNIT 0x2
+#define PCIE_SER_WOW_TIMER_UNIT 0x4
 #define B_BE_PL1_TIMER_CLEAR BIT(0)
 
 #define R_BE_REG_PL1_MASK 0x34B0
diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c
index 114f40c6c31b..dea01d9e57fd 100644
--- a/drivers/net/wireless/realtek/rtw89/pci_be.c
+++ b/drivers/net/wireless/realtek/rtw89/pci_be.c
@@ -721,12 +721,24 @@ static int __maybe_unused rtw89_pci_suspend_be(struct device *dev)
 {
 	struct ieee80211_hw *hw = dev_get_drvdata(dev);
 	struct rtw89_dev *rtwdev = hw->priv;
+	u32 val32;
 
 	rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
 	rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_R_DIS_PRST);
 	rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
 	rtw89_write32_set(rtwdev, R_BE_PCIE_FRZ_CLK, B_BE_PCIE_FRZ_REG_RST);
-	rtw89_write32_clr(rtwdev, R_BE_REG_PL1_MASK, B_BE_SER_PM_MASTER_IMR);
+
+	val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL);
+	if (val32 & B_BE_PL1_SER_PL1_EN) {
+		val32 = u32_replace_bits(val32, PCIE_SER_WOW_TIMER_UNIT,
+					 B_BE_PL1_TIMER_UNIT_MASK);
+		rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32);
+
+		if (rtwdev->chip->chip_id == RTL8922A)
+			rtw89_write32_clr(rtwdev, R_BE_REG_PL1_MASK,
+					  B_BE_SER_PM_MASTER_IMR);
+	}
+
 	return 0;
 }
 
@@ -735,12 +747,19 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev)
 	struct ieee80211_hw *hw = dev_get_drvdata(dev);
 	struct rtw89_dev *rtwdev = hw->priv;
 	u32 polling;
+	u32 val32;
+	u16 val16;
 	int ret;
 
 	rtw89_write32_set(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
 	rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_R_DIS_PRST);
 	rtw89_write32_clr(rtwdev, R_BE_RSV_CTRL, B_BE_WLOCK_1C_BIT6);
 	rtw89_write32_clr(rtwdev, R_BE_PCIE_FRZ_CLK, B_BE_PCIE_FRZ_REG_RST);
+
+	val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL);
+	if (!(val32 & B_BE_PL1_SER_PL1_EN))
+		goto clear_phy_isr;
+
 	rtw89_write32_clr(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN);
 
 	ret = read_poll_timeout_atomic(rtw89_read32, polling, !polling, 1, 1000,
@@ -748,8 +767,35 @@ static int __maybe_unused rtw89_pci_resume_be(struct device *dev)
 	if (ret)
 		rtw89_warn(rtwdev, "[ERR] PCIE SER clear polling fail\n");
 
-	rtw89_write32_set(rtwdev, R_BE_SER_PL1_CTRL, B_BE_PL1_SER_PL1_EN);
-	rtw89_write32_set(rtwdev, R_BE_REG_PL1_MASK, B_BE_SER_PM_MASTER_IMR);
+	if (rtwdev->chip->chip_id == RTL8922A)
+		rtw89_write32_set(rtwdev, R_BE_REG_PL1_MASK,
+				  B_BE_SER_PM_MASTER_IMR | B_BE_SER_PCLKREQ_ACK_MASK);
+
+	val32 = rtw89_read32(rtwdev, R_BE_SER_PL1_CTRL);
+	val32 = u32_replace_bits(val32, PCIE_SER_TIMER_UNIT, B_BE_PL1_TIMER_UNIT_MASK);
+	val32 |= B_BE_PL1_SER_PL1_EN;
+	rtw89_write32(rtwdev, R_BE_SER_PL1_CTRL, val32);
+
+clear_phy_isr:
+	if (rtwdev->chip->chip_id == RTL8922D) {
+		val16 = rtw89_read16(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
+					     RAC_ANA41 * RAC_MULT);
+		if (val16 & PHY_ERR_FLAG_EN) {
+			rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
+						  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
+			rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G2 +
+						  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
+		}
+
+		val16 = rtw89_read16(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
+					     RAC_ANA41 * RAC_MULT);
+		if (val16 & PHY_ERR_FLAG_EN) {
+			rtw89_write16_clr(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
+						  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
+			rtw89_write16_set(rtwdev, RAC_DIRECT_OFFESET_L0_G1 +
+						  RAC_ANA41 * RAC_MULT, PHY_ERR_FLAG_EN);
+		}
+	}
 
 	rtw89_pci_basic_cfg(rtwdev, true);
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 03/13] wifi: rtw89: mac: remove A-die off setting for RTL8852C and RTL8922A
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

Fix timing issue of A-die off followed by XTAL off. Otherwise, device might
get lost potentially.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/rtw8852c.c | 2 +-
 drivers/net/wireless/realtek/rtw89/rtw8922a.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
index e62a7288c8aa..7ea0a8905282 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c
@@ -463,7 +463,7 @@ static int rtw8852c_pwr_off_func(struct rtw89_dev *rtwdev)
 	else if (rtwdev->hci.type == RTW89_HCI_TYPE_USB)
 		rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_SOP_EDSWR);
 
-	rtw89_write32_set(rtwdev, R_AX_SYS_PW_CTRL, B_AX_XTAL_OFF_A_DIE);
+	rtw89_write32_clr(rtwdev, R_AX_SYS_PW_CTRL, B_AX_XTAL_OFF_A_DIE);
 	rtw89_write32_set(rtwdev, R_AX_SYS_SWR_CTRL1, B_AX_SYM_CTRL_SPS_PWMFREQ);
 	rtw89_write32_mask(rtwdev, R_AX_SPS_DIG_ON_CTRL0,
 			   B_AX_REG_ZCDC_H_MASK, 0x3);
diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
index 36ef36110602..a489aaf90f23 100644
--- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c
+++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c
@@ -492,7 +492,7 @@ static int rtw8922a_pwr_off_func(struct rtw89_dev *rtwdev)
 		return ret;
 
 	rtw89_write32(rtwdev, R_BE_WLLPS_CTRL, 0x0000A1B2);
-	rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE);
+	rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_XTAL_OFF_A_DIE);
 	rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS);
 	rtw89_write32(rtwdev, R_BE_UDM1, 0);
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 04/13] wifi: rtw89: phy: limit AMPDU number for RA try rate
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

When RA (Rate Adaptive) does try higher rate, a TRY bit is flagged, and
hardware will reference registers configured by this patch as maximum
number of AMPDU. To prevent aggregate too many MPDU over peer's capability
causing loss in peer side, set the minimum values across all stations and
TID since there is single one register per hardware band. Consider MLD
case, a BA session can run across two hardware bands, so set the same
value as well.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/mac.c      |  2 ++
 drivers/net/wireless/realtek/rtw89/mac.h      |  1 +
 drivers/net/wireless/realtek/rtw89/mac80211.c |  3 +++
 drivers/net/wireless/realtek/rtw89/mac_be.c   |  2 ++
 drivers/net/wireless/realtek/rtw89/phy.c      | 27 +++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/phy.h      |  1 +
 6 files changed, 36 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 234928613ef4..a292a14394b2 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -7319,6 +7319,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {
 	},
 	.wow_ctrl = {.addr = R_AX_WOW_CTRL, .mask = B_AX_WOW_WOWEN,},
 	.agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT, .mask = B_AX_AMPDU_MAX_TIME_MASK,},
+	.ra_agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT,
+			 .mask = B_AX_RA_TRY_RATE_AGG_LMT_MASK,},
 	.txcnt_limit = {.addr = R_AX_TXCNT, .mask = B_AX_L_TXCNT_LMT_MASK,},
 
 	.check_mac_en = rtw89_mac_check_mac_en_ax,
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index e6b715b95409..9c77bfaa34ee 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -1037,6 +1037,7 @@ struct rtw89_mac_gen_def {
 	struct rtw89_reg_def narrow_bw_ru_dis;
 	struct rtw89_reg_def wow_ctrl;
 	struct rtw89_reg_def agg_limit;
+	struct rtw89_reg_def ra_agg_limit;
 	struct rtw89_reg_def txcnt_limit;
 
 	int (*check_mac_en)(struct rtw89_dev *rtwdev, u8 band,
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 1ef73bfc40d1..cd8e2c8de888 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -1005,6 +1005,8 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw,
 		clear_bit(tid, rtwsta->ampdu_map);
 		rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta);
 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		rtw89_leave_ps_mode(rtwdev);
+		rtw89_phy_ra_recalc_agg_limit(rtwdev);
 		break;
 	case IEEE80211_AMPDU_TX_OPERATIONAL:
 		set_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags);
@@ -1013,6 +1015,7 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw,
 		set_bit(tid, rtwsta->ampdu_map);
 		rtw89_leave_ps_mode(rtwdev);
 		rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta);
+		rtw89_phy_ra_recalc_agg_limit(rtwdev);
 		break;
 	case IEEE80211_AMPDU_RX_START:
 		rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params);
diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c
index ed1ea13bb98d..b9e8f4fa366b 100644
--- a/drivers/net/wireless/realtek/rtw89/mac_be.c
+++ b/drivers/net/wireless/realtek/rtw89/mac_be.c
@@ -3196,6 +3196,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {
 	},
 	.wow_ctrl = {.addr = R_BE_WOW_CTRL, .mask = B_BE_WOW_WOWEN,},
 	.agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT, .mask = B_BE_AMPDU_MAX_TIME_MASK,},
+	.ra_agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT,
+			 .mask = B_BE_RA_TRY_RATE_AGG_LMT_MASK,},
 	.txcnt_limit = {.addr = R_BE_TXCNT, .mask = B_BE_L_TXCNT_LMT_MASK,},
 
 	.check_mac_en = rtw89_mac_check_mac_en_be,
diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index 74f5d5562848..0fa4d8d791f1 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -775,6 +775,33 @@ void rtw89_phy_ra_assoc(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_
 	rtw89_fw_h2c_ra(rtwdev, ra, csi);
 }
 
+void rtw89_phy_ra_recalc_agg_limit(struct rtw89_dev *rtwdev)
+{
+	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
+	const struct rtw89_reg_def *ra_limit = &mac->ra_agg_limit;
+	struct ieee80211_sta *sta;
+	struct rtw89_sta *rtwsta;
+	u16 agg_num = U16_MAX;
+	u8 tid;
+
+	for_each_station(sta, rtwdev->hw) {
+		rtwsta = sta_to_rtwsta(sta);
+
+		for_each_set_bit(tid, rtwsta->ampdu_map, IEEE80211_NUM_TIDS)
+			agg_num = min(agg_num, rtwsta->ampdu_params[tid].agg_num);
+	}
+
+	if (agg_num == U16_MAX)
+		agg_num = 0x3F;
+	else
+		agg_num = clamp(agg_num, 1, 256) - 1;
+
+	rtw89_write32_idx(rtwdev, ra_limit->addr, ra_limit->mask, agg_num, RTW89_MAC_0);
+	if (!rtwdev->dbcc_en)
+		return;
+	rtw89_write32_idx(rtwdev, ra_limit->addr, ra_limit->mask, agg_num, RTW89_MAC_1);
+}
+
 u8 rtw89_phy_get_txsc(struct rtw89_dev *rtwdev,
 		      const struct rtw89_chan *chan,
 		      enum rtw89_bandwidth dbw)
diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h
index 094c7e45f254..bde419edf744 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.h
+++ b/drivers/net/wireless/realtek/rtw89/phy.h
@@ -1006,6 +1006,7 @@ void rtw89_phy_ra_update_sta(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta
 void rtw89_phy_ra_update_sta_link(struct rtw89_dev *rtwdev,
 				  struct rtw89_sta_link *rtwsta_link,
 				  u32 changed);
+void rtw89_phy_ra_recalc_agg_limit(struct rtw89_dev *rtwdev);
 void rtw89_phy_rate_pattern_vif(struct rtw89_dev *rtwdev,
 				struct ieee80211_vif *vif,
 				const struct cfg80211_bitrate_mask *mask);
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 05/13] wifi: rtw89: move disabling dynamic mechanism functions to core
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Zong-Zhe Yang <kevin_yang@realtek.com>

Some dynamic mechanism (DM) may need to be disabled during some normal
processes rather than debugging. For example, should not do MLSR switch
during SCAN/ROC or even MCC. So, move the disabling DM functions to core
for impending uses.

No logic changes.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c  | 29 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/core.h  |  3 ++
 drivers/net/wireless/realtek/rtw89/debug.c | 35 ++--------------------
 3 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 9d9b91570989..81004ef18168 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -4713,6 +4713,35 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work)
 		rtw89_enter_lps_track(rtwdev);
 }
 
+void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new)
+{
+	struct rtw89_hal *hal = &rtwdev->hal;
+	u32 old = hal->disabled_dm_bitmap;
+
+	if (new == old)
+		return;
+
+	hal->disabled_dm_bitmap = new;
+
+	rtw89_debug(rtwdev, RTW89_DBG_STATE, "Disable DM: 0x%x -> 0x%x\n", old, new);
+}
+
+void rtw89_core_dm_disable_set(struct rtw89_dev *rtwdev, enum rtw89_dm_type type)
+{
+	struct rtw89_hal *hal = &rtwdev->hal;
+	u32 cur = hal->disabled_dm_bitmap;
+
+	rtw89_core_dm_disable_cfg(rtwdev, cur | BIT(type));
+}
+
+void rtw89_core_dm_disable_clr(struct rtw89_dev *rtwdev, enum rtw89_dm_type type)
+{
+	struct rtw89_hal *hal = &rtwdev->hal;
+	u32 cur = hal->disabled_dm_bitmap;
+
+	rtw89_core_dm_disable_cfg(rtwdev, cur & ~BIT(type));
+}
+
 u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size)
 {
 	unsigned long bit;
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index cf0cc718f41c..05f8ad6d3034 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -7862,5 +7862,8 @@ void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev,
 void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event);
 int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif,
 			   unsigned int link_id);
+void rtw89_core_dm_disable_cfg(struct rtw89_dev *rtwdev, u32 new);
+void rtw89_core_dm_disable_set(struct rtw89_dev *rtwdev, enum rtw89_dm_type type);
+void rtw89_core_dm_disable_clr(struct rtw89_dev *rtwdev, enum rtw89_dm_type type);
 
 #endif
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index 3e16ed2c4570..aee0f25e036a 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -4333,35 +4333,6 @@ static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev,
 	return p - buf;
 }
 
-static void rtw89_debug_disable_dm_cfg_bmap(struct rtw89_dev *rtwdev, u32 new)
-{
-	struct rtw89_hal *hal = &rtwdev->hal;
-	u32 old = hal->disabled_dm_bitmap;
-
-	if (new == old)
-		return;
-
-	hal->disabled_dm_bitmap = new;
-
-	rtw89_debug(rtwdev, RTW89_DBG_STATE, "Disable DM: 0x%x -> 0x%x\n", old, new);
-}
-
-static void rtw89_debug_disable_dm_set_flag(struct rtw89_dev *rtwdev, u8 flag)
-{
-	struct rtw89_hal *hal = &rtwdev->hal;
-	u32 cur = hal->disabled_dm_bitmap;
-
-	rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur | BIT(flag));
-}
-
-static void rtw89_debug_disable_dm_clr_flag(struct rtw89_dev *rtwdev, u8 flag)
-{
-	struct rtw89_hal *hal = &rtwdev->hal;
-	u32 cur = hal->disabled_dm_bitmap;
-
-	rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur & ~BIT(flag));
-}
-
 #define DM_INFO(type) {RTW89_DM_ ## type, #type}
 
 static const struct rtw89_disabled_dm_info {
@@ -4412,7 +4383,7 @@ rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev,
 	if (ret)
 		return -EINVAL;
 
-	rtw89_debug_disable_dm_cfg_bmap(rtwdev, conf);
+	rtw89_core_dm_disable_cfg(rtwdev, conf);
 
 	return count;
 }
@@ -4475,7 +4446,7 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev,
 	if (num != 2)
 		return -EINVAL;
 
-	rtw89_debug_disable_dm_set_flag(rtwdev, RTW89_DM_MLO);
+	rtw89_core_dm_disable_set(rtwdev, RTW89_DM_MLO);
 
 	rtw89_debug(rtwdev, RTW89_DBG_STATE, "Set MLO mode to %x\n", mlo_mode);
 
@@ -4485,7 +4456,7 @@ rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev,
 		break;
 	default:
 		rtw89_debug(rtwdev, RTW89_DBG_STATE, "Unsupported MLO mode\n");
-		rtw89_debug_disable_dm_clr_flag(rtwdev, RTW89_DM_MLO);
+		rtw89_core_dm_disable_clr(rtwdev, RTW89_DM_MLO);
 
 		return -EOPNOTSUPP;
 	}
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 06/13] wifi: rtw89: tweak settings of TX power and channel for Wi-Fi 7
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Zong-Zhe Yang <kevin_yang@realtek.com>

The support_mlo flag depends on FW features, so it's determined at runtime.
Since Wi-Fi 7 chip now needs to initialize second HW band, if support_mlo
is not allowed, second HW band might act without settings of TX power and
channel. So, set that for Wi-Fi 7 chip.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 81004ef18168..6ab99a3577f3 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -463,7 +463,7 @@ void rtw89_core_set_chip_txpwr(struct rtw89_dev *rtwdev)
 	chan = rtw89_mgnt_chan_get(rtwdev, 0);
 	__rtw89_core_set_chip_txpwr(rtwdev, chan, RTW89_PHY_0);
 
-	if (!rtwdev->support_mlo)
+	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX)
 		return;
 
 	chan = rtw89_mgnt_chan_get(rtwdev, 1);
@@ -558,7 +558,7 @@ int rtw89_set_channel(struct rtw89_dev *rtwdev)
 	chan = rtw89_mgnt_chan_get(rtwdev, 0);
 	__rtw89_set_channel(rtwdev, chan, RTW89_MAC_0, RTW89_PHY_0);
 
-	if (!rtwdev->support_mlo)
+	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX)
 		return 0;
 
 	chan = rtw89_mgnt_chan_get(rtwdev, 1);
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 07/13] wifi: rtw89: chan: simplify link handling related to ROC
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Zong-Zhe Yang <kevin_yang@realtek.com>

The original channel is swapped out for the target channel during ROC.
And, all vifs/links accessing the original channel will be marked with
off-channel. So, it doesn't seem necessary for chan.c to determine which
link instance it is.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/chan.c | 11 -----------
 drivers/net/wireless/realtek/rtw89/core.h |  1 -
 2 files changed, 12 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c
index 9b2f6f0a00fd..def9e4f3af59 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.c
+++ b/drivers/net/wireless/realtek/rtw89/chan.c
@@ -276,7 +276,6 @@ void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev,
 		}
 
 		hal->roc_chandef = *chandef;
-		hal->roc_link_index = rtw89_vif_link_inst_get_index(rtwvif_link);
 	} else {
 		cur = atomic_cmpxchg(&hal->roc_chanctx_idx, idx,
 				     RTW89_CHANCTX_IDLE);
@@ -389,7 +388,6 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
 	struct rtw89_hal *hal = &rtwdev->hal;
 	struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
 	enum rtw89_chanctx_idx chanctx_idx;
-	enum rtw89_chanctx_idx roc_idx;
 	enum rtw89_entity_mode mode;
 	u8 role_index;
 
@@ -419,15 +417,6 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
 	if (chanctx_idx == RTW89_CHANCTX_IDLE)
 		goto dflt;
 
-	roc_idx = atomic_read(&hal->roc_chanctx_idx);
-	if (roc_idx != RTW89_CHANCTX_IDLE) {
-		/* ROC is ongoing (given ROC runs on @hal->roc_link_index).
-		 * If @link_index is the same, get the ongoing ROC chanctx.
-		 */
-		if (link_index == hal->roc_link_index)
-			chanctx_idx = roc_idx;
-	}
-
 	return rtw89_chan_get(rtwdev, chanctx_idx);
 
 dflt:
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 05f8ad6d3034..01573150ab3c 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -5170,7 +5170,6 @@ struct rtw89_hal {
 	bool no_eht;
 
 	atomic_t roc_chanctx_idx;
-	u8 roc_link_index;
 
 	DECLARE_BITMAP(changes, NUM_OF_RTW89_CHANCTX_CHANGES);
 	DECLARE_BITMAP(entity_map, NUM_OF_RTW89_CHANCTX);
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 08/13] wifi: rtw89: chan: recalc MLO DBCC mode based on current entity mode
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Zong-Zhe Yang <kevin_yang@realtek.com>

Since MLD vif can do MLSR switch, it may not always run on HW band 0.
But when preparing MCC for MLD + P2P, P2P vif needs to use HW band 0
to handle connection, i.e. uses of HW bands may be different by vif.

The current major role/vif can be indicated through entity mode. So,
based on it, recalculate MLO DBCC mode to change use of HW band.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/chan.c | 61 +++++++++++++++--------
 1 file changed, 41 insertions(+), 20 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c
index def9e4f3af59..ceb399fc2b94 100644
--- a/drivers/net/wireless/realtek/rtw89/chan.c
+++ b/drivers/net/wireless/realtek/rtw89/chan.c
@@ -381,6 +381,23 @@ static void rtw89_normalize_link_chanctx(struct rtw89_dev *rtwdev,
 	rtw89_swap_chanctx(rtwdev, rtwvif_link->chanctx_idx, cur->chanctx_idx);
 }
 
+static u8 rtw89_entity_role_get_index(struct rtw89_dev *rtwdev)
+{
+	enum rtw89_entity_mode mode;
+
+	mode = rtw89_get_entity_mode(rtwdev);
+	switch (mode) {
+	default:
+		WARN(1, "Invalid ent mode: %d\n", mode);
+		fallthrough;
+	case RTW89_ENTITY_MODE_SCC_OR_SMLD:
+	case RTW89_ENTITY_MODE_MCC:
+		return 0;
+	case RTW89_ENTITY_MODE_MCC_PREPARE:
+		return 1;
+	}
+}
+
 const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
 					       const char *caller_message,
 					       u8 link_index, bool nullchk)
@@ -388,7 +405,6 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
 	struct rtw89_hal *hal = &rtwdev->hal;
 	struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
 	enum rtw89_chanctx_idx chanctx_idx;
-	enum rtw89_entity_mode mode;
 	u8 role_index;
 
 	lockdep_assert_wiphy(rtwdev->hw->wiphy);
@@ -399,19 +415,7 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,
 		goto dflt;
 	}
 
-	mode = rtw89_get_entity_mode(rtwdev);
-	switch (mode) {
-	case RTW89_ENTITY_MODE_SCC_OR_SMLD:
-	case RTW89_ENTITY_MODE_MCC:
-		role_index = 0;
-		break;
-	case RTW89_ENTITY_MODE_MCC_PREPARE:
-		role_index = 1;
-		break;
-	default:
-		WARN(1, "Invalid ent mode: %d\n", mode);
-		goto dflt;
-	}
+	role_index = rtw89_entity_role_get_index(rtwdev);
 
 	chanctx_idx = mgnt->chanctx_tbl[role_index][link_index];
 	if (chanctx_idx == RTW89_CHANCTX_IDLE)
@@ -479,10 +483,28 @@ rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws)
 	}
 }
 
-static
-void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws)
+static void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev)
 {
+	struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt;
 	enum rtw89_mlo_dbcc_mode mode;
+	struct rtw89_vif *role;
+	u8 active_hws = 0;
+	u8 ridx;
+
+	ridx = rtw89_entity_role_get_index(rtwdev);
+	role = mgnt->active_roles[ridx];
+	if (role) {
+		struct rtw89_vif_link *link;
+		int i;
+
+		for (i = 0; i < role->links_inst_valid_num; i++) {
+			link = rtw89_vif_get_link_inst(role, i);
+			if (!link || !link->chanctx_assigned)
+				continue;
+
+			active_hws |= BIT(i);
+		}
+	}
 
 	mode = rtw89_entity_sel_mlo_dbcc_mode(rtwdev, active_hws);
 	rtwdev->mlo_dbcc_mode = mode;
@@ -496,7 +518,6 @@ static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev)
 	struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
 	struct rtw89_vif_link *link;
 	struct rtw89_vif *role;
-	u8 active_hws = 0;
 	u8 pos = 0;
 	int i, j;
 
@@ -545,13 +566,10 @@ static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev)
 				continue;
 
 			mgnt->chanctx_tbl[pos][i] = link->chanctx_idx;
-			active_hws |= BIT(i);
 		}
 
 		mgnt->active_roles[pos++] = role;
 	}
-
-	rtw89_entity_recalc_mlo_dbcc_mode(rtwdev, active_hws);
 }
 
 enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)
@@ -621,6 +639,9 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev)
 		return rtw89_get_entity_mode(rtwdev);
 
 	rtw89_set_entity_mode(rtwdev, mode);
+
+	rtw89_entity_recalc_mlo_dbcc_mode(rtwdev);
+
 	return mode;
 }
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 09/13] wifi: rtw89: wow: add retry for ensuring packet are processed
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Chin-Yen Lee <timlee@realtek.com>

Before entering WoWLAN mode, the driver must ensure that
all received packets have been processed to prevent packet
loss. Consequently, a retry mechanism has been implemented
to guarantee completion.

Signed-off-by: Chin-Yen Lee <timlee@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/mac.c | 15 ++++++++++++++-
 drivers/net/wireless/realtek/rtw89/mac.h |  1 +
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index a292a14394b2..35fd18fe6470 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -7183,7 +7183,7 @@ int rtw89_mac_ptk_drop_by_band_and_wait(struct rtw89_dev *rtwdev,
 	return ret;
 }
 
-int rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable)
+static int _rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable)
 {
 	struct rtw89_mac_h2c_info h2c_info = {};
 	struct rtw89_mac_c2h_info c2h_info = {};
@@ -7206,6 +7206,19 @@ int rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable)
 	return ret;
 }
 
+int rtw89_mac_cpu_io_rx(struct rtw89_dev *rtwdev, bool wow_enable)
+{
+	int i, ret;
+
+	for (i = 0; i < CPU_IO_RX_RETRY_CNT; i++) {
+		ret = _rtw89_mac_cpu_io_rx(rtwdev, wow_enable);
+		if (!ret)
+			return 0;
+	}
+
+	return ret;
+}
+
 static int rtw89_wow_config_mac_ax(struct rtw89_dev *rtwdev, bool enable_wow)
 {
 	const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h
index 9c77bfaa34ee..88a877556cb3 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.h
+++ b/drivers/net/wireless/realtek/rtw89/mac.h
@@ -17,6 +17,7 @@
 #define BSSID_CAM_ENT_SIZE 0x08
 #define HFC_PAGE_UNIT 64
 #define RPWM_TRY_CNT 3
+#define CPU_IO_RX_RETRY_CNT 3
 
 enum rtw89_mac_hwmod_sel {
 	RTW89_DMAC_SEL = 0,
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 10/13] wifi: rtw89: replace RF mutex with wiphy lock assertion
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Zong-Zhe Yang <kevin_yang@realtek.com>

Now, stack has introduced wiphy lock. And, the normal paths calling
RF read/write should be under wiphy lock. So, replace RF mutex with
wiphy lock assertion. Besides, in dbgfs paths, add the corresponding
lock option.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c  |  2 --
 drivers/net/wireless/realtek/rtw89/core.h  | 14 ++++----------
 drivers/net/wireless/realtek/rtw89/debug.c |  6 +++---
 3 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 6ab99a3577f3..18dbf3664f0a 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -6147,7 +6147,6 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
 		return -ENOMEM;
 	spin_lock_init(&rtwdev->ba_lock);
 	spin_lock_init(&rtwdev->rpwm_lock);
-	mutex_init(&rtwdev->rf_mutex);
 	rtwdev->total_sta_assoc = 0;
 
 	rtw89_init_wait(&rtwdev->mcc.wait);
@@ -6206,7 +6205,6 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev)
 	__rtw89_fw_free_all_early_h2c(rtwdev);
 
 	destroy_workqueue(rtwdev->txq_wq);
-	mutex_destroy(&rtwdev->rf_mutex);
 }
 EXPORT_SYMBOL(rtw89_core_deinit);
 
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index 01573150ab3c..ce04ecaa3a5e 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -6182,8 +6182,6 @@ struct rtw89_dev {
 	refcount_t refcount_ap_info;
 
 	struct list_head rtwvifs_list;
-	/* used to protect rf read write */
-	struct mutex rf_mutex;
 	struct workqueue_struct *txq_wq;
 	struct work_struct txq_work;
 	struct delayed_work txq_reinvoke_work;
@@ -6808,22 +6806,18 @@ static inline u32
 rtw89_read_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
 	      u32 addr, u32 mask)
 {
-	u32 val;
-
-	mutex_lock(&rtwdev->rf_mutex);
-	val = rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask);
-	mutex_unlock(&rtwdev->rf_mutex);
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
 
-	return val;
+	return rtwdev->chip->ops->read_rf(rtwdev, rf_path, addr, mask);
 }
 
 static inline void
 rtw89_write_rf(struct rtw89_dev *rtwdev, enum rtw89_rf_path rf_path,
 	       u32 addr, u32 mask, u32 data)
 {
-	mutex_lock(&rtwdev->rf_mutex);
+	lockdep_assert_wiphy(rtwdev->hw->wiphy);
+
 	rtwdev->chip->ops->write_rf(rtwdev, rf_path, addr, mask, data);
-	mutex_unlock(&rtwdev->rf_mutex);
 }
 
 static inline u32 rtw89_read32_pci_cfg(struct rtw89_dev *rtwdev, u32 addr)
diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index aee0f25e036a..d461ffc6dc9e 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -4859,9 +4859,9 @@ rtw89_debug_priv_beacon_info_get(struct rtw89_dev *rtwdev,
 static const struct rtw89_debugfs rtw89_debugfs_templ = {
 	.read_reg = rtw89_debug_priv_select_and_get(read_reg),
 	.write_reg = rtw89_debug_priv_set(write_reg),
-	.read_rf = rtw89_debug_priv_select_and_get(read_rf),
-	.write_rf = rtw89_debug_priv_set(write_rf),
-	.rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K),
+	.read_rf = rtw89_debug_priv_select_and_get(read_rf, RLOCK),
+	.write_rf = rtw89_debug_priv_set(write_rf, WLOCK),
+	.rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K, RLOCK),
 	.txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K, RLOCK),
 	.mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump, RSIZE_128K),
 	.mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K, RLOCK),
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 11/13] wifi: rtw89: Drop malformed AMPDU frames with abnormal PN
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Po-Hao Huang <phhuang@realtek.com>

Fix connection issue caused by AMPDU frames with abnormal PN patterns
(out-of-order packets with correct MPDU sequence numbers but paired
with abnormal PN values, which is next PN of previous in-order packet).
This is causing packet drops, low throughput and disconnections. It is
observed in fields with some specific AP firmwares. Do this workaround for
better interoperability since some APs could never receive a proper FW
update.

Signed-off-by: Po-Hao Huang <phhuang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/core.c     | 115 ++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/core.h     |  11 ++
 drivers/net/wireless/realtek/rtw89/mac80211.c |   3 +
 drivers/net/wireless/realtek/rtw89/util.h     |  17 +++
 drivers/net/wireless/realtek/rtw89/wow.c      |   2 +
 drivers/net/wireless/realtek/rtw89/wow.h      |   7 --
 6 files changed, 148 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 18dbf3664f0a..9d3f651798ff 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -3272,6 +3272,114 @@ static void rtw89_core_correct_mcc_chan(struct rtw89_dev *rtwdev,
 	rcu_read_unlock();
 }
 
+static void __rtw89_core_tid_rx_stats_reset(struct rtw89_tid_stats *tid_stats)
+{
+	tid_stats->last_pn = -1LL;
+	tid_stats->last_sn = IEEE80211_SN_MASK;
+}
+
+void rtw89_core_tid_rx_stats_ctrl(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
+				  struct ieee80211_ampdu_params *params, bool enable)
+{
+	struct rtw89_tid_stats *tid_stats;
+	u16 tid = params->tid;
+
+	tid_stats = &rtwsta->tid_rx_stats[tid];
+
+	if (enable) {
+		__rtw89_core_tid_rx_stats_reset(tid_stats);
+		tid_stats->started = true;
+	} else {
+		tid_stats->started = false;
+	}
+}
+
+void rtw89_core_tid_rx_stats_reset(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_tid_stats *tid_stats;
+	struct ieee80211_sta *sta;
+	struct rtw89_sta *rtwsta;
+	u16 tid;
+
+	for_each_station(sta, rtwdev->hw) {
+		rtwsta = sta_to_rtwsta(sta);
+
+		for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) {
+			tid_stats = &rtwsta->tid_rx_stats[tid];
+
+			if (!tid_stats->started)
+				continue;
+
+			__rtw89_core_tid_rx_stats_reset(tid_stats);
+		}
+	}
+}
+
+static bool rtw89_core_skb_pn_valid(struct rtw89_dev *rtwdev,
+				    struct rtw89_rx_desc_info *desc_info,
+				    struct sk_buff *skb)
+{
+	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	struct rtw89_sta_link *rtwsta_link;
+	struct rtw89_tid_stats *tid_stats;
+	struct rtw89_sta *rtwsta;
+	u8 tid, *ccmp_hdr_ptr;
+	s64 pn, last_pn;
+	u16 mpdu_sn;
+	int hdrlen;
+
+	if (chip->chip_gen != RTW89_CHIP_AX)
+		return true;
+
+	if (!ieee80211_is_data_qos(hdr->frame_control))
+		return true;
+
+	if (!desc_info->hw_dec || !desc_info->addr1_match)
+		return true;
+
+	guard(rcu)();
+
+	rtwsta_link = rtw89_assoc_link_rcu_dereference(rtwdev, desc_info->mac_id);
+	if (!rtwsta_link)
+		return true;
+
+	rtwsta = rtwsta_link->rtwsta;
+	tid = ieee80211_get_tid(hdr);
+	tid_stats = &rtwsta->tid_rx_stats[tid];
+
+	if (!tid_stats->started)
+		return true;
+
+	switch (desc_info->sec_type) {
+	case RTW89_SEC_KEY_TYPE_CCMP128:
+	case RTW89_SEC_KEY_TYPE_CCMP256:
+	case RTW89_SEC_KEY_TYPE_GCMP128:
+	case RTW89_SEC_KEY_TYPE_GCMP256:
+		mpdu_sn = ieee80211_get_sn(hdr);
+		hdrlen = ieee80211_hdrlen(hdr->frame_control);
+		ccmp_hdr_ptr = skb->data + hdrlen;
+		ccmp_hdr2pn(&pn, ccmp_hdr_ptr);
+		last_pn = tid_stats->last_pn;
+
+		if (pn > last_pn) {
+			if (ieee80211_sn_less(mpdu_sn, tid_stats->last_sn)) {
+				dev_kfree_skb_any(skb);
+
+				return false;
+			}
+
+			tid_stats->last_sn = mpdu_sn;
+			tid_stats->last_pn = pn;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
 static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev,
 				      struct rtw89_rx_phy_ppdu *phy_ppdu,
 				      struct rtw89_rx_desc_info *desc_info,
@@ -3421,6 +3529,7 @@ void rtw89_core_query_rxdesc(struct rtw89_dev *rtwdev,
 	desc_info->sec_cam_id = le32_get_bits(rxd_l->dword5, AX_RXD_SEC_CAM_IDX_MASK);
 	desc_info->mac_id = le32_get_bits(rxd_l->dword5, AX_RXD_MAC_ID_MASK);
 	desc_info->rx_pl_id = le32_get_bits(rxd_l->dword5, AX_RXD_RX_PL_ID_MASK);
+	desc_info->sec_type = le32_get_bits(rxd_l->dword7, AX_RXD_SEC_TYPE_MASK);
 }
 EXPORT_SYMBOL(rtw89_core_query_rxdesc);
 
@@ -3450,6 +3559,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev,
 	desc_info->mac_id = le32_get_bits(rxd_s->dword2, BE_RXD_MAC_ID_MASK);
 	desc_info->addr_cam_valid = le32_get_bits(rxd_s->dword2, BE_RXD_ADDR_CAM_VLD);
 
+	desc_info->sec_type = le32_get_bits(rxd_s->dword3, BE_RXD_SEC_TYPE_MASK);
 	desc_info->icv_err = le32_get_bits(rxd_s->dword3, BE_RXD_ICV_ERR);
 	desc_info->crc32_err = le32_get_bits(rxd_s->dword3, BE_RXD_CRC32_ERR);
 	desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC);
@@ -3523,6 +3633,7 @@ void rtw89_core_query_rxdesc_v3(struct rtw89_dev *rtwdev,
 	desc_info->mac_id = le32_get_bits(rxd_s->dword2, BE_RXD_MAC_ID_V1);
 	desc_info->addr_cam_valid = le32_get_bits(rxd_s->dword2, BE_RXD_ADDR_CAM_VLD);
 
+	desc_info->sec_type = le32_get_bits(rxd_s->dword3, BE_RXD_SEC_TYPE_MASK);
 	desc_info->icv_err = le32_get_bits(rxd_s->dword3, BE_RXD_ICV_ERR);
 	desc_info->crc32_err = le32_get_bits(rxd_s->dword3, BE_RXD_CRC32_ERR);
 	desc_info->hw_dec = le32_get_bits(rxd_s->dword3, BE_RXD_HW_DEC);
@@ -3802,6 +3913,10 @@ void rtw89_core_rx(struct rtw89_dev *rtwdev,
 	memset(rx_status, 0, sizeof(*rx_status));
 	rtw89_core_update_rx_status(rtwdev, skb, desc_info, rx_status);
 	rtw89_core_rx_pkt_hdl(rtwdev, skb, desc_info);
+
+	if (!rtw89_core_skb_pn_valid(rtwdev, desc_info, skb))
+		return;
+
 	if (desc_info->long_rxdesc &&
 	    BIT(desc_info->frame_type) & PPDU_FILTER_BITMAP)
 		skb_queue_tail(&ppdu_sts->rx_queue[band], skb);
diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h
index ce04ecaa3a5e..94e4faf70e12 100644
--- a/drivers/net/wireless/realtek/rtw89/core.h
+++ b/drivers/net/wireless/realtek/rtw89/core.h
@@ -1126,6 +1126,7 @@ struct rtw89_rx_desc_info {
 	bool addr_cam_valid;
 	u8 addr_cam_id;
 	u8 sec_cam_id;
+	u8 sec_type;
 	u8 mac_id;
 	u16 offset;
 	u16 rxd_len;
@@ -6153,6 +6154,12 @@ struct rtw89_beacon_track_info {
 	u32 tbtt_diff_th;
 };
 
+struct rtw89_tid_stats {
+	s64 last_pn;
+	u16 last_sn;
+	bool started;
+};
+
 struct rtw89_dev {
 	struct ieee80211_hw *hw;
 	struct device *dev;
@@ -6359,6 +6366,7 @@ struct rtw89_sta {
 	struct sk_buff_head roc_queue;
 
 	struct rtw89_ampdu_params ampdu_params[IEEE80211_NUM_TIDS];
+	struct rtw89_tid_stats tid_rx_stats[IEEE80211_NUM_TIDS];
 	DECLARE_BITMAP(ampdu_map, IEEE80211_NUM_TIDS);
 
 	DECLARE_BITMAP(pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM);
@@ -7769,6 +7777,9 @@ int rtw89_core_sta_link_remove(struct rtw89_dev *rtwdev,
 void rtw89_core_set_tid_config(struct rtw89_dev *rtwdev,
 			       struct ieee80211_sta *sta,
 			       struct cfg80211_tid_config *tid_config);
+void rtw89_core_tid_rx_stats_ctrl(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta,
+				  struct ieee80211_ampdu_params *params, bool enable);
+void rtw89_core_tid_rx_stats_reset(struct rtw89_dev *rtwdev);
 void rtw89_core_rfkill_poll(struct rtw89_dev *rtwdev, bool force);
 void rtw89_check_quirks(struct rtw89_dev *rtwdev, const struct dmi_system_id *quirks);
 int rtw89_core_init(struct rtw89_dev *rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index cd8e2c8de888..501c3af1da01 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -964,6 +964,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 			rtw89_err(rtwdev, "failed to add key to sec cam\n");
 			return ret;
 		}
+		rtw89_core_tid_rx_stats_reset(rtwdev);
 		break;
 	case DISABLE_KEY:
 		flush_work(&rtwdev->txq_work);
@@ -1018,9 +1019,11 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw,
 		rtw89_phy_ra_recalc_agg_limit(rtwdev);
 		break;
 	case IEEE80211_AMPDU_RX_START:
+		rtw89_core_tid_rx_stats_ctrl(rtwdev, rtwsta, params, true);
 		rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params);
 		break;
 	case IEEE80211_AMPDU_RX_STOP:
+		rtw89_core_tid_rx_stats_ctrl(rtwdev, rtwsta, params, false);
 		rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, false, params);
 		break;
 	default:
diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h
index bd08495301e4..c16e7a7f8bc9 100644
--- a/drivers/net/wireless/realtek/rtw89/util.h
+++ b/drivers/net/wireless/realtek/rtw89/util.h
@@ -6,6 +6,13 @@
 
 #include "core.h"
 
+#define RTW89_KEY_PN_0 GENMASK_ULL(7, 0)
+#define RTW89_KEY_PN_1 GENMASK_ULL(15, 8)
+#define RTW89_KEY_PN_2 GENMASK_ULL(23, 16)
+#define RTW89_KEY_PN_3 GENMASK_ULL(31, 24)
+#define RTW89_KEY_PN_4 GENMASK_ULL(39, 32)
+#define RTW89_KEY_PN_5 GENMASK_ULL(47, 40)
+
 #define rtw89_iterate_vifs_bh(rtwdev, iterator, data)                          \
 	ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw,               \
 			IEEE80211_IFACE_ITER_NORMAL, iterator, data)
@@ -73,6 +80,16 @@ static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask)
 	}
 }
 
+static inline void ccmp_hdr2pn(s64 *pn, const u8 *hdr)
+{
+	*pn = u64_encode_bits(hdr[0], RTW89_KEY_PN_0) |
+	      u64_encode_bits(hdr[1], RTW89_KEY_PN_1) |
+	      u64_encode_bits(hdr[4], RTW89_KEY_PN_2) |
+	      u64_encode_bits(hdr[5], RTW89_KEY_PN_3) |
+	      u64_encode_bits(hdr[6], RTW89_KEY_PN_4) |
+	      u64_encode_bits(hdr[7], RTW89_KEY_PN_5);
+}
+
 s32 rtw89_linear_to_db_quarter(u64 val);
 s32 rtw89_linear_to_db(u64 val);
 u64 rtw89_db_quarter_to_linear(s32 db);
diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c
index 368e08826f1e..8dadd8df4fc6 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.c
+++ b/drivers/net/wireless/realtek/rtw89/wow.c
@@ -1741,6 +1741,8 @@ static int rtw89_wow_disable(struct rtw89_dev *rtwdev)
 
 	rtw89_wow_leave_ps(rtwdev, false);
 
+	rtw89_core_tid_rx_stats_reset(rtwdev);
+
 	ret = rtw89_wow_fw_stop(rtwdev);
 	if (ret) {
 		rtw89_err(rtwdev, "wow: failed to swap to normal fw\n");
diff --git a/drivers/net/wireless/realtek/rtw89/wow.h b/drivers/net/wireless/realtek/rtw89/wow.h
index 71e07f482174..d7e67632efeb 100644
--- a/drivers/net/wireless/realtek/rtw89/wow.h
+++ b/drivers/net/wireless/realtek/rtw89/wow.h
@@ -8,13 +8,6 @@
 #define RTW89_KEY_TKIP_PN_IV16 GENMASK_ULL(15, 0)
 #define RTW89_KEY_TKIP_PN_IV32 GENMASK_ULL(47, 16)
 
-#define RTW89_KEY_PN_0 GENMASK_ULL(7, 0)
-#define RTW89_KEY_PN_1 GENMASK_ULL(15, 8)
-#define RTW89_KEY_PN_2 GENMASK_ULL(23, 16)
-#define RTW89_KEY_PN_3 GENMASK_ULL(31, 24)
-#define RTW89_KEY_PN_4 GENMASK_ULL(39, 32)
-#define RTW89_KEY_PN_5 GENMASK_ULL(47, 40)
-
 #define RTW89_IGTK_IPN_0 GENMASK_ULL(7, 0)
 #define RTW89_IGTK_IPN_1 GENMASK_ULL(15, 8)
 #define RTW89_IGTK_IPN_2 GENMASK_ULL(23, 16)
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 12/13] wifi: rtw89: Recalculate station aggregates when AMSDU length changes for MLO links
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Po-Hao Huang <phhuang@realtek.com>

Currently, AMSDU length is updated per-link for MLO but not propagated
to the station aggregates, causing suboptimal TX throughput. This change
ensures station aggregates are recalculated when any link's AMSDU length
changes.

Signed-off-by: Po-Hao Huang <phhuang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/phy.c | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c
index 0fa4d8d791f1..3a241738ac06 100644
--- a/drivers/net/wireless/realtek/rtw89/phy.c
+++ b/drivers/net/wireless/realtek/rtw89/phy.c
@@ -3213,7 +3213,8 @@ struct rtw89_phy_iter_ra_data {
 
 static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link,
 					struct ieee80211_link_sta *link_sta,
-					struct rtw89_phy_iter_ra_data *ra_data)
+					struct rtw89_phy_iter_ra_data *ra_data,
+					bool *changed)
 {
 	struct rtw89_dev *rtwdev = ra_data->rtwdev;
 	const struct rtw89_c2h_ra_rpt *c2h =
@@ -3222,7 +3223,7 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link,
 	const struct rtw89_chip_info *chip = rtwdev->chip;
 	bool format_v1 = chip->chip_gen == RTW89_CHIP_BE;
 	u8 mode, rate, bw, giltf, mac_id;
-	u16 legacy_bitrate;
+	u16 legacy_bitrate, amsdu_len;
 	bool valid;
 	u8 mcs = 0;
 	u8 t;
@@ -3319,7 +3320,13 @@ static void __rtw89_phy_c2h_ra_rpt_iter(struct rtw89_sta_link *rtwsta_link,
 			     u16_encode_bits(mode, RTW89_HW_RATE_MASK_MOD) |
 			     u16_encode_bits(rate, RTW89_HW_RATE_MASK_VAL);
 	ra_report->might_fallback_legacy = mcs <= 2;
-	link_sta->agg.max_rc_amsdu_len = get_max_amsdu_len(rtwdev, ra_report);
+
+	amsdu_len = get_max_amsdu_len(rtwdev, ra_report);
+	if (link_sta->agg.max_rc_amsdu_len != amsdu_len) {
+		link_sta->agg.max_rc_amsdu_len = amsdu_len;
+		*changed = true;
+	}
+
 	rtwsta_link->max_agg_wait = link_sta->agg.max_rc_amsdu_len / 1500 - 1;
 }
 
@@ -3330,14 +3337,18 @@ static void rtw89_phy_c2h_ra_rpt_iter(void *data, struct ieee80211_sta *sta)
 	struct rtw89_sta_link *rtwsta_link;
 	struct ieee80211_link_sta *link_sta;
 	unsigned int link_id;
+	bool changed = false;
 
 	rcu_read_lock();
 
 	rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) {
 		link_sta = rtw89_sta_rcu_dereference_link(rtwsta_link, false);
-		__rtw89_phy_c2h_ra_rpt_iter(rtwsta_link, link_sta, ra_data);
+		__rtw89_phy_c2h_ra_rpt_iter(rtwsta_link, link_sta, ra_data, &changed);
 	}
 
+	if (changed)
+		ieee80211_sta_recalc_aggregates(sta);
+
 	rcu_read_unlock();
 }
 
-- 
2.25.1


^ permalink raw reply related

* [PATCH rtw-next 13/13] wifi: rtw89: debug: simulate Wi-Fi 7 SER L0/L1 without PS mode
From: Ping-Ke Shih @ 2026-03-10  8:01 UTC (permalink / raw)
  To: linux-wireless; +Cc: timlee, phhuang, kevin_yang
In-Reply-To: <20260310080146.31113-1-pkshih@realtek.com>

From: Zong-Zhe Yang <kevin_yang@realtek.com>

Current triggers of Wi-Fi 7 SER (system error recovery) L0/L1 simulation
don't yet guarantee working with PS mode. So, leave PS mode first before
triggering them for now.

Signed-off-by: Zong-Zhe Yang <kevin_yang@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/debug.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c
index d461ffc6dc9e..82849d109cc3 100644
--- a/drivers/net/wireless/realtek/rtw89/debug.c
+++ b/drivers/net/wireless/realtek/rtw89/debug.c
@@ -3552,6 +3552,8 @@ static int rtw89_dbg_trigger_l1_error_by_halt_h2c_be(struct rtw89_dev *rtwdev)
 	if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags))
 		return -EBUSY;
 
+	rtw89_leave_ps_mode(rtwdev);
+
 	rtw89_write32_set(rtwdev, R_BE_FW_TRIGGER_IDCT_ISR,
 			  B_BE_DMAC_FW_TRIG_IDCT | B_BE_DMAC_FW_ERR_IDCT_IMR);
 
@@ -3654,6 +3656,8 @@ static int rtw89_dbg_trigger_l0_error_by_halt_h2c_be(struct rtw89_dev *rtwdev)
 	if (!test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags))
 		return -EBUSY;
 
+	rtw89_leave_ps_mode(rtwdev);
+
 	rtw89_write32_set(rtwdev, R_BE_CMAC_FW_TRIGGER_IDCT_ISR,
 			  B_BE_CMAC_FW_TRIG_IDCT | B_BE_CMAC_FW_ERR_IDCT_IMR);
 
-- 
2.25.1


^ permalink raw reply related

* Re: [PATCH ath-next] wifi: ath12k: avoid dynamic alloc when parsing wmi tb
From: Nicolas Escande @ 2026-03-10  9:56 UTC (permalink / raw)
  To: Jeff Johnson, Nicolas Escande, ath12k; +Cc: linux-wireless
In-Reply-To: <621ab5fd-206d-4cf9-b30d-cb9d6bc0459b@oss.qualcomm.com>

On Mon Mar 9, 2026 at 9:16 PM CET, Jeff Johnson wrote:
> On 3/9/2026 8:20 AM, Nicolas Escande wrote:
>
> I have some nit comments...
>
>> On each WMI message received from the hardware, we alloc a temporary array
>
> note that not every WMI message handler uses this pattern.

Technically yes indeed. Maybe if I rephrase with something in the lines of:

When we receive a WMI message, we need to extract the relevant data from the
hardware depending on the event type. A common pattern is to alloc a temp
array of WMI_TAG_MAX entries of type void * in which we store pointers to
structs holding the actual data send by the chipset. Once it is no longer used
this array is then freed. This short lived allocation can fail when memory
pressure in the system is high enough.
>> 
>> Given the fact that it is scheduled in softirq with the system_bh_wq, we
>> should not be able to parse more than one WMI message per CPU at any time
>
> add hard stop .

ok

>
>> 
>> So instead lets move to a per cpu allocated array, stored in the struct
>> ath12k_base, that is reused accros calls. The ath12k_wmi_tlv_parse_alloc()
>
> s/accros/across/

ok

>
>> is also renamed into / merged with ath12k_wmi_tlv_parse() as it no longer
>> allocs memory but returns the existing per-cpu one.
>
> note that imperative voice is preferred.

s/So instead lets move/Move/ ?

>
>> 
>> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00218-QCAHKSWPL_SILICONZ-1
>> 
>> Signed-off-by: Nicolas Escande <nico.escande@gmail.com>
>> ---
>> changes from RFC:
>>   - rebased on ath-next 8e0ab5b9adb7
>>   - converted missing call sites ath12k_wmi_obss_color_collision_event()
>>     & ath12k_wmi_pdev_temperature_event()
>>   - changed alloc order & cleanup path in ath12k_core_alloc() as it seems
>>     it confused people
>>   - used sizeof(*tb) in ath12k_wmi_tlv_parse()
>> 
>> Note I did try to move to a DEFINE_PER_CPU() allocated array but the module
>> loader complained about the array size. So I stuck to the per ab alloc. 
>> ---
>>  drivers/net/wireless/ath/ath12k/core.c |   7 +
>>  drivers/net/wireless/ath/ath12k/core.h |   2 +
>>  drivers/net/wireless/ath/ath12k/wmi.c  | 178 ++++++-------------------
>>  3 files changed, 51 insertions(+), 136 deletions(-)
> ...
>> @@ -3913,7 +3903,7 @@ ath12k_wmi_obss_color_collision_event(struct ath12k_base *ab, struct sk_buff *sk
>>  	u32 vdev_id, evt_type;
>>  	u64 bitmap;
>>  
>> -	const void **tb __free(kfree) = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
>> +	const void **tb = ath12k_wmi_tlv_parse(ab, skb);
>
> checkpatch complains:
> Missing a blank line after declarations

Weird that my checkpatch (from yesterday's ath-next) did not catch this:
scripts/checkpatch.pl \
	mail/0001-wifi-ath12k-avoid-dynamic-alloc-when-parsing-wmi-tb.patch
total: 0 errors, 0 warnings, 0 checks, 807 lines checked

Is there additionnal flags or tools I should run ?

>
> Note that when __free() is used that this rule is not enforced since __free()
> declarations should appear at first use
>
> So I'd separate the declaration of tb from the assignment to be aligned with
> all the other WMI functions

No problem I hesitated to be honest. And I think I prefer seperate declaration
anyway.

>
>>  	if (IS_ERR(tb)) {
>>  		ath12k_warn(ab, "failed to parse OBSS color collision tlv %ld\n",
>>  			    PTR_ERR(tb));
>
> that's it for the nits. the development is stress testing the functionality.

On that front, I can say that it has been running at scale in production on
around half a million devices for one mounth and so far so good. But there is
no need to rush this and we can gladly wait till you guys make the necessary
checks so you feel confident enough with it.

>
> /jeff

^ permalink raw reply

* Re: [PATCH v1 1/8] soc: qcom: qmi: Enumerate the service IDs of QMI
From: Konrad Dybcio @ 2026-03-10  9:57 UTC (permalink / raw)
  To: Daniel Lezcano, konradybcio, andersson
  Cc: linux-kernel, Alex Elder, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jeff Johnson,
	Mathieu Poirier, Srinivas Kandagatla, Jaroslav Kysela,
	Takashi Iwai, Kees Cook, Greg Kroah-Hartman, Arnd Bergmann,
	Mark Brown, Wesley Cheng, netdev, linux-wireless, ath10k, ath11k,
	ath12k, linux-arm-msm, linux-remoteproc, linux-sound
In-Reply-To: <20260309230346.3584252-2-daniel.lezcano@oss.qualcomm.com>

On 3/10/26 12:03 AM, Daniel Lezcano wrote:
> The QMI framework proposes a set of services which are defined by an
> integer identifier. The different QMI client lookup for the services
> via this identifier. Moreover, the function qmi_add_lookup() and
> qmi_add_server() must match the service ID but the code in different
> places set the same value but with a different macro name. These
> macros are spreaded across the different subsystems implementing the
> protocols associated with a service. It would make more sense to
> define them in the QMI header for the sake of consistency and clarity.
> 
> This change use an unified naming for the services and enumerate the
> ones implemented in the Linux kernel. More services can come later and
> put the service ID in this same header.
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
> ---
>  include/linux/soc/qcom/qmi.h | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h
> index 291cdc7ef49c..b8d07f2c07e7 100644
> --- a/include/linux/soc/qcom/qmi.h
> +++ b/include/linux/soc/qcom/qmi.h
> @@ -92,6 +92,18 @@ struct qmi_elem_info {
>  #define QMI_ERR_INCOMPATIBLE_STATE_V01		90
>  #define QMI_ERR_NOT_SUPPORTED_V01		94
>  
> +/*
> + * Enumerate the IDs of the QMI services

In case that's useful:

https://github.com/linux-msm/qrtr/blob/master/src/lookup.c#L23

Konrad

^ permalink raw reply

* Re: [PATCH v1 1/8] soc: qcom: qmi: Enumerate the service IDs of QMI
From: Daniel Lezcano @ 2026-03-10 10:06 UTC (permalink / raw)
  To: Konrad Dybcio, konradybcio, andersson
  Cc: linux-kernel, Alex Elder, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Jeff Johnson,
	Mathieu Poirier, Srinivas Kandagatla, Jaroslav Kysela,
	Takashi Iwai, Kees Cook, Greg Kroah-Hartman, Arnd Bergmann,
	Mark Brown, Wesley Cheng, netdev, linux-wireless, ath10k, ath11k,
	ath12k, linux-arm-msm, linux-remoteproc, linux-sound
In-Reply-To: <7d036c96-e257-460c-a91b-deee0b9c348b@oss.qualcomm.com>

On 3/10/26 10:57, Konrad Dybcio wrote:
> On 3/10/26 12:03 AM, Daniel Lezcano wrote:
>> The QMI framework proposes a set of services which are defined by an
>> integer identifier. The different QMI client lookup for the services
>> via this identifier. Moreover, the function qmi_add_lookup() and
>> qmi_add_server() must match the service ID but the code in different
>> places set the same value but with a different macro name. These
>> macros are spreaded across the different subsystems implementing the
>> protocols associated with a service. It would make more sense to
>> define them in the QMI header for the sake of consistency and clarity.
>>
>> This change use an unified naming for the services and enumerate the
>> ones implemented in the Linux kernel. More services can come later and
>> put the service ID in this same header.
>>
>> Signed-off-by: Daniel Lezcano <daniel.lezcano@oss.qualcomm.com>
>> ---
>>   include/linux/soc/qcom/qmi.h | 12 ++++++++++++
>>   1 file changed, 12 insertions(+)
>>
>> diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h
>> index 291cdc7ef49c..b8d07f2c07e7 100644
>> --- a/include/linux/soc/qcom/qmi.h
>> +++ b/include/linux/soc/qcom/qmi.h
>> @@ -92,6 +92,18 @@ struct qmi_elem_info {
>>   #define QMI_ERR_INCOMPATIBLE_STATE_V01		90
>>   #define QMI_ERR_NOT_SUPPORTED_V01		94
>>   
>> +/*
>> + * Enumerate the IDs of the QMI services
> 
> In case that's useful:
> 
> https://github.com/linux-msm/qrtr/blob/master/src/lookup.c#L23

Thanks !

^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath12k: avoid dynamic alloc when parsing wmi tb
From: Nicolas Escande @ 2026-03-10 10:31 UTC (permalink / raw)
  To: Baochen Qiang, Nicolas Escande, ath12k; +Cc: linux-wireless
In-Reply-To: <04d268a7-8f6e-4aa4-b366-0dc38c355de7@oss.qualcomm.com>

On Tue Mar 10, 2026 at 3:05 AM CET, Baochen Qiang wrote:
[...]
>> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
>> index 59c193b24764..ebe7b94fd712 100644
>> --- a/drivers/net/wireless/ath/ath12k/core.h
>> +++ b/drivers/net/wireless/ath/ath12k/core.h
>> @@ -19,6 +19,7 @@
>>  #include <linux/average.h>
>>  #include <linux/of.h>
>>  #include <linux/rhashtable.h>
>> +#include <linux/percpu.h>
>>  #include "qmi.h"
>>  #include "htc.h"
>>  #include "wmi.h"
>> @@ -937,6 +938,7 @@ struct ath12k_base {
>>  	struct device *dev;
>>  	struct ath12k_qmi qmi;
>>  	struct ath12k_wmi_base wmi_ab;
>> +	void __percpu *wmi_tb;
>
> any reason why my v1 suggestion is not considered?
>

I considered it but I for sure did not write enough about it in the changelog.
Sorry about that, see my thoughts bellow.

> instead of allocating it per device, how about making it global and define/allocate once
> when loading driver. This way we may save some memory in case where more than one devices
> get probed?

So what I did try first is to use DEFINE_PER_CPU() directly in wmi.c to have
this as a static array, directly in the compilation unit where it is used.
But this failled at runtime as the allocated size it too big and it would have
needed modifying the max alloc size that the module loader would allow. 

My second option was to add something in the module_init so ath12k_wifi7_init().
But as there is no ath12k 'global' struct, it meant adding a global variable to
hold the per cpu array, that would be alloced in wifi7/core.c and used in wmi.c.
And that felt weird too, the wifi version specific part of it at least.

So I stayed with the per ath12k_base, which as relatively low overhead for a
clean consistant interface.

But if you guys don't want it that way, I can rework it. Just tell me in more
details what you think is the right way and I can modify it.

Thanks

^ permalink raw reply

* Re: [BUG] wifi: rtw88: Hard system freeze on RTL8821CE when power_save is enabled (LPS/ASPM conflict)
From: LB F @ 2026-03-10 11:01 UTC (permalink / raw)
  To: Ping-Ke Shih; +Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <1e96af437fa24674b353ddb530b2d8e7@realtek.com>

Hi Ping-Ke,

Thank you for the incredibly fast response and assistance!

> Can you dig kernel log (by netconsole or ramoops) if something useful?
> I'd like to know this is hardware level freeze or kernel can capture something wrong.

I managed to pull a call trace from a historic journald log just
before the system hung. The kernel gets trapped in an IRQ thread
inside `rtw_pci_interrupt_threadfn`, calling up into `mac80211`
`ieee80211_rx_list` before everything freezes. Here is the relevant
snippet:

```text
Call Trace:
<IRQ>
? __alloc_skb+0x23a/0x2a0
? __alloc_skb+0x10c/0x2a0
? __pfx_irq_thread_fn+0x10/0x10
[ ... truncated module list ... ]
Tainted: G W I 6.19.6-2-cachyos #1 PREEMPT(full)
Hardware name: HP HP Notebook/81F0, BIOS F.50 11/20/2020
RIP: 0010:ieee80211_rx_list+0x1012/0x1020 [mac80211]
CPU: 2 UID: 0 PID: 765 Comm: irq/56-rtw88_pc
rtw_pci_interrupt_threadfn+0x239/0x310 [rtw88_pci]
```

It behaves exactly like a PCIe bus deadlock or a hardware fault that
eventually brings down the CPU handling the IRQ.

> Are these totally needed to workaround the problem? Or disable_aspm is enough?
> I'd list them in order of power consumption impact:
> 1. disable_aspm=y
> 2. disable_lps_deep=y
> 3. disable WiFi power save

To verify which parameters are strictly necessary, I performed
isolated testing today. I ensured no other modprobe configs were
active, rebuilt the initramfs, and manually enforced that
`wifi.powersave` was active via `iw dev wlan0 set power_save on`
during all tests (as the OS power management profiles were defaulting
it to off, which initially masked the issue).

I tested each workaround individually across multiple sleep/wake
cycles and active usage:

**Test 1 (ASPM Disabled, LPS Deep Enabled):**
- Kernel parameters: `rtw88_pci disable_aspm=y` (and `rtw88_core
disable_lps_deep=n`)
- Result: Stable. No freezes were observed during usage or transitions
into/out of S3 sleep while power saving was enforced.

**Test 2 (ASPM Enabled, LPS Deep Disabled):**
- Kernel parameters: `rtw88_core disable_lps_deep=y` (and `rtw88_pci
disable_aspm=n`)
- Result: Stable. No freezes were observed under the same forced power
save conditions.

**Conclusion:** It appears we do not need both workarounds
simultaneously for this specific hardware. Using only `disable_aspm=y`
seems to be sufficient to prevent the system freeze. Given your note
about the power consumption impact ranking, this looks like the
optimal path forward.

> But what does 'deadlock' mean? As I know NAPI poll is scheduled by ISR,
> and going to receive packets. The rx_no_aspm workaround is to forcely turn
> off ASPM during this period.

By "deadlock" I meant a hardware-level bus lockup. It seems the
physical RTL8821CE chip itself crashes or hangs the system's PCIe bus
when trying to negotiate waking up from ASPM L1 while simultaneously
existing in `LPS_DEEP_MODE_LCLK`. The `rx_no_aspm` workaround in NAPI
helps during active Rx decoding, but the laptop often freezes while
completely idle, presumably when the AP sends a basic beacon, the chip
attempts to leave LPS Deep + L1, and the hardware simply gives up and
halts the system.

> We have not modified RTL8821CE for a long time, so I'd add workaround
> to specific platform as mentioned above.

Adding a DMI/platform quirk specifically for this laptop to disable
ASPM would be wonderful and deeply appreciated. I agree it is safer
than touching the global flags for hardware that is functioning
correctly out in the wild.

Here is the exact identifying information for my system:

System Vendor: HP
Product Name: HP Notebook
SKU Number: P3S95EA#ACB
Family: 103C_5335KV
PCI ID: 10ec:c821
Subsystem ID: 103c:831a

I am completely ready to test any patch or quirk you send my way.
Thank you so much for your time and helping track this down!

Best regards,
Oleksandr

^ permalink raw reply

* Re: [PATCH 11/14] media: qcom: Switch to generic PAS TZ APIs
From: Konrad Dybcio @ 2026-03-10 11:18 UTC (permalink / raw)
  To: Jorge Ramirez, Sumit Garg
  Cc: linux-arm-msm, devicetree, dri-devel, freedreno, linux-media,
	netdev, linux-wireless, ath12k, linux-remoteproc, andersson,
	konradybcio, robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo,
	lumag, abhinav.kumar, jesszhan0024, marijn.suijten, airlied,
	simona, vikash.garodia, dikshita.agarwal, bod, mchehab, elder,
	andrew+netdev, davem, edumazet, kuba, pabeni, jjohnson,
	mathieu.poirier, trilokkumar.soni, mukesh.ojha, pavan.kondeti,
	tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
	jens.wiklander, op-tee, apurupa, skare, Sumit Garg
In-Reply-To: <aa6O5tir4kVIe0eZ@trex>

On 3/9/26 10:12 AM, Jorge Ramirez wrote:
> On 06/03/26 16:20:24, Sumit Garg wrote:
>> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>>
>> Switch qcom media client drivers over to generic PAS TZ APIs. Generic PAS
>> TZ service allows to support multiple TZ implementation backends like QTEE
>> based SCM PAS service, OP-TEE based PAS service and any further future TZ
>> backend service.
>>
>> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
>> ---

[...]

> are the calls to set_remote_state required?
> 0 is not the IRIS/VENUS remote processor.
> 
> If it is legacy, maybe they can be phased out?

FWIW, in msm-3.10 (yes) QCOM_SCM_SVC_BOOT/0x0a used to be called
'TZBSP_VIDEO_SET_STATE', taking the arguments:

args[0] = suspend->0, resume->1
args[1] = spare=0

seems like sdm845/msm-4.9 still had that and so did 8250/msm-4.19

Konrad

^ permalink raw reply

* [PATCH 08/61] fuse: Prefer IS_ERR_OR_NULL over manual NULL check
From: Philipp Hahn @ 2026-03-10 11:48 UTC (permalink / raw)
  To: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Philipp Hahn
  Cc: Miklos Szeredi
In-Reply-To: <20260310-b4-is_err_or_null-v1-0-bd63b656022d@avm.de>

Prefer using IS_ERR_OR_NULL() over using IS_ERR() and a manual NULL
check.

Change generated with coccinelle.

To: Miklos Szeredi <miklos@szeredi.hu>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Philipp Hahn <phahn-oss@avm.de>
---
 fs/fuse/dir.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 7ac6b232ef12323e3afb97b98301f623bce917a4..7b39c013027bd9c4ba6f080bfc9b3ec22bc2dd4a 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1599,7 +1599,7 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid,
 		goto put_parent;
 	while (!entry) {
 		struct dentry *child = try_lookup_noperm(name, dir);
-		if (!child || IS_ERR(child))
+		if (IS_ERR_OR_NULL(child))
 			goto put_parent;
 		entry = start_removing_dentry(dir, child);
 		dput(child);

-- 
2.43.0


^ permalink raw reply related

* [PATCH 13/61] squashfs: Prefer IS_ERR_OR_NULL over manual NULL check
From: Philipp Hahn @ 2026-03-10 11:48 UTC (permalink / raw)
  To: amd-gfx, apparmor, bpf, ceph-devel, cocci, dm-devel, dri-devel,
	gfs2, intel-gfx, intel-wired-lan, iommu, kvm, linux-arm-kernel,
	linux-block, linux-bluetooth, linux-btrfs, linux-cifs, linux-clk,
	linux-erofs, linux-ext4, linux-fsdevel, linux-gpio, linux-hyperv,
	linux-input, linux-kernel, linux-leds, linux-media, linux-mips,
	linux-mm, linux-modules, linux-mtd, linux-nfs, linux-omap,
	linux-phy, linux-pm, linux-rockchip, linux-s390, linux-scsi,
	linux-sctp, linux-security-module, linux-sh, linux-sound,
	linux-stm32, linux-trace-kernel, linux-usb, linux-wireless,
	netdev, ntfs3, samba-technical, sched-ext, target-devel,
	tipc-discussion, v9fs, Philipp Hahn
  Cc: Phillip Lougher
In-Reply-To: <20260310-b4-is_err_or_null-v1-0-bd63b656022d@avm.de>

Prefer using IS_ERR_OR_NULL() over using IS_ERR() and a manual NULL
check.

Change generated with coccinelle.

To: Phillip Lougher <phillip@squashfs.org.uk>
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Philipp Hahn <phahn-oss@avm.de>
---
 fs/squashfs/cache.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index 67abd4dff222235e75d8c2b10d5e9b811d6e38d8..8888cc02966e2e33210c872c733205d4c581ecc9 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -198,7 +198,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache)
 {
 	int i, j;
 
-	if (IS_ERR(cache) || cache == NULL)
+	if (IS_ERR_OR_NULL(cache))
 		return;
 
 	for (i = 0; i < cache->entries; i++) {

-- 
2.43.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox