]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw89: add H2C command to protect TX/RX for unused PHY
authorKuan-Chung Chen <damon.chen@realtek.com>
Fri, 13 Feb 2026 06:15:48 +0000 (14:15 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Tue, 3 Mar 2026 02:36:20 +0000 (10:36 +0800)
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>
Link: https://patch.msgid.link/20260213061552.29997-9-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/fw.c
drivers/net/wireless/realtek/rtw89/fw.h
drivers/net/wireless/realtek/rtw89/mac.c
drivers/net/wireless/realtek/rtw89/mac80211.c
drivers/net/wireless/realtek/rtw89/ps.c

index 52f7e65fe6a5ac5129f266a24da325ec790bcdca..ffbd75cb553348a8a64648efff64784d4f8cdf52 100644 (file)
@@ -6871,6 +6871,93 @@ flex_member:
        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)
index c60d419616d63da0df0825640002fc7cb7325c4d..80d260eb08cd43cf37788e0cce02ce6a6f3fcda7 100644 (file)
@@ -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)
index c98ca2a82194d190acf89d023a3260560a565922..04ef4bae18529a46aee8a3a2c9236dfe02a67a16 100644 (file)
@@ -5411,6 +5411,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;
index 0ea33743853eb45d23a31d2ccaad0150cbd67488..1ef73bfc40d1708111bbf8f64f3dfd436adea375 100644 (file)
@@ -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;
index aad2ee7926d67df55a9ddd2ffe422b4ee12d2daf..125cf14fa581fc474345f67034d82c49fbdf0146 100644 (file)
@@ -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)