]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: rtw89: 8922a: add TAS feature support
authorKuan-Chung Chen <damon.chen@realtek.com>
Mon, 15 Sep 2025 06:54:29 +0000 (14:54 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Thu, 18 Sep 2025 01:20:11 +0000 (09:20 +0800)
Add TAS support for 8922AE. Unlike AX ICs, BE ICs introduce a TAS
timer switch. The firmware starts a TAS timer to periodically
collect TX power information and notify the driver via C2H
events. To avoid unnecessary C2H events, the TAS timer is
enabled during core_start().

Signed-off-by: Kuan-Chung Chen <damon.chen@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20250915065429.39269-1-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/core.c
drivers/net/wireless/realtek/rtw89/fw.c
drivers/net/wireless/realtek/rtw89/fw.h
drivers/net/wireless/realtek/rtw89/phy.c
drivers/net/wireless/realtek/rtw89/phy.h
drivers/net/wireless/realtek/rtw89/rtw8922a.c
drivers/net/wireless/realtek/rtw89/sar.c
drivers/net/wireless/realtek/rtw89/sar.h

index c7907949d3a20c0a4d2c32c842b7c9af0b15e9b0..fe059c4a6b5596e1d5004274b23b5f556896058c 100644 (file)
@@ -5520,6 +5520,7 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
        rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
        rtw89_fw_h2c_fw_log(rtwdev, rtwdev->fw.log.enable);
        rtw89_fw_h2c_init_ba_cam(rtwdev);
+       rtw89_tas_fw_timer_enable(rtwdev, true);
 
        return 0;
 }
@@ -5535,6 +5536,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)
        if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
                return;
 
+       rtw89_tas_fw_timer_enable(rtwdev, false);
        rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_OFF);
 
        clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags);
index 9c98c25c10ce1f04a13f2612bb6a72a65fd7af48..2273dae8434ab2dd7a1c6fba82caf7a4ef24e579 100644 (file)
@@ -6688,6 +6688,40 @@ fail:
        return ret;
 }
 
+int rtw89_fw_h2c_rf_tas_trigger(struct rtw89_dev *rtwdev, bool enable)
+{
+       struct rtw89_h2c_rf_tas *h2c;
+       u32 len = sizeof(*h2c);
+       struct sk_buff *skb;
+       int ret;
+
+       skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
+       if (!skb) {
+               rtw89_err(rtwdev, "failed to alloc skb for h2c RF TAS\n");
+               return -ENOMEM;
+       }
+       skb_put(skb, len);
+       h2c = (struct rtw89_h2c_rf_tas *)skb->data;
+
+       h2c->enable = cpu_to_le32(enable);
+
+       rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
+                             H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RF_FW_RFK,
+                             H2C_FUNC_RFK_TAS_OFFLOAD, 0, 0, len);
+
+       ret = rtw89_h2c_tx(rtwdev, skb, false);
+       if (ret) {
+               rtw89_err(rtwdev, "failed to send h2c\n");
+               goto fail;
+       }
+
+       return 0;
+fail:
+       dev_kfree_skb_any(skb);
+
+       return ret;
+}
+
 int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev,
                              u8 h2c_class, u8 h2c_func, u8 *buf, u16 len,
                              bool rack, bool dack)
index 98b187305481b35bf8c928c451d757f202a4a8b9..ddebf797206879a73a37d9adcc9bf6b13d47aba3 100644 (file)
@@ -4430,6 +4430,7 @@ enum rtw89_rfk_offload_h2c_func {
        H2C_FUNC_RFK_DACK_OFFLOAD = 0x5,
        H2C_FUNC_RFK_RXDCK_OFFLOAD = 0x6,
        H2C_FUNC_RFK_PRE_NOTIFY = 0x8,
+       H2C_FUNC_RFK_TAS_OFFLOAD = 0x9,
 };
 
 struct rtw89_fw_h2c_rf_get_mccch {
@@ -4611,6 +4612,10 @@ struct rtw89_h2c_rf_rxdck_v0 {
        u8 rxdck_dbg_en;
 } __packed;
 
+struct rtw89_h2c_rf_tas {
+       __le32 enable;
+} __packed;
+
 struct rtw89_h2c_rf_rxdck {
        struct rtw89_h2c_rf_rxdck_v0 v0;
        u8 is_chl_k;
@@ -4743,12 +4748,16 @@ struct rtw89_c2h_rfk_report {
        u8 version;
 } __packed;
 
-struct rtw89_c2h_rf_tas_info {
-       struct rtw89_c2h_hdr hdr;
+struct rtw89_c2h_rf_tas_rpt_log {
        __le32 cur_idx;
        __le16 txpwr_history[20];
 } __packed;
 
+struct rtw89_c2h_rf_tas_info {
+       struct rtw89_c2h_hdr hdr;
+       struct rtw89_c2h_rf_tas_rpt_log content;
+} __packed;
+
 #define RTW89_FW_RSVD_PLE_SIZE 0x800
 
 #define RTW89_FW_BACKTRACE_INFO_SIZE 8
@@ -4889,6 +4898,7 @@ int rtw89_fw_h2c_rf_dack(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx,
                         const struct rtw89_chan *chan);
 int rtw89_fw_h2c_rf_rxdck(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx,
                          const struct rtw89_chan *chan, bool is_chl_k);
+int rtw89_fw_h2c_rf_tas_trigger(struct rtw89_dev *rtwdev, bool enable);
 int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev,
                              u8 h2c_class, u8 h2c_func, u8 *buf, u16 len,
                              bool rack, bool dack);
index 45823c9e944847ec1017963870ab23391c4f0c54..ba7feadd758289f44adda5ad72cc149fb884cdef 100644 (file)
@@ -3172,6 +3172,34 @@ void (* const rtw89_phy_c2h_dm_handler[])(struct rtw89_dev *rtwdev,
        [RTW89_PHY_C2H_DM_FUNC_FW_SCAN] = rtw89_phy_c2h_fw_scan_rpt,
 };
 
+static
+void rtw89_phy_c2h_rfk_tas_pwr(struct rtw89_dev *rtwdev,
+                              const struct rtw89_c2h_rf_tas_rpt_log *content)
+{
+       const enum rtw89_sar_sources src = rtwdev->sar.src;
+       struct rtw89_tas_info *tas = &rtwdev->tas;
+       u64 linear = 0;
+       u32 i, cur_idx;
+       s16 txpwr;
+
+       if (!tas->enable || src == RTW89_SAR_SOURCE_NONE)
+               return;
+
+       cur_idx = le32_to_cpu(content->cur_idx);
+       for (i = 0; i < cur_idx; i++) {
+               txpwr = le16_to_cpu(content->txpwr_history[i]);
+               linear += rtw89_db_quarter_to_linear(txpwr);
+
+               rtw89_debug(rtwdev, RTW89_DBG_SAR,
+                           "tas: index: %u, txpwr: %d\n", i, txpwr);
+       }
+
+       if (cur_idx == 0)
+               tas->instant_txpwr = rtw89_db_to_linear(0);
+       else
+               tas->instant_txpwr = DIV_ROUND_DOWN_ULL(linear, cur_idx);
+}
+
 static void rtw89_phy_c2h_rfk_rpt_log(struct rtw89_dev *rtwdev,
                                      enum rtw89_phy_c2h_rfk_log_func func,
                                      void *content, u16 len)
@@ -3423,6 +3451,13 @@ static void rtw89_phy_c2h_rfk_rpt_log(struct rtw89_dev *rtwdev,
                rtw89_debug(rtwdev, RTW89_DBG_RFK, "[TXGAPK]rpt power_d[1] = %*ph\n",
                            (int)sizeof(txgapk->power_d[1]), txgapk->power_d[1]);
                return;
+       case RTW89_PHY_C2H_RFK_LOG_FUNC_TAS_PWR:
+               if (len != sizeof(struct rtw89_c2h_rf_tas_rpt_log))
+                       goto out;
+
+               rtw89_phy_c2h_rfk_tas_pwr(rtwdev, content);
+
+               return;
        default:
                break;
        }
@@ -3475,9 +3510,6 @@ static void rtw89_phy_c2h_rfk_log(struct rtw89_dev *rtwdev, struct sk_buff *c2h,
        u16 chunk_len;
        bool handled;
 
-       if (!rtw89_debug_is_enabled(rtwdev, RTW89_DBG_RFK))
-               return;
-
        log_ptr += sizeof(*c2h_hdr);
        len -= sizeof(*c2h_hdr);
 
@@ -3554,6 +3586,13 @@ rtw89_phy_c2h_rfk_log_txgapk(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32
                              RTW89_PHY_C2H_RFK_LOG_FUNC_TXGAPK, "TXGAPK");
 }
 
+static void
+rtw89_phy_c2h_rfk_log_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+{
+       rtw89_phy_c2h_rfk_log(rtwdev, c2h, len,
+                             RTW89_PHY_C2H_RFK_LOG_FUNC_TAS_PWR, "TAS");
+}
+
 static
 void (* const rtw89_phy_c2h_rfk_log_handler[])(struct rtw89_dev *rtwdev,
                                               struct sk_buff *c2h, u32 len) = {
@@ -3563,6 +3602,7 @@ void (* const rtw89_phy_c2h_rfk_log_handler[])(struct rtw89_dev *rtwdev,
        [RTW89_PHY_C2H_RFK_LOG_FUNC_RXDCK] = rtw89_phy_c2h_rfk_log_rxdck,
        [RTW89_PHY_C2H_RFK_LOG_FUNC_TSSI] = rtw89_phy_c2h_rfk_log_tssi,
        [RTW89_PHY_C2H_RFK_LOG_FUNC_TXGAPK] = rtw89_phy_c2h_rfk_log_txgapk,
+       [RTW89_PHY_C2H_RFK_LOG_FUNC_TAS_PWR] = rtw89_phy_c2h_rfk_log_tas_pwr,
 };
 
 static
@@ -3625,39 +3665,19 @@ rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u3
 }
 
 static void
-rtw89_phy_c2h_rfk_log_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
+rtw89_phy_c2h_rfk_report_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
 {
-       const struct rtw89_c2h_rf_tas_info *rf_tas =
+       const struct rtw89_c2h_rf_tas_info *report =
                (const struct rtw89_c2h_rf_tas_info *)c2h->data;
-       const enum rtw89_sar_sources src = rtwdev->sar.src;
-       struct rtw89_tas_info *tas = &rtwdev->tas;
-       u64 linear = 0;
-       u32 i, cur_idx;
-       s16 txpwr;
 
-       if (!tas->enable || src == RTW89_SAR_SOURCE_NONE)
-               return;
-
-       cur_idx = le32_to_cpu(rf_tas->cur_idx);
-       for (i = 0; i < cur_idx; i++) {
-               txpwr = (s16)le16_to_cpu(rf_tas->txpwr_history[i]);
-               linear += rtw89_db_quarter_to_linear(txpwr);
-
-               rtw89_debug(rtwdev, RTW89_DBG_SAR,
-                           "tas: index: %u, txpwr: %d\n", i, txpwr);
-       }
-
-       if (cur_idx == 0)
-               tas->instant_txpwr = rtw89_db_to_linear(0);
-       else
-               tas->instant_txpwr = DIV_ROUND_DOWN_ULL(linear, cur_idx);
+       rtw89_phy_c2h_rfk_tas_pwr(rtwdev, &report->content);
 }
 
 static
 void (* const rtw89_phy_c2h_rfk_report_handler[])(struct rtw89_dev *rtwdev,
                                                  struct sk_buff *c2h, u32 len) = {
        [RTW89_PHY_C2H_RFK_REPORT_FUNC_STATE] = rtw89_phy_c2h_rfk_report_state,
-       [RTW89_PHY_C2H_RFK_LOG_TAS_PWR] = rtw89_phy_c2h_rfk_log_tas_pwr,
+       [RTW89_PHY_C2H_RFK_REPORT_FUNC_TAS_PWR] = rtw89_phy_c2h_rfk_report_tas_pwr,
 };
 
 bool rtw89_phy_c2h_chk_atomic(struct rtw89_dev *rtwdev, u8 class, u8 func)
index 674397c4b9a9f6befce64bd7f821f4f54f6689d2..9caacffd0af8682ee4300b43b1600a2f66470de7 100644 (file)
@@ -149,13 +149,14 @@ enum rtw89_phy_c2h_rfk_log_func {
        RTW89_PHY_C2H_RFK_LOG_FUNC_RXDCK = 3,
        RTW89_PHY_C2H_RFK_LOG_FUNC_TSSI = 4,
        RTW89_PHY_C2H_RFK_LOG_FUNC_TXGAPK = 5,
+       RTW89_PHY_C2H_RFK_LOG_FUNC_TAS_PWR = 9,
 
        RTW89_PHY_C2H_RFK_LOG_FUNC_NUM,
 };
 
 enum rtw89_phy_c2h_rfk_report_func {
        RTW89_PHY_C2H_RFK_REPORT_FUNC_STATE = 0,
-       RTW89_PHY_C2H_RFK_LOG_TAS_PWR = 6,
+       RTW89_PHY_C2H_RFK_REPORT_FUNC_TAS_PWR = 6,
 };
 
 enum rtw89_phy_c2h_dm_func {
index 8eb0cb9bb23e4604d10d5fc2d2ca680ddf700025..6aa19ad259acab9c1261e91ec61d85e8be536ff4 100644 (file)
@@ -2765,6 +2765,10 @@ static int rtw8922a_mac_disable_bb_rf(struct rtw89_dev *rtwdev)
        return 0;
 }
 
+static const struct rtw89_chanctx_listener rtw8922a_chanctx_listener = {
+       .callbacks[RTW89_CHANCTX_CALLBACK_TAS] = rtw89_tas_chanctx_cb,
+};
+
 #ifdef CONFIG_PM
 static const struct wiphy_wowlan_support rtw_wowlan_stub_8922a = {
        .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT |
@@ -2876,6 +2880,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
        .nctl_post_table        = NULL,
        .dflt_parms             = NULL, /* load parm from fw */
        .rfe_parms_conf         = NULL, /* load parm from fw */
+       .chanctx_listener       = &rtw8922a_chanctx_listener,
        .txpwr_factor_bb        = 3,
        .txpwr_factor_rf        = 2,
        .txpwr_factor_mac       = 1,
@@ -2895,7 +2900,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
                                  BIT(NL80211_CHAN_WIDTH_160),
        .support_unii4          = true,
        .support_ant_gain       = true,
-       .support_tas            = false,
+       .support_tas            = true,
        .support_sar_by_ant     = true,
        .support_noise          = false,
        .ul_tb_waveform_ctrl    = false,
index 7f568ffb3766f46d0d5f28f06478a4218f0b7cb1..ef7feccccd5e19d9f27299b998d300123a37c9d3 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "acpi.h"
 #include "debug.h"
+#include "fw.h"
 #include "phy.h"
 #include "reg.h"
 #include "sar.h"
@@ -843,6 +844,20 @@ void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev,
 }
 EXPORT_SYMBOL(rtw89_tas_chanctx_cb);
 
+void rtw89_tas_fw_timer_enable(struct rtw89_dev *rtwdev, bool enable)
+{
+       const struct rtw89_chip_info *chip = rtwdev->chip;
+       struct rtw89_tas_info *tas = &rtwdev->tas;
+
+       if (!tas->enable)
+               return;
+
+       if (chip->chip_gen == RTW89_CHIP_AX)
+               return;
+
+       rtw89_fw_h2c_rf_tas_trigger(rtwdev, enable);
+}
+
 void rtw89_sar_init(struct rtw89_dev *rtwdev)
 {
        rtw89_set_sar_from_acpi(rtwdev);
index 4b7f3d44f57bf4facc47100c7b5bd7b32455670b..60b3aec5b3be1ffe27c62220f5b01596954bdc4f 100644 (file)
@@ -37,6 +37,7 @@ void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force);
 void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start);
 void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev,
                          enum rtw89_chanctx_state state);
+void rtw89_tas_fw_timer_enable(struct rtw89_dev *rtwdev, bool enable);
 void rtw89_sar_init(struct rtw89_dev *rtwdev);
 void rtw89_sar_track(struct rtw89_dev *rtwdev);