public inbox for linux-wireless@vger.kernel.org
 help / color / mirror / Atom feed
From: Ping-Ke Shih <pkshih@realtek.com>
To: <linux-wireless@vger.kernel.org>
Cc: <damon.chen@realtek.com>, <kevin_yang@realtek.com>
Subject: [PATCH rtw-next 08/12] wifi: rtw89: add H2C command to protect TX/RX for unused PHY
Date: Fri, 13 Feb 2026 14:15:48 +0800	[thread overview]
Message-ID: <20260213061552.29997-9-pkshih@realtek.com> (raw)
In-Reply-To: <20260213061552.29997-1-pkshih@realtek.com>

From: Kuan-Chung Chen <damon.chen@realtek.com>

For BE chips, the unused PHY should pause transmissions and receptions.
This ensures that no unexpected packets are routed to an inactive PHY,
which could otherwise trigger SER L0 and lead to TX hang.

Signed-off-by: Kuan-Chung Chen <damon.chen@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
---
 drivers/net/wireless/realtek/rtw89/fw.c       | 87 +++++++++++++++++++
 drivers/net/wireless/realtek/rtw89/fw.h       | 51 +++++++++++
 drivers/net/wireless/realtek/rtw89/mac.c      |  3 +
 drivers/net/wireless/realtek/rtw89/mac80211.c |  5 ++
 drivers/net/wireless/realtek/rtw89/ps.c       |  2 +
 5 files changed, 148 insertions(+)

diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 64874907bfe0..bfa5cac24d88 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -6871,6 +6871,93 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
 	return 0;
 }
 
+int rtw89_fw_h2c_trx_protect(struct rtw89_dev *rtwdev,
+			     enum rtw89_phy_idx phy_idx, bool enable)
+{
+	struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait;
+	const struct rtw89_chip_info *chip = rtwdev->chip;
+	struct rtw89_h2c_trx_protect *h2c;
+	u32 len = sizeof(*h2c);
+	struct sk_buff *skb;
+	int ret;
+
+	if (chip->chip_gen != RTW89_CHIP_BE)
+		return 0;
+
+	skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+	if (!skb) {
+		rtw89_err(rtwdev, "failed to alloc skb for h2c trx protect\n");
+		return -ENOMEM;
+	}
+
+	skb_put(skb, len);
+	h2c = (struct rtw89_h2c_trx_protect *)skb->data;
+
+	h2c->c0 = le32_encode_bits(BIT(phy_idx), RTW89_H2C_TRX_PROTECT_C0_BAND_BITMAP) |
+		  le32_encode_bits(0, RTW89_H2C_TRX_PROTECT_C0_OP_MODE);
+	h2c->c1 = le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_C1_RX_IN) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_C1_PPDU_STS) |
+		  le32_encode_bits(1, RTW89_H2C_TRX_PROTECT_C1_MSK_RX_IN) |
+		  le32_encode_bits(1, RTW89_H2C_TRX_PROTECT_C1_MSK_PPDU_STS);
+	h2c->w0 = le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BE0) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BK0) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VI0) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VO0) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BE1) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BK1) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VI1) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_VO1) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_MG0) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_MG1) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_MG2) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_HI)  |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_BCN) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_UL) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT0) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT1) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT2) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT3) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ0) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ1);
+	h2c->m0 = cpu_to_le32(RTW89_H2C_TRX_PROTECT_W0_TXEN_BE0 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_BK0 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_VI0 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_VO0 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_BE1 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_BK1 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_VI1 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_VO1 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_MG0 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_MG1 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_MG2 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_HI |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_BCN |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_UL |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT0 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT1 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT2 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT3 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ0 |
+			      RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ1);
+	h2c->w1 = le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN) |
+		  le32_encode_bits(enable, RTW89_H2C_TRX_PROTECT_W1_DFS_EN);
+	h2c->m1 = cpu_to_le32(RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN |
+			      RTW89_H2C_TRX_PROTECT_W1_DFS_EN);
+
+	rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+			      H2C_CAT_MAC, H2C_CL_MAC_FW_OFLD,
+			      H2C_FUNC_TRX_PROTECT, 0, 1, len);
+
+	ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait,
+				    RTW89_FW_OFLD_WAIT_COND_TRX_PROTECT);
+	if (ret) {
+		rtw89_debug(rtwdev, RTW89_DBG_FW, "failed to trx protect\n");
+		return ret;
+	}
+
+	return 0;
+}
+
 int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev,
 			struct rtw89_fw_h2c_rf_reg_info *info,
 			u16 len, u8 page)
diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h
index c60d419616d6..80d260eb08cd 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.h
+++ b/drivers/net/wireless/realtek/rtw89/fw.h
@@ -3106,6 +3106,44 @@ struct rtw89_h2c_scanofld_be {
 #define RTW89_H2C_SCANOFLD_BE_W9_SIZE_MACC GENMASK(15, 8)
 #define RTW89_H2C_SCANOFLD_BE_W9_SIZE_OP GENMASK(23, 16)
 
+struct rtw89_h2c_trx_protect {
+	__le32 c0;
+	__le32 c1;
+	__le32 w0;
+	__le32 m0;
+	__le32 w1;
+	__le32 m1;
+} __packed;
+
+#define RTW89_H2C_TRX_PROTECT_C0_BAND_BITMAP GENMASK(2, 0)
+#define RTW89_H2C_TRX_PROTECT_C0_OP_MODE GENMASK(4, 3)
+#define RTW89_H2C_TRX_PROTECT_C1_RX_IN BIT(0)
+#define RTW89_H2C_TRX_PROTECT_C1_PPDU_STS BIT(4)
+#define RTW89_H2C_TRX_PROTECT_C1_MSK_RX_IN BIT(16)
+#define RTW89_H2C_TRX_PROTECT_C1_MSK_PPDU_STS BIT(20)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BE0 BIT(0)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BK0 BIT(1)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VI0 BIT(2)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VO0 BIT(3)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BE1 BIT(4)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BK1 BIT(5)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VI1 BIT(6)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_VO1 BIT(7)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_MG0 BIT(8)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_MG1 BIT(9)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_MG2 BIT(10)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_HI BIT(11)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_BCN BIT(12)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_UL BIT(13)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT0 BIT(14)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT1 BIT(15)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT2 BIT(16)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_TWT3 BIT(17)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ0 BIT(18)
+#define RTW89_H2C_TRX_PROTECT_W0_TXEN_SPEQ1 BIT(19)
+#define RTW89_H2C_TRX_PROTECT_W1_CHINFO_EN BIT(0)
+#define RTW89_H2C_TRX_PROTECT_W1_DFS_EN BIT(1)
+
 struct rtw89_h2c_fwips {
 	__le32 w0;
 } __packed;
@@ -4598,6 +4636,7 @@ enum rtw89_fw_ofld_h2c_func {
 	H2C_FUNC_OFLD_TP		= 0x20,
 	H2C_FUNC_MAC_MACID_PAUSE_SLEEP	= 0x28,
 	H2C_FUNC_SCANOFLD_BE		= 0x2c,
+	H2C_FUNC_TRX_PROTECT		= 0x34,
 
 	NUM_OF_RTW89_FW_OFLD_H2C_FUNC,
 };
@@ -4608,6 +4647,7 @@ enum rtw89_fw_ofld_h2c_func {
 #define RTW89_FW_OFLD_WAIT_COND_PKT_OFLD(pkt_id, pkt_op) \
 	RTW89_FW_OFLD_WAIT_COND(RTW89_PKT_OFLD_WAIT_TAG(pkt_id, pkt_op), \
 				H2C_FUNC_PACKET_OFLD)
+#define RTW89_FW_OFLD_WAIT_COND_TRX_PROTECT RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_TRX_PROTECT)
 
 #define RTW89_SCANOFLD_WAIT_COND_ADD_CH RTW89_FW_OFLD_WAIT_COND(0, H2C_FUNC_ADD_SCANOFLD_CH)
 
@@ -5294,6 +5334,8 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev,
 				 struct rtw89_scan_option *opt,
 				 struct rtw89_vif_link *vif,
 				 bool wowlan);
+int rtw89_fw_h2c_trx_protect(struct rtw89_dev *rtwdev,
+			     enum rtw89_phy_idx phy_idx, bool enable);
 int rtw89_fw_h2c_rf_reg(struct rtw89_dev *rtwdev,
 			struct rtw89_fw_h2c_rf_reg_info *info,
 			u16 len, u8 page);
@@ -5469,6 +5511,15 @@ static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev)
 		rtw89_fw_h2c_init_dynamic_ba_cam_v0_ext(rtwdev);
 }
 
+static inline void rtw89_fw_h2c_init_trx_protect(struct rtw89_dev *rtwdev)
+{
+	u8 active_bands = rtw89_get_active_phy_bitmap(rtwdev);
+	int i;
+
+	for (i = 0; i < RTW89_PHY_NUM; i++)
+		rtw89_fw_h2c_trx_protect(rtwdev, i, active_bands & BIT(i));
+}
+
 static inline int rtw89_chip_h2c_default_cmac_tbl(struct rtw89_dev *rtwdev,
 						  struct rtw89_vif_link *rtwvif_link,
 						  struct rtw89_sta_link *rtwsta_link)
diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c
index 8472f1a63951..4d507eccaa00 100644
--- a/drivers/net/wireless/realtek/rtw89/mac.c
+++ b/drivers/net/wireless/realtek/rtw89/mac.c
@@ -5412,6 +5412,9 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le
 			cond = RTW89_SCANOFLD_BE_WAIT_COND_START;
 			h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN;
 			break;
+		case H2C_FUNC_TRX_PROTECT:
+			cond = RTW89_FW_OFLD_WAIT_COND_TRX_PROTECT;
+			break;
 		}
 
 		data.err = !!h2c_return;
diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c
index 315bb0d0759f..6b8f8bf2234d 100644
--- a/drivers/net/wireless/realtek/rtw89/mac80211.c
+++ b/drivers/net/wireless/realtek/rtw89/mac80211.c
@@ -528,6 +528,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev,
 	if (vif->type == NL80211_IFTYPE_AP || sta->tdls)
 		rtw89_queue_chanctx_change(rtwdev, RTW89_CHANCTX_REMOTE_STA_CHANGE);
 
+	rtw89_fw_h2c_init_trx_protect(rtwdev);
+
 	return 0;
 
 unset_link:
@@ -1584,6 +1586,8 @@ static void __rtw89_ops_clr_vif_links(struct rtw89_dev *rtwdev,
 		if (unlikely(!rtwvif_link))
 			continue;
 
+		rtw89_fw_h2c_trx_protect(rtwdev, rtwvif_link->phy_idx, false);
+
 		__rtw89_ops_remove_iface_link(rtwdev, rtwvif_link);
 
 		rtw89_vif_unset_link(rtwvif, link_id);
@@ -1609,6 +1613,7 @@ static int __rtw89_ops_set_vif_links(struct rtw89_dev *rtwdev,
 				  __func__, link_id);
 			return ret;
 		}
+		rtw89_fw_h2c_trx_protect(rtwdev, rtwvif_link->phy_idx, true);
 	}
 
 	return 0;
diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c
index aad2ee7926d6..125cf14fa581 100644
--- a/drivers/net/wireless/realtek/rtw89/ps.c
+++ b/drivers/net/wireless/realtek/rtw89/ps.c
@@ -226,6 +226,8 @@ void rtw89_leave_lps(struct rtw89_dev *rtwdev)
 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
 		rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id)
 			rtw89_leave_lps_vif(rtwdev, rtwvif_link);
+
+	rtw89_fw_h2c_init_trx_protect(rtwdev);
 }
 
 void rtw89_enter_ips(struct rtw89_dev *rtwdev)
-- 
2.25.1


  parent reply	other threads:[~2026-02-13  6:17 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-02-13  6:15 [PATCH rtw-next 00/12] wifi: rtw89: refine to read HW parameters from file, and update SER code Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 01/12] wifi: rtw89: fw: add fw_def struct to put firmware name and format version Ping-Ke Shih
2026-03-03  2:48   ` Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 02/12] wifi: rtw89: fw: recognize firmware type B by AID Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 03/12] wifi: rtw89: add general way to generate module firmware string Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 04/12] wifi: rtw89: 8852b: update supported firmware format to 2 Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 05/12] wifi: rtw89: 8852a: move DIG tables to rtw8852a.c Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 06/12] wifi: rtw89: 8852a: update supported firmware format to 1 Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 07/12] wifi: rtw89: 8851b: " Ping-Ke Shih
2026-02-13  6:15 ` Ping-Ke Shih [this message]
2026-02-13  6:15 ` [PATCH rtw-next 09/12] wifi: rtw89: rfk: add hardware version to rtw89_fw_h2c_rf_pre_ntfy_mcc for new WiFi 7 firmware Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 10/12] wifi: rtw89: debug: add SER SW counters to count simulation Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 11/12] wifi: rtw89: ser: Wi-Fi 7 reset HALT C2H after reading it Ping-Ke Shih
2026-02-13  6:15 ` [PATCH rtw-next 12/12] wifi: rtw89: ser: post-recover DMAC state to prevent LPS Ping-Ke Shih

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260213061552.29997-9-pkshih@realtek.com \
    --to=pkshih@realtek.com \
    --cc=damon.chen@realtek.com \
    --cc=kevin_yang@realtek.com \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox