From 06cccc2ebbe6c8a20f714f3a0ff3ff489d3004bb Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 17 Mar 2025 18:22:35 +0800 Subject: [PATCH 01/16] wifi: mt76: mt76x2u: add TP-Link TL-WDN6200 ID to device table The TP-Link TL-WDN6200 "Driverless" version cards use a MT7612U chipset. Add the USB ID to mt76x2u driver. Signed-off-by: Icenowy Zheng Link: https://patch.msgid.link/20250317102235.1421726-1-uwu@icenowy.me Signed-off-by: Felix Fietkau --- drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index 3747f9ed31e6..84ef80ab4afb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -22,6 +22,7 @@ static const struct usb_device_id mt76x2u_device_table[] = { { USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */ { USB_DEVICE(0x045e, 0x02e6) }, /* XBox One Wireless Adapter */ { USB_DEVICE(0x045e, 0x02fe) }, /* XBox One Wireless Adapter */ + { USB_DEVICE(0x2357, 0x0137) }, /* TP-Link TL-WDN6200 */ { }, }; -- 2.51.0 From 1794d7ab34d2221ac7eb921b171e75b856e10561 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 19 Mar 2025 19:44:25 +0100 Subject: [PATCH 02/16] wifi: mt76: mt7996: fix locking in mt7996_mac_sta_rc_work() The 'continue' statements need to be under spinlock, since the spinlock needs to be held as a loop invariant. Fixes: 0762bdd30279 ("wifi: mt76: mt7996: rework mt7996_mac_sta_rc_work to support MLO") Signed-off-by: Johannes Berg --- drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index a52b4e71b267..d89c06f47997 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -2334,7 +2334,6 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) changed = msta_link->changed; msta_link->changed = 0; - spin_unlock_bh(&dev->mt76.sta_poll_lock); sta = wcid_to_sta(&msta_link->wcid); link_id = msta_link->wcid.link_id; @@ -2354,6 +2353,8 @@ void mt7996_mac_sta_rc_work(struct work_struct *work) if (!link_conf) continue; + spin_unlock_bh(&dev->mt76.sta_poll_lock); + link = (struct mt7996_vif_link *)mlink; if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | -- 2.51.0 From 81433a8a4f404d3e52e8e591fdc9409101dadf57 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Fri, 21 Mar 2025 11:47:36 +0800 Subject: [PATCH 03/16] wifi: rtw89: set 2TX for 1SS rate by default To improve performance in range, for 1SS rate, transmit the same signal on 2 antenna, which is called 2TX. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250321034736.6269-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 8643b17866f8..c7172334f895 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -3065,8 +3065,8 @@ static void __rtw89_fw_h2c_set_tx_path(struct rtw89_dev *rtwdev, ntx_path = RF_A; map_b = 0; } else { - ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_B; - map_b = hal->antenna_tx == RF_AB ? 1 : 0; + ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_AB; + map_b = ntx_path == RF_AB ? 1 : 0; } SET_CMC_TBL_NTX_PATH_EN(skb->data, ntx_path); -- 2.51.0 From bc1265b5c982fc73077812a8be727ba3d734164b Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 25 Mar 2025 10:54:24 +0800 Subject: [PATCH 04/16] wifi: rtw89: fw: cast mfw_hdr pointer from address of zeroth byte of firmware->data The firmware->size is validated before using firmware->data, but Coverity still reports: Downcasting "firmware->data" from "u8 const *" to "struct rtw89_mfw_hdr" implies that the data that this pointer points to is tainted." Using &firmware->data[0] to avoid the warning. No change logic at all. Addresses-Coverity-ID: 1494046 ("Untrusted loop bound") Addresses-Coverity-ID: 1544385 ("Untrusted array index read") Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250325025424.14079-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/fw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index c7172334f895..16499fce94cc 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -554,7 +554,7 @@ const struct rtw89_mfw_hdr *rtw89_mfw_get_hdr_ptr(struct rtw89_dev *rtwdev, if (sizeof(*mfw_hdr) > firmware->size) return NULL; - mfw_hdr = (const struct rtw89_mfw_hdr *)firmware->data; + mfw_hdr = (const struct rtw89_mfw_hdr *)&firmware->data[0]; if (mfw_hdr->sig != RTW89_MFW_SIG) return NULL; -- 2.51.0 From e95129709a86cecdb5bea242d2ad489db2ad2ef5 Mon Sep 17 00:00:00 2001 From: Ping-Ke Shih Date: Tue, 25 Mar 2025 11:10:21 +0800 Subject: [PATCH 05/16] wifi: rtw89: phy: reset value of force TX power for MAC ID The force TX power function is disabled, but the force TX power value is preserved, causing misunderstand the behavior in debug. Clear all values. Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250325031021.15619-1-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/phy_be.c | 2 +- drivers/net/wireless/realtek/rtw89/reg.h | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 37d8f254ae32..d321cf1fc485 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -362,7 +362,7 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ENON, 0); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ON, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_FORCE_MACID, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ON, 0); + rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ALL, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_COEX_CTRL, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_COEX_ON, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index c776954ad360..6ef546d1b575 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -7601,7 +7601,15 @@ #define B_BE_PWR_FORCE_RU_ON BIT(18) #define B_BE_PWR_FORCE_RU_ENON BIT(28) #define R_BE_PWR_FORCE_MACID 0x11A48 -#define B_BE_PWR_FORCE_MACID_ON BIT(9) +#define B_BE_PWR_FORCE_MACID_DBM_ON BIT(9) +#define B_BE_PWR_FORCE_MACID_DBM_VAL GENMASK(17, 10) +#define B_BE_PWR_FORCE_MACID_EN_VAL BIT(18) +#define B_BE_PWR_FORCE_MACID_EN_ON BIT(19) +#define B_BE_PWR_FORCE_MACID_ALL \ + (B_BE_PWR_FORCE_MACID_DBM_ON | \ + B_BE_PWR_FORCE_MACID_DBM_VAL | \ + B_BE_PWR_FORCE_MACID_EN_VAL | \ + B_BE_PWR_FORCE_MACID_EN_ON) #define R_BE_PWR_REG_CTRL 0x11A50 #define B_BE_PWR_BT_EN BIT(23) -- 2.51.0 From 8bde621f124b593e3d1570a40d02155690714e3f Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:32 +0800 Subject: [PATCH 06/16] wifi: rtw89: fix typo of "access" in rtw89_sar_info description The "acces" should be "access". So, fix it. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-2-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 4be05d6cad18..8e9697679aaa 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4631,7 +4631,7 @@ struct rtw89_sar_cfg_common { }; struct rtw89_sar_info { - /* used to decide how to acces SAR cfg union */ + /* used to decide how to access SAR cfg union */ enum rtw89_sar_sources src; /* reserved for different knids of SAR cfg struct. -- 2.51.0 From 4cecf99124925bf27ed17a0f75cf44409b1ac6ae Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:33 +0800 Subject: [PATCH 07/16] wifi: rtw89: regd: introduce string getter for reuse Introduce a function to get the string for a given regulatory. It will be used in the following. Besides, drop similar things in debug code and use this too. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-3-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/debug.c | 29 +------------------- drivers/net/wireless/realtek/rtw89/regd.c | 32 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 8e9697679aaa..18340a9cd72b 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -7185,6 +7185,7 @@ void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, bool rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate, u16 *bitrate); int rtw89_regd_setup(struct rtw89_dev *rtwdev); int rtw89_regd_init_hint(struct rtw89_dev *rtwdev); +const char *rtw89_regd_get_string(enum rtw89_regulation_type regd); void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats); int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond); diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index f2c5753fd386..5105a192b15a 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -854,11 +854,6 @@ static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char *buf, size_t buf return p - buf; } -#define case_REGD(_regd) \ - case RTW89_ ## _regd: \ - p += scnprintf(p, end - p, #_regd "\n"); \ - break - static int __print_regd(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, const struct rtw89_chan *chan) { @@ -866,33 +861,11 @@ static int __print_regd(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, u8 band = chan->band_type; u8 regd = rtw89_regd_get(rtwdev, band); - switch (regd) { - default: - p += scnprintf(p, end - p, "UNKNOWN: %d\n", regd); - break; - case_REGD(WW); - case_REGD(ETSI); - case_REGD(FCC); - case_REGD(MKK); - case_REGD(NA); - case_REGD(IC); - case_REGD(KCC); - case_REGD(NCC); - case_REGD(CHILE); - case_REGD(ACMA); - case_REGD(MEXICO); - case_REGD(UKRAINE); - case_REGD(CN); - case_REGD(QATAR); - case_REGD(UK); - case_REGD(THAILAND); - } + p += scnprintf(p, end - p, "%s\n", rtw89_regd_get_string(regd)); return p - buf; } -#undef case_REGD - struct dbgfs_txpwr_table { const struct txpwr_map *byr; const struct txpwr_map *lmt; diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 655323a79608..49e9540f2049 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -588,6 +588,38 @@ bottom: kfree(sband); } +#define RTW89_DEF_REGD_STR(regd) \ + [RTW89_ ## regd] = #regd + +static const char * const rtw89_regd_string[] = { + RTW89_DEF_REGD_STR(WW), + RTW89_DEF_REGD_STR(ETSI), + RTW89_DEF_REGD_STR(FCC), + RTW89_DEF_REGD_STR(MKK), + RTW89_DEF_REGD_STR(NA), + RTW89_DEF_REGD_STR(IC), + RTW89_DEF_REGD_STR(KCC), + RTW89_DEF_REGD_STR(ACMA), + RTW89_DEF_REGD_STR(NCC), + RTW89_DEF_REGD_STR(MEXICO), + RTW89_DEF_REGD_STR(CHILE), + RTW89_DEF_REGD_STR(UKRAINE), + RTW89_DEF_REGD_STR(CN), + RTW89_DEF_REGD_STR(QATAR), + RTW89_DEF_REGD_STR(UK), + RTW89_DEF_REGD_STR(THAILAND), +}; + +static_assert(ARRAY_SIZE(rtw89_regd_string) == RTW89_REGD_NUM); + +const char *rtw89_regd_get_string(enum rtw89_regulation_type regd) +{ + if (regd < 0 || regd >= RTW89_REGD_NUM) + return "(unknown)"; + + return rtw89_regd_string[regd]; +} + int rtw89_regd_setup(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; -- 2.51.0 From 1e262fc8df10aa7b55694bf2008dd0c1f8ae8acf Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:34 +0800 Subject: [PATCH 08/16] wifi: rtw89: sar: introduce structure to wrap query parameters The following implementations will support SAR source from ACPI/BIOS. And when querying, it needs to take more parameters into account. To avoid changing function prototype of querying SAR everytime when new SAR source is introduced, wrap query parameters into a structure first. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-4-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/debug.c | 4 +++- drivers/net/wireless/realtek/rtw89/phy.c | 10 ++++++++-- drivers/net/wireless/realtek/rtw89/sar.c | 16 ++++++++++------ drivers/net/wireless/realtek/rtw89/sar.h | 11 ++++++++--- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 5105a192b15a..a7ef9cb9c6fe 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -922,6 +922,7 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; + struct rtw89_sar_parm sar_parm = {}; const struct dbgfs_txpwr_table *tbl; const struct rtw89_chan *chan; char *p = buf, *end = buf + bufsz; @@ -931,11 +932,12 @@ ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, rtw89_leave_ps_mode(rtwdev); chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + sar_parm.center_freq = chan->freq; p += rtw89_debug_priv_txpwr_table_get_regd(rtwdev, p, end - p, chan); p += scnprintf(p, end - p, "[SAR]\n"); - p += rtw89_print_sar(rtwdev, p, end - p, chan->freq); + p += rtw89_print_sar(rtwdev, p, end - p, &sar_parm); p += scnprintf(p, end - p, "[TAS]\n"); p += rtw89_print_tas(rtwdev, p, end - p); diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index f4eee642e5ce..64eb4154a696 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2264,6 +2264,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); u8 regd = rtw89_regd_get(rtwdev, band); u8 reg6 = regulatory->reg_6ghz_power; + struct rtw89_sar_parm sar_parm = { + .center_freq = freq, + }; s8 lmt = 0, sar, offset; s8 cstr; @@ -2298,7 +2301,7 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq); lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt + offset); - sar = rtw89_query_sar(rtwdev, freq); + sar = rtw89_query_sar(rtwdev, &sar_parm); cstr = rtw89_phy_get_tpe_constraint(rtwdev, band); return min3(lmt, sar, cstr); @@ -2524,6 +2527,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); u8 regd = rtw89_regd_get(rtwdev, band); u8 reg6 = regulatory->reg_6ghz_power; + struct rtw89_sar_parm sar_parm = { + .center_freq = freq, + }; s8 lmt_ru = 0, sar, offset; s8 cstr; @@ -2558,7 +2564,7 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq); lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt_ru + offset); - sar = rtw89_query_sar(rtwdev, freq); + sar = rtw89_query_sar(rtwdev, &sar_parm); cstr = rtw89_phy_get_tpe_constraint(rtwdev, band); return min3(lmt_ru, sar, cstr); diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 0b5af9528702..95255e02f1bb 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -57,10 +57,12 @@ static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev, } static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, - u32 center_freq, s32 *cfg) + const struct rtw89_sar_parm *sar_parm, + s32 *cfg) { struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common; enum rtw89_sar_subband subband_l, subband_h; + u32 center_freq = sar_parm->center_freq; const struct rtw89_6ghz_span *span; span = rtw89_get_6ghz_span(rtwdev, center_freq); @@ -175,7 +177,7 @@ static const char *rtw89_tas_state_str(enum rtw89_tas_state state) } } -s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) +s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm) { const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ @@ -191,7 +193,7 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) if (src == RTW89_SAR_SOURCE_NONE) return RTW89_SAR_TXPWR_MAC_MAX; - ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); + ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg); if (ret) return RTW89_SAR_TXPWR_MAC_MAX; @@ -217,7 +219,7 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) } int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, - u32 center_freq) + const struct rtw89_sar_parm *sar_parm) { const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ @@ -238,7 +240,7 @@ int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, p += scnprintf(p, end - p, "source: %d (%s)\n", src, sar_hdl->descr_sar_source); - ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); + ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg); if (ret) { p += scnprintf(p, end - p, "config: return code: %d\n", ret); p += scnprintf(p, end - p, @@ -369,12 +371,14 @@ static bool rtw89_tas_query_sar_config(struct rtw89_dev *rtwdev, s32 *cfg) const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; + struct rtw89_sar_parm sar_parm = {}; int ret; if (src == RTW89_SAR_SOURCE_NONE) return false; - ret = sar_hdl->query_sar_config(rtwdev, chan->freq, cfg); + sar_parm.center_freq = chan->freq; + ret = sar_hdl->query_sar_config(rtwdev, &sar_parm, cfg); if (ret) return false; diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h index 0df1661db9a8..3e4159c08409 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.h +++ b/drivers/net/wireless/realtek/rtw89/sar.h @@ -10,17 +10,22 @@ #define RTW89_SAR_TXPWR_MAC_MAX 63 #define RTW89_SAR_TXPWR_MAC_MIN -64 +struct rtw89_sar_parm { + u32 center_freq; +}; + struct rtw89_sar_handler { const char *descr_sar_source; u8 txpwr_factor_sar; - int (*query_sar_config)(struct rtw89_dev *rtwdev, u32 center_freq, s32 *cfg); + int (*query_sar_config)(struct rtw89_dev *rtwdev, + const struct rtw89_sar_parm *sar_parm, s32 *cfg); }; extern const struct cfg80211_sar_capa rtw89_sar_capa; -s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq); +s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm); int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, - u32 center_freq); + const struct rtw89_sar_parm *sar_parm); int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, const struct cfg80211_sar_specs *sar); -- 2.51.0 From 88ca3107d2ce06448018e0571f7c0f1b40f57b55 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:35 +0800 Subject: [PATCH 09/16] wifi: rtw89: sar: add skeleton for SAR configuration via ACPI To support SAR configuration in BIOS via ACPI, add related subbnad/band converting/handling function and define SW format to store result after parsing. Then, register a new SAR source, i.e. ACPI, into SAR flow and implement its query function. Besides, tweak priority of common SAR to be the highest. And, ACPI SAR can just be configured once when no other sources is already working. For now, evaluating SAR via ACPI returns -ENOENT, i.e. ACPI SAR doesn't really work yet. The evaluating flow will be implemented in the following. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-5-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/acpi.c | 79 ++++++++++++ drivers/net/wireless/realtek/rtw89/acpi.h | 7 ++ drivers/net/wireless/realtek/rtw89/core.c | 4 +- drivers/net/wireless/realtek/rtw89/core.h | 44 +++++++ drivers/net/wireless/realtek/rtw89/phy.c | 2 + drivers/net/wireless/realtek/rtw89/sar.c | 143 ++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/sar.h | 3 +- 7 files changed, 269 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c index f5dedb12c129..b870a3df4072 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.c +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -195,3 +195,82 @@ out: ACPI_FREE(obj); return ret; } + +enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, + u32 center_freq) +{ + switch (center_freq) { + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "center freq %u to ACPI SAR subband is unhandled\n", + center_freq); + fallthrough; + case 2412 ... 2484: + return RTW89_ACPI_SAR_2GHZ_SUBBAND; + case 5180 ... 5240: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_1; + case 5250 ... 5320: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_2; + case 5500 ... 5720: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_2E; + case 5745 ... 5885: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4; + case 5955 ... 6155: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L; + case 6175 ... 6415: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H; + case 6435 ... 6515: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_6; + case 6535 ... 6695: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L; + case 6715 ... 6855: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H; + + /* freq 6875 (ch 185, 20MHz) spans RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H + * and RTW89_ACPI_SAR_6GHZ_SUBBAND_8, so directly describe it with + * struct rtw89_6ghz_span. + */ + + case 6895 ... 7115: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_8; + } +} + +enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, + enum rtw89_acpi_sar_subband subband) +{ + switch (subband) { + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "ACPI SAR subband %u to band is unhandled\n", subband); + fallthrough; + case RTW89_ACPI_SAR_2GHZ_SUBBAND: + return RTW89_BAND_2G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_1: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_2: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_2E: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_6: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_8: + return RTW89_BAND_6G; + } +} + +int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + return -ENOENT; +} diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h index b43ab106e44d..a7f583a2164f 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.h +++ b/drivers/net/wireless/realtek/rtw89/acpi.h @@ -70,10 +70,17 @@ struct rtw89_acpi_rtag_result { u8 ant_gain_table[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR]; } __packed; +enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, + u32 center_freq); +enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, + enum rtw89_acpi_sar_subband subband); + int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, enum rtw89_acpi_dsm_func func, struct rtw89_acpi_dsm_result *res); int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev, struct rtw89_acpi_rtag_result *res); +int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg); #endif diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index cc9b014457ac..2088c6f4a98e 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -211,6 +211,8 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = { [RTW89_6GHZ_SPAN_IDX(center_freq)] = { \ .sar_subband_low = RTW89_SAR_6GHZ_ ## subband_l, \ .sar_subband_high = RTW89_SAR_6GHZ_ ## subband_h, \ + .acpi_sar_subband_low = RTW89_ACPI_SAR_6GHZ_ ## subband_l, \ + .acpi_sar_subband_high = RTW89_ACPI_SAR_6GHZ_ ## subband_h, \ .ant_gain_subband_low = RTW89_ANT_GAIN_6GHZ_ ## subband_l, \ .ant_gain_subband_high = RTW89_ANT_GAIN_6GHZ_ ## subband_h, \ } @@ -4919,7 +4921,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtw89_ser_init(rtwdev); rtw89_entity_init(rtwdev); - rtw89_tas_init(rtwdev); + rtw89_sar_init(rtwdev); rtw89_phy_ant_gain_init(rtwdev); return 0; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 18340a9cd72b..de0734c44985 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4606,6 +4606,7 @@ struct rtw89_cam_info { enum rtw89_sar_sources { RTW89_SAR_SOURCE_NONE, RTW89_SAR_SOURCE_COMMON, + RTW89_SAR_SOURCE_ACPI, RTW89_SAR_SOURCE_NR, }; @@ -4630,6 +4631,45 @@ struct rtw89_sar_cfg_common { s32 cfg[RTW89_SAR_SUBBAND_NR]; }; +enum rtw89_acpi_sar_subband { + RTW89_ACPI_SAR_2GHZ_SUBBAND, + RTW89_ACPI_SAR_5GHZ_SUBBAND_1, /* U-NII-1 */ + RTW89_ACPI_SAR_5GHZ_SUBBAND_2, /* U-NII-2 */ + RTW89_ACPI_SAR_5GHZ_SUBBAND_2E, /* U-NII-2-Extended */ + RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4, /* U-NII-3 and U-NII-4 */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L, /* U-NII-5 lower part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H, /* U-NII-5 higher part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_6, /* U-NII-6 */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L, /* U-NII-7 lower part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H, /* U-NII-7 higher part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_8, /* U-NII-8 */ + + NUM_OF_RTW89_ACPI_SAR_SUBBAND, +}; + +#define TXPWR_FACTOR_OF_RTW89_ACPI_SAR 3 /* unit: 0.125 dBm */ +#define MAX_VAL_OF_RTW89_ACPI_SAR S16_MAX +#define MIN_VAL_OF_RTW89_ACPI_SAR S16_MIN +#define MAX_NUM_OF_RTW89_ACPI_SAR_TBL 6 +#define NUM_OF_RTW89_ACPI_SAR_RF_PATH (RF_PATH_B + 1) + +struct rtw89_sar_entry_from_acpi { + s16 v[NUM_OF_RTW89_ACPI_SAR_SUBBAND][NUM_OF_RTW89_ACPI_SAR_RF_PATH]; +}; + +struct rtw89_sar_table_from_acpi { + /* If this table is active, must fill all fields according to either + * configuration in BIOS or some default values for SAR to work well. + */ + struct rtw89_sar_entry_from_acpi entries[RTW89_REGD_NUM]; +}; + +struct rtw89_sar_cfg_acpi { + u8 downgrade_2tx; + unsigned int valid_num; + struct rtw89_sar_table_from_acpi tables[MAX_NUM_OF_RTW89_ACPI_SAR_TBL]; +}; + struct rtw89_sar_info { /* used to decide how to access SAR cfg union */ enum rtw89_sar_sources src; @@ -4639,6 +4679,7 @@ struct rtw89_sar_info { */ union { struct rtw89_sar_cfg_common cfg_common; + struct rtw89_sar_cfg_acpi cfg_acpi; }; }; @@ -4674,11 +4715,14 @@ struct rtw89_ant_gain_info { struct rtw89_6ghz_span { enum rtw89_sar_subband sar_subband_low; enum rtw89_sar_subband sar_subband_high; + enum rtw89_acpi_sar_subband acpi_sar_subband_low; + enum rtw89_acpi_sar_subband acpi_sar_subband_high; enum rtw89_ant_gain_subband ant_gain_subband_low; enum rtw89_ant_gain_subband ant_gain_subband_high; }; #define RTW89_SAR_SPAN_VALID(span) ((span)->sar_subband_high) +#define RTW89_ACPI_SAR_SPAN_VALID(span) ((span)->acpi_sar_subband_high) #define RTW89_ANT_GAIN_SPAN_VALID(span) ((span)->ant_gain_subband_high) enum rtw89_tas_state { diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index 64eb4154a696..270f40e44c0b 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2266,6 +2266,7 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, u8 reg6 = regulatory->reg_6ghz_power; struct rtw89_sar_parm sar_parm = { .center_freq = freq, + .ntx = ntx, }; s8 lmt = 0, sar, offset; s8 cstr; @@ -2529,6 +2530,7 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, u8 reg6 = regulatory->reg_6ghz_power; struct rtw89_sar_parm sar_parm = { .center_freq = freq, + .ntx = ntx, }; s8 lmt_ru = 0, sar, offset; s8 cstr; diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 95255e02f1bb..8bf5b5d9ef37 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -92,6 +92,53 @@ static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, return 0; } +static +s32 rtw89_sar_cfg_acpi_get_min(const struct rtw89_sar_entry_from_acpi *ent, + enum rtw89_rf_path path, + enum rtw89_acpi_sar_subband subband_low, + enum rtw89_acpi_sar_subband subband_high) +{ + return min(ent->v[subband_low][path], ent->v[subband_high][path]); +} + +static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev, + const struct rtw89_sar_parm *sar_parm, + s32 *cfg) +{ + const struct rtw89_sar_cfg_acpi *rtwsar = &rtwdev->sar.cfg_acpi; + const struct rtw89_sar_table_from_acpi *tbl = rtwsar->tables; + enum rtw89_acpi_sar_subband subband_l, subband_h; + const struct rtw89_sar_entry_from_acpi *ent; + u32 center_freq = sar_parm->center_freq; + const struct rtw89_6ghz_span *span; + enum rtw89_regulation_type regd; + enum rtw89_band band; + s32 cfg_a, cfg_b; + + span = rtw89_get_6ghz_span(rtwdev, center_freq); + + if (span && RTW89_ACPI_SAR_SPAN_VALID(span)) { + subband_l = span->acpi_sar_subband_low; + subband_h = span->acpi_sar_subband_high; + } else { + subband_l = rtw89_acpi_sar_get_subband(rtwdev, center_freq); + subband_h = subband_l; + } + + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband_l); + regd = rtw89_regd_get(rtwdev, band); + ent = &tbl->entries[regd]; + + cfg_a = rtw89_sar_cfg_acpi_get_min(ent, RF_PATH_A, subband_l, subband_h); + cfg_b = rtw89_sar_cfg_acpi_get_min(ent, RF_PATH_B, subband_l, subband_h); + *cfg = min(cfg_a, cfg_b); + + if (sar_parm->ntx == RTW89_2TX) + *cfg -= rtwsar->downgrade_2tx; + + return 0; +} + static const struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { [RTW89_SAR_SOURCE_COMMON] = { @@ -99,6 +146,11 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { .txpwr_factor_sar = 2, .query_sar_config = rtw89_query_sar_config_common, }, + [RTW89_SAR_SOURCE_ACPI] = { + .descr_sar_source = "RTW89_SAR_SOURCE_ACPI", + .txpwr_factor_sar = TXPWR_FACTOR_OF_RTW89_ACPI_SAR, + .query_sar_config = rtw89_query_sar_config_acpi, + }, }; #define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data) \ @@ -288,16 +340,7 @@ out: static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, const struct rtw89_sar_cfg_common *sar) { - enum rtw89_sar_sources src; - - lockdep_assert_wiphy(rtwdev->hw->wiphy); - - src = rtwdev->sar.src; - if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { - rtw89_warn(rtwdev, "SAR source: %d is in use", src); - return -EBUSY; - } - + /* let common SAR have the highest priority; always apply it */ rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); rtw89_core_set_chip_txpwr(rtwdev); rtw89_tas_reset(rtwdev, false); @@ -365,6 +408,78 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, return rtw89_apply_sar_common(rtwdev, &sar_common); } +static void rtw89_apply_sar_acpi(struct rtw89_dev *rtwdev, + const struct rtw89_sar_cfg_acpi *sar) +{ + const struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_sar_entry_from_acpi *ent; + enum rtw89_sar_sources src; + unsigned int i, j, k; + + src = rtwdev->sar.src; + if (src != RTW89_SAR_SOURCE_NONE) { + rtw89_warn(rtwdev, "SAR source: %d is in use", src); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "SAR-ACPI downgrade 2TX: %u (unit: 1/%lu dBm)\n", + sar->downgrade_2tx, BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR)); + + for (i = 0; i < sar->valid_num; i++) { + tbl = &sar->tables[i]; + + for (j = 0; j < RTW89_REGD_NUM; j++) { + ent = &tbl->entries[j]; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "SAR-ACPI-[%u] REGD-%s (unit: 1/%lu dBm)\n", + i, rtw89_regd_get_string(j), + BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR)); + + for (k = 0; k < NUM_OF_RTW89_ACPI_SAR_SUBBAND; k++) + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "On subband %u, { %d, %d }\n", k, + ent->v[k][RF_PATH_A], ent->v[k][RF_PATH_B]); + } + } + + rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_ACPI, cfg_acpi, sar); + + /* SAR via ACPI is only configured in the early initial phase, so + * it does not seem necessary to reset txpwr related things here. + */ +} + +static void rtw89_set_sar_from_acpi(struct rtw89_dev *rtwdev) +{ + struct rtw89_sar_cfg_acpi *cfg; + int ret; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return; + + ret = rtw89_acpi_evaluate_sar(rtwdev, cfg); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "evaluating ACPI SAR returns %d\n", ret); + goto out; + } + + if (unlikely(!cfg->valid_num)) { + rtw89_debug(rtwdev, RTW89_DBG_SAR, "no valid SAR table from ACPI\n"); + goto out; + } + + rtw89_apply_sar_acpi(rtwdev, cfg); + +out: + kfree(cfg); +} + static bool rtw89_tas_query_sar_config(struct rtw89_dev *rtwdev, s32 *cfg) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); @@ -524,7 +639,7 @@ static void rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) rtw89_tas_state_update(rtwdev, state); } -void rtw89_tas_init(struct rtw89_dev *rtwdev) +static void rtw89_tas_init(struct rtw89_dev *rtwdev) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_tas_info *tas = &rtwdev->tas; @@ -671,3 +786,9 @@ void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev, } } EXPORT_SYMBOL(rtw89_tas_chanctx_cb); + +void rtw89_sar_init(struct rtw89_dev *rtwdev) +{ + rtw89_set_sar_from_acpi(rtwdev); + rtw89_tas_init(rtwdev); +} diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h index 3e4159c08409..2d30e0d8be1f 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.h +++ b/drivers/net/wireless/realtek/rtw89/sar.h @@ -12,6 +12,7 @@ struct rtw89_sar_parm { u32 center_freq; + enum rtw89_ntx ntx; }; struct rtw89_sar_handler { @@ -29,11 +30,11 @@ int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, const struct cfg80211_sar_specs *sar); -void rtw89_tas_init(struct rtw89_dev *rtwdev); void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force); void rtw89_tas_track(struct rtw89_dev *rtwdev); 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_sar_init(struct rtw89_dev *rtwdev); #endif -- 2.51.0 From 5ee5f848469f584adb7010acb8ac656a544f5654 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:36 +0800 Subject: [PATCH 10/16] wifi: rtw89: acpi: introduce method evaluation function for reuse The following implementations will evaluate different ACPI methods, but the pre-process flow of them are the same. So, introduce a function for these pre-process things. Besides, also change ACPI RTAG method to call this function. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-6-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/acpi.c | 146 ++++++++++++++++++---- drivers/net/wireless/realtek/rtw89/acpi.h | 5 + 2 files changed, 126 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c index b870a3df4072..97b069062ec8 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.c +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -12,6 +12,121 @@ static const guid_t rtw89_guid = GUID_INIT(0xD2A8C3E8, 0x4B69, 0x4F00, 0x82, 0xBD, 0xFE, 0x86, 0x07, 0x80, 0x3A, 0xA7); +static u32 rtw89_acpi_traversal_object(struct rtw89_dev *rtwdev, + const union acpi_object *obj, u8 *pos) +{ + const union acpi_object *elm; + unsigned int i; + u32 sub_len; + u32 len = 0; + u8 *tmp; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + if (pos) + pos[len] = obj->integer.value; + + len++; + break; + case ACPI_TYPE_BUFFER: + if (unlikely(obj->buffer.length == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: invalid buffer type\n", __func__); + goto err; + } + + if (pos) + memcpy(pos, obj->buffer.pointer, obj->buffer.length); + + len += obj->buffer.length; + break; + case ACPI_TYPE_PACKAGE: + if (unlikely(obj->package.count == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: invalid package type\n", __func__); + goto err; + } + + for (i = 0; i < obj->package.count; i++) { + elm = &obj->package.elements[i]; + tmp = pos ? pos + len : NULL; + + sub_len = rtw89_acpi_traversal_object(rtwdev, elm, tmp); + if (unlikely(sub_len == 0)) + goto err; + + len += sub_len; + } + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: unhandled type: %d\n", + __func__, obj->type); + goto err; + } + + return len; + +err: + return 0; +} + +static u32 rtw89_acpi_calculate_object_length(struct rtw89_dev *rtwdev, + const union acpi_object *obj) +{ + return rtw89_acpi_traversal_object(rtwdev, obj, NULL); +} + +static struct rtw89_acpi_data * +rtw89_acpi_evaluate_method(struct rtw89_dev *rtwdev, const char *method) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + struct rtw89_acpi_data *data = NULL; + acpi_handle root, handle; + union acpi_object *obj; + acpi_status status; + u32 len; + + root = ACPI_HANDLE(rtwdev->dev); + if (!root) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to get root\n", method); + return NULL; + } + + status = acpi_get_handle(root, (acpi_string)method, &handle); + if (ACPI_FAILURE(status)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to get handle\n", method); + return NULL; + } + + status = acpi_evaluate_object(handle, NULL, NULL, &buf); + if (ACPI_FAILURE(status)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to evaluate object\n", method); + return NULL; + } + + obj = buf.pointer; + len = rtw89_acpi_calculate_object_length(rtwdev, obj); + if (unlikely(len == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to traversal obj len\n", method); + goto out; + } + + data = kzalloc(struct_size(data, buf, len), GFP_KERNEL); + if (!data) + goto out; + + data->len = len; + rtw89_acpi_traversal_object(rtwdev, obj, data->buf); + +out: + ACPI_FREE(obj); + return data; +} + static int rtw89_acpi_dsm_get_value(struct rtw89_dev *rtwdev, union acpi_object *obj, u8 *value) @@ -152,34 +267,15 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev, struct rtw89_acpi_rtag_result *res) { - struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; - acpi_handle root, handle; - union acpi_object *obj; - acpi_status status; + const struct rtw89_acpi_data *data; u32 buf_len; int ret = 0; - root = ACPI_HANDLE(rtwdev->dev); - if (!root) - return -EOPNOTSUPP; - - status = acpi_get_handle(root, (acpi_string)"RTAG", &handle); - if (ACPI_FAILURE(status)) - return -EIO; - - status = acpi_evaluate_object(handle, NULL, NULL, &buf); - if (ACPI_FAILURE(status)) + data = rtw89_acpi_evaluate_method(rtwdev, "RTAG"); + if (!data) return -EIO; - obj = buf.pointer; - if (obj->type != ACPI_TYPE_BUFFER) { - rtw89_debug(rtwdev, RTW89_DBG_ACPI, - "acpi: expect buffer but type: %d\n", obj->type); - ret = -EINVAL; - goto out; - } - - buf_len = obj->buffer.length; + buf_len = data->len; if (buf_len != sizeof(*res)) { rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", __func__, buf_len); @@ -187,12 +283,12 @@ int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev, goto out; } - *res = *(struct rtw89_acpi_rtag_result *)obj->buffer.pointer; + *res = *(struct rtw89_acpi_rtag_result *)data->buf; rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res)); out: - ACPI_FREE(obj); + kfree(data); return ret; } diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h index a7f583a2164f..1f5d732c33c0 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.h +++ b/drivers/net/wireless/realtek/rtw89/acpi.h @@ -7,6 +7,11 @@ #include "core.h" +struct rtw89_acpi_data { + u32 len; + u8 buf[] __counted_by(len); +}; + enum rtw89_acpi_dsm_func { RTW89_ACPI_DSM_FUNC_IDN_BAND_SUP = 2, RTW89_ACPI_DSM_FUNC_6G_DIS = 3, -- 2.51.0 From 91f4ea117467f5dc2066658cd5e302ae2fc7584c Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:37 +0800 Subject: [PATCH 11/16] wifi: rtw89: acpi: support loading static SAR table Support to load static SAR table with ACPI WRDS method. The format of a static SAR table is like the following, where according to header, antenna number could be either 2 or 4 and subband number could either contain 6 GHz or not. And then, an entry of it describes a TX power limitation with a given unit, which is also based on header, for the antenna under the subband. Though things can be determined by header, still not all combinations are allowed in content. For the recognizing flow, there is a list of allowed combinations. +--------------------------------+ | header | +--------------------------------+ +---+---+---+---+---+------------+ + / | | | | | | ... | | \ +---+---+---+---+---+------------+ | antenna | | | | | | ... | | number +---+---+---+---+---+------------+ | content |...| | | | | ... | | +---+---+---+---+---+------------+ | \ |...| | | | | ... | | / +---+---+---+---+---+------------+ + \ / subband number Following the format above, try to load a static SAR table and normalize its content into SW structure. If any recognized is loaded, SW SAR flow is then set up with source from ACPI. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-7-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/acpi.c | 342 +++++++++++++++++++++- drivers/net/wireless/realtek/rtw89/acpi.h | 61 ++++ drivers/net/wireless/realtek/rtw89/core.h | 2 + 3 files changed, 404 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c index 97b069062ec8..b937832b0f7a 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.c +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -365,8 +365,348 @@ enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, } } +static u8 rtw89_acpi_sar_rfpath_to_hp_antidx(enum rtw89_rf_path rfpath) +{ + switch (rfpath) { + default: + case RF_PATH_B: + return 0; + case RF_PATH_A: + return 1; + } +} + +static u8 rtw89_acpi_sar_rfpath_to_rt_antidx(enum rtw89_rf_path rfpath) +{ + switch (rfpath) { + default: + case RF_PATH_A: + return 0; + case RF_PATH_B: + return 1; + } +} + +static s16 rtw89_acpi_sar_normalize_hp_val(u8 v) +{ + static const u8 bias = 10; + static const u8 fct = 1; + u16 res; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + res = (bias << TXPWR_FACTOR_OF_RTW89_ACPI_SAR) + + (v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct)); + + return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static s16 rtw89_acpi_sar_normalize_rt_val(u8 v) +{ + static const u8 fct = 3; + u16 res; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + res = v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); + + return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static +void rtw89_acpi_sar_load_std_legacy(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_std_legacy *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) + ent->v[subband][path] = + rec->normalize(ptr->v[antidx][subband]); + else + ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; + } + } +} + +static +void rtw89_acpi_sar_load_std_has_6ghz(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_std_has_6ghz *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); + } + } +} + +static +void rtw89_acpi_sar_load_sml_legacy(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_sml_legacy *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) + ent->v[subband][path] = + rec->normalize(ptr->v[antidx][subband]); + else + ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; + } + } +} + +static +void rtw89_acpi_sar_load_sml_has_6ghz(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_sml_has_6ghz *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); + } + } +} + +static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { + { + .id = { + .cid = RTW89_ACPI_SAR_CID_HP, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), + }, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, + .normalize = rtw89_acpi_sar_normalize_hp_val, + .load = rtw89_acpi_sar_load_std_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_HP, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), + }, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, + .normalize = rtw89_acpi_sar_normalize_hp_val, + .load = rtw89_acpi_sar_load_std_has_6ghz, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), + }, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_std_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), + }, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_std_has_6ghz, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(sml_legacy), + }, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_sml_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(sml_has_6ghz), + }, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_sml_has_6ghz, + }, +}; + +struct rtw89_acpi_sar_rec_parm { + u32 pld_len; + u8 tbl_cnt; + u16 cid; + u8 rev; +}; + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_sar_recognize(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_rec_parm *parm) +{ + const u32 tbl_len = parm->pld_len / parm->tbl_cnt; + const struct rtw89_acpi_sar_recognition *rec; + struct rtw89_acpi_sar_identifier id = {}; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: cid %u, rev %u, tbl len %u, tbl cnt %u\n", + __func__, parm->cid, parm->rev, tbl_len, parm->tbl_cnt); + + if (unlikely(parm->pld_len % parm->tbl_cnt)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid pld len %u\n", + parm->pld_len); + return NULL; + } + + if (unlikely(tbl_len > RTW89_ACPI_SAR_SIZE_MAX)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl len %u\n", + tbl_len); + return NULL; + } + + if (unlikely(parm->tbl_cnt > MAX_NUM_OF_RTW89_ACPI_SAR_TBL)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl cnt %u\n", + parm->tbl_cnt); + return NULL; + } + + switch (parm->cid) { + case RTW89_ACPI_SAR_CID_HP: + case RTW89_ACPI_SAR_CID_RT: + id.cid = parm->cid; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid cid 0x%x\n", + parm->cid); + return NULL; + } + + switch (parm->rev) { + case RTW89_ACPI_SAR_REV_LEGACY: + case RTW89_ACPI_SAR_REV_HAS_6GHZ: + id.rev = parm->rev; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid rev %u\n", + parm->rev); + return NULL; + } + + id.size = tbl_len; + for (unsigned int i = 0; i < ARRAY_SIZE(rtw89_acpi_sar_recs); i++) { + rec = &rtw89_acpi_sar_recs[i]; + if (memcmp(&rec->id, &id, sizeof(rec->id)) == 0) + return rec; + } + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "failed to recognize\n"); + return NULL; +} + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_static_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_sar_recognition *rec = NULL; + const struct rtw89_acpi_static_sar_hdr *hdr; + struct rtw89_sar_entry_from_acpi tmp = {}; + struct rtw89_acpi_sar_rec_parm parm = {}; + struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_STATIC_SAR); + if (!data) + return NULL; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load static sar\n"); + + len = data->len; + if (len <= sizeof(*hdr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); + goto out; + } + + hdr = (typeof(hdr))data->buf; + + parm.cid = le16_to_cpu(hdr->cid); + parm.rev = hdr->rev; + parm.tbl_cnt = 1; + parm.pld_len = len - sizeof(*hdr); + + rec = rtw89_acpi_sar_recognize(rtwdev, &parm); + if (!rec) + goto out; + + rec->load(rtwdev, rec, hdr->content, &tmp); + + tbl = &cfg->tables[0]; + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + tbl->entries[regd] = tmp; + + cfg->valid_num = 1; + +out: + kfree(data); + return rec; +} + int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, struct rtw89_sar_cfg_acpi *cfg) { - return -ENOENT; + const struct rtw89_acpi_sar_recognition *rec; + + rec = rtw89_acpi_evaluate_static_sar(rtwdev, cfg); + if (!rec) + return -ENOENT; + + switch (rec->id.cid) { + case RTW89_ACPI_SAR_CID_HP: + cfg->downgrade_2tx = 3 << TXPWR_FACTOR_OF_RTW89_ACPI_SAR; + break; + case RTW89_ACPI_SAR_CID_RT: + cfg->downgrade_2tx = 0; + break; + default: + return -EFAULT; + } + + return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h index 1f5d732c33c0..68a3433dd41e 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.h +++ b/drivers/net/wireless/realtek/rtw89/acpi.h @@ -75,6 +75,67 @@ struct rtw89_acpi_rtag_result { u8 ant_gain_table[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR]; } __packed; +enum rtw89_acpi_sar_cid { + RTW89_ACPI_SAR_CID_HP = 0x5048, + RTW89_ACPI_SAR_CID_RT = 0x5452, +}; + +enum rtw89_acpi_sar_rev { + RTW89_ACPI_SAR_REV_LEGACY = 1, + RTW89_ACPI_SAR_REV_HAS_6GHZ = 2, +}; + +#define RTW89_ACPI_SAR_ANT_NR_STD 4 +#define RTW89_ACPI_SAR_ANT_NR_SML 2 + +#define RTW89_ACPI_METHOD_STATIC_SAR "WRDS" + +struct rtw89_acpi_sar_std_legacy { + u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; +} __packed; + +struct rtw89_acpi_sar_std_has_6ghz { + u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ]; +} __packed; + +struct rtw89_acpi_sar_sml_legacy { + u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; +} __packed; + +struct rtw89_acpi_sar_sml_has_6ghz { + u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ]; +} __packed; + +struct rtw89_acpi_static_sar_hdr { + __le16 cid; + u8 rev; + u8 content[]; +} __packed; + +struct rtw89_acpi_sar_identifier { + enum rtw89_acpi_sar_cid cid; + enum rtw89_acpi_sar_rev rev; + u8 size; +}; + +/* for rtw89_acpi_sar_identifier::size */ +#define RTW89_ACPI_SAR_SIZE_MAX U8_MAX +#define RTW89_ACPI_SAR_SIZE_OF(type) \ + (BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_sar_ ## type) > \ + RTW89_ACPI_SAR_SIZE_MAX) + \ + sizeof(struct rtw89_acpi_sar_ ## type)) + +struct rtw89_acpi_sar_recognition { + struct rtw89_acpi_sar_identifier id; + + u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath); + s16 (*normalize)(u8 v); + void (*load)(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent); +}; + enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, u32 center_freq); enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index de0734c44985..f36546505b0d 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4645,6 +4645,8 @@ enum rtw89_acpi_sar_subband { RTW89_ACPI_SAR_6GHZ_SUBBAND_8, /* U-NII-8 */ NUM_OF_RTW89_ACPI_SAR_SUBBAND, + RTW89_ACPI_SAR_SUBBAND_NR_LEGACY = RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4 + 1, + RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ = RTW89_ACPI_SAR_6GHZ_SUBBAND_8 + 1, }; #define TXPWR_FACTOR_OF_RTW89_ACPI_SAR 3 /* unit: 0.125 dBm */ -- 2.51.0 From 2eba885ab8e9c5cc547363a270d77adf53447f76 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:38 +0800 Subject: [PATCH 12/16] wifi: rtw89: acpi: support loading dynamic SAR tables and indicator Support to load dynamic SAR tables with ACPI RWRD method. The content format of a single dynamic SAR table is basically the same as static SAR table. However, it's able to carry multiple dynamic SAR tables at one time. And, its header contains one more field to describe how many dynamic SAR tables are filled in the content. Either static SAR table or dynamic SAR tables can be supported, but not both simultaneously. Besides, also support to load indicator of dynamic SAR with ACPI RWSI method. The indicator will describe a target dynamic SAR table, which should be followed currently, by antenna. It can be changed at runtime according to platform mode. For example, tablet mode can use different SAR from normal mode. So, track indicator configuration if dynamic SAR is configured. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-8-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/acpi.c | 133 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/acpi.h | 12 ++ drivers/net/wireless/realtek/rtw89/core.c | 2 +- drivers/net/wireless/realtek/rtw89/core.h | 13 +++ drivers/net/wireless/realtek/rtw89/sar.c | 106 +++++++++++++---- drivers/net/wireless/realtek/rtw89/sar.h | 2 +- 6 files changed, 246 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c index b937832b0f7a..d66515def211 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.c +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -688,25 +688,158 @@ out: return rec; } +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_dynamic_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_sar_recognition *rec = NULL; + const struct rtw89_acpi_dynamic_sar_hdr *hdr; + struct rtw89_acpi_sar_rec_parm parm = {}; + struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR); + if (!data) + return NULL; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar\n"); + + len = data->len; + if (len <= sizeof(*hdr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); + goto out; + } + + hdr = (typeof(hdr))data->buf; + + parm.cid = le16_to_cpu(hdr->cid); + parm.rev = hdr->rev; + parm.tbl_cnt = hdr->cnt; + parm.pld_len = len - sizeof(*hdr); + + rec = rtw89_acpi_sar_recognize(rtwdev, &parm); + if (!rec) + goto out; + + for (unsigned int i = 0; i < hdr->cnt; i++) { + const u8 *content = hdr->content + rec->id.size * i; + struct rtw89_sar_entry_from_acpi tmp = {}; + + rec->load(rtwdev, rec, content, &tmp); + + tbl = &cfg->tables[i]; + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + tbl->entries[regd] = tmp; + } + + cfg->valid_num = hdr->cnt; + +out: + kfree(data); + return rec; +} + +int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg, + bool *poll_changed) +{ + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; + struct rtw89_sar_indicator_from_acpi tmp = *ind; + const struct rtw89_acpi_data *data; + const u8 *tbl_base1_by_ant; + enum rtw89_rf_path path; + int ret = 0; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR); + if (!data) + return -EFAULT; + + if (!poll_changed) + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar indicator\n"); + + len = data->len; + if (len != ind->fields) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); + ret = -EINVAL; + goto out; + } + + tbl_base1_by_ant = data->buf; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = ind->rfpath_to_antidx(path); + u8 sel; + + if (antidx >= ind->fields) + antidx = 0; + + /* convert the table index from 1-based to 0-based */ + sel = tbl_base1_by_ant[antidx] - 1; + if (sel >= cfg->valid_num) + sel = 0; + + tmp.tblsel[path] = sel; + } + + if (memcmp(ind, &tmp, sizeof(*ind)) == 0) { + if (poll_changed) + *poll_changed = false; + } else { + if (poll_changed) + *poll_changed = true; + + *ind = tmp; + } + +out: + kfree(data); + return ret; +} + int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, struct rtw89_sar_cfg_acpi *cfg) { + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; const struct rtw89_acpi_sar_recognition *rec; + bool fetch_indicator = false; + int ret; rec = rtw89_acpi_evaluate_static_sar(rtwdev, cfg); + if (rec) + goto recognized; + + rec = rtw89_acpi_evaluate_dynamic_sar(rtwdev, cfg); if (!rec) return -ENOENT; + fetch_indicator = true; + +recognized: switch (rec->id.cid) { case RTW89_ACPI_SAR_CID_HP: cfg->downgrade_2tx = 3 << TXPWR_FACTOR_OF_RTW89_ACPI_SAR; + ind->fields = RTW89_ACPI_SAR_ANT_NR_STD; break; case RTW89_ACPI_SAR_CID_RT: cfg->downgrade_2tx = 0; + ind->fields = 1; break; default: return -EFAULT; } + if (fetch_indicator) { + ind->rfpath_to_antidx = rec->rfpath_to_antidx; + ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, NULL); + if (ret) + fetch_indicator = false; + } + + if (!fetch_indicator) + memset(ind->tblsel, 0, sizeof(ind->tblsel)); + + ind->enable_sync = fetch_indicator; return 0; } diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h index 68a3433dd41e..b03e4af6555e 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.h +++ b/drivers/net/wireless/realtek/rtw89/acpi.h @@ -89,6 +89,8 @@ enum rtw89_acpi_sar_rev { #define RTW89_ACPI_SAR_ANT_NR_SML 2 #define RTW89_ACPI_METHOD_STATIC_SAR "WRDS" +#define RTW89_ACPI_METHOD_DYNAMIC_SAR "RWRD" +#define RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR "RWSI" struct rtw89_acpi_sar_std_legacy { u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; @@ -112,6 +114,13 @@ struct rtw89_acpi_static_sar_hdr { u8 content[]; } __packed; +struct rtw89_acpi_dynamic_sar_hdr { + __le16 cid; + u8 rev; + u8 cnt; + u8 content[]; +} __packed; + struct rtw89_acpi_sar_identifier { enum rtw89_acpi_sar_cid cid; enum rtw89_acpi_sar_rev rev; @@ -148,5 +157,8 @@ int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev, struct rtw89_acpi_rtag_result *res); int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, struct rtw89_sar_cfg_acpi *cfg); +int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg, + bool *changed); #endif diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 2088c6f4a98e..869728922def 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -3617,7 +3617,7 @@ static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) rtw89_phy_antdiv_track(rtwdev); rtw89_phy_ul_tb_ctrl_track(rtwdev); rtw89_phy_edcca_track(rtwdev); - rtw89_tas_track(rtwdev); + rtw89_sar_track(rtwdev); rtw89_chanctx_track(rtwdev); rtw89_core_rfkill_poll(rtwdev, false); diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index f36546505b0d..0789a943074f 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4666,10 +4666,23 @@ struct rtw89_sar_table_from_acpi { struct rtw89_sar_entry_from_acpi entries[RTW89_REGD_NUM]; }; +struct rtw89_sar_indicator_from_acpi { + bool enable_sync; + unsigned int fields; + u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath); + + /* Select among @tables of container, rtw89_sar_cfg_acpi, by path. + * Not design with pointers since addresses will be invalid after + * sync content with local container instance. + */ + u8 tblsel[NUM_OF_RTW89_ACPI_SAR_RF_PATH]; +}; + struct rtw89_sar_cfg_acpi { u8 downgrade_2tx; unsigned int valid_num; struct rtw89_sar_table_from_acpi tables[MAX_NUM_OF_RTW89_ACPI_SAR_TBL]; + struct rtw89_sar_indicator_from_acpi indicator; }; struct rtw89_sar_info { diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 8bf5b5d9ef37..120cf2088c9e 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -92,6 +92,21 @@ static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, return 0; } +static const struct rtw89_sar_entry_from_acpi * +rtw89_sar_cfg_acpi_get_ent(const struct rtw89_sar_cfg_acpi *rtwsar, + enum rtw89_rf_path path, + enum rtw89_regulation_type regd) +{ + const struct rtw89_sar_indicator_from_acpi *ind = &rtwsar->indicator; + const struct rtw89_sar_table_from_acpi *tbl; + u8 sel; + + sel = ind->tblsel[path]; + tbl = &rtwsar->tables[sel]; + + return &tbl->entries[regd]; +} + static s32 rtw89_sar_cfg_acpi_get_min(const struct rtw89_sar_entry_from_acpi *ent, enum rtw89_rf_path path, @@ -106,9 +121,8 @@ static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev, s32 *cfg) { const struct rtw89_sar_cfg_acpi *rtwsar = &rtwdev->sar.cfg_acpi; - const struct rtw89_sar_table_from_acpi *tbl = rtwsar->tables; + const struct rtw89_sar_entry_from_acpi *ent_a, *ent_b; enum rtw89_acpi_sar_subband subband_l, subband_h; - const struct rtw89_sar_entry_from_acpi *ent; u32 center_freq = sar_parm->center_freq; const struct rtw89_6ghz_span *span; enum rtw89_regulation_type regd; @@ -127,10 +141,12 @@ static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev, band = rtw89_acpi_sar_subband_to_band(rtwdev, subband_l); regd = rtw89_regd_get(rtwdev, band); - ent = &tbl->entries[regd]; - cfg_a = rtw89_sar_cfg_acpi_get_min(ent, RF_PATH_A, subband_l, subband_h); - cfg_b = rtw89_sar_cfg_acpi_get_min(ent, RF_PATH_B, subband_l, subband_h); + ent_a = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_A, regd); + ent_b = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_B, regd); + + cfg_a = rtw89_sar_cfg_acpi_get_min(ent_a, RF_PATH_A, subband_l, subband_h); + cfg_b = rtw89_sar_cfg_acpi_get_min(ent_b, RF_PATH_B, subband_l, subband_h); *cfg = min(cfg_a, cfg_b); if (sar_parm->ntx == RTW89_2TX) @@ -502,18 +518,27 @@ static bool rtw89_tas_query_sar_config(struct rtw89_dev *rtwdev, s32 *cfg) return true; } -static void rtw89_tas_state_update(struct rtw89_dev *rtwdev, - enum rtw89_tas_state state) +static bool __rtw89_tas_state_update(struct rtw89_dev *rtwdev, + enum rtw89_tas_state state) { struct rtw89_tas_info *tas = &rtwdev->tas; if (tas->state == state) - return; + return false; rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: switch state: %s -> %s\n", rtw89_tas_state_str(tas->state), rtw89_tas_state_str(state)); tas->state = state; + return true; +} + +static void rtw89_tas_state_update(struct rtw89_dev *rtwdev, + enum rtw89_tas_state state) +{ + if (!__rtw89_tas_state_update(rtwdev, state)) + return; + rtw89_core_set_chip_txpwr(rtwdev); } @@ -608,7 +633,7 @@ static void rtw89_tas_history_update(struct rtw89_dev *rtwdev) rtw89_linear_to_db_quarter(div_u64(txpwr, PERCENT))); } -static void rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) +static bool rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) { struct rtw89_tas_info *tas = &rtwdev->tas; s32 dpr_on_threshold, dpr_off_threshold; @@ -634,9 +659,9 @@ static void rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) else if (txpwr_avg < dpr_off_threshold) state = RTW89_TAS_STATE_DPR_OFF; else - return; + return false; - rtw89_tas_state_update(rtwdev, state); + return __rtw89_tas_state_update(rtwdev, state); } static void rtw89_tas_init(struct rtw89_dev *rtwdev) @@ -717,29 +742,28 @@ void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force) "tas: band: %u, freq: %u\n", chan->band_type, chan->freq); } -void rtw89_tas_track(struct rtw89_dev *rtwdev) +static bool rtw89_tas_track(struct rtw89_dev *rtwdev) { struct rtw89_tas_info *tas = &rtwdev->tas; struct rtw89_hal *hal = &rtwdev->hal; s32 cfg; if (hal->disabled_dm_bitmap & BIT(RTW89_DM_TAS)) - return; + return false; if (!rtw89_tas_is_active(rtwdev)) - return; + return false; - if (!rtw89_tas_query_sar_config(rtwdev, &cfg) || tas->block_regd) { - rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); - return; - } + if (!rtw89_tas_query_sar_config(rtwdev, &cfg) || tas->block_regd) + return __rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); if (tas->pause) - return; + return false; rtw89_tas_window_update(rtwdev); rtw89_tas_history_update(rtwdev); - rtw89_tas_rolling_average(rtwdev); + + return rtw89_tas_rolling_average(rtwdev); } void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start) @@ -792,3 +816,45 @@ void rtw89_sar_init(struct rtw89_dev *rtwdev) rtw89_set_sar_from_acpi(rtwdev); rtw89_tas_init(rtwdev); } + +static bool rtw89_sar_track_acpi(struct rtw89_dev *rtwdev) +{ + struct rtw89_sar_cfg_acpi *cfg = &rtwdev->sar.cfg_acpi; + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; + const enum rtw89_sar_sources src = rtwdev->sar.src; + bool changed; + int ret; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (src != RTW89_SAR_SOURCE_ACPI) + return false; + + if (!ind->enable_sync) + return false; + + ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, &changed); + if (likely(!ret)) + return changed; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "%s: failed to track indicator: %d; reset and disable\n", + __func__, ret); + + memset(ind->tblsel, 0, sizeof(ind->tblsel)); + ind->enable_sync = false; + return true; +} + +void rtw89_sar_track(struct rtw89_dev *rtwdev) +{ + unsigned int changes = 0; + + changes += rtw89_sar_track_acpi(rtwdev); + changes += rtw89_tas_track(rtwdev); + + if (!changes) + return; + + rtw89_core_set_chip_txpwr(rtwdev); +} diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h index 2d30e0d8be1f..038a5c0d1e09 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.h +++ b/drivers/net/wireless/realtek/rtw89/sar.h @@ -31,10 +31,10 @@ int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, const struct cfg80211_sar_specs *sar); void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force); -void rtw89_tas_track(struct rtw89_dev *rtwdev); 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_sar_init(struct rtw89_dev *rtwdev); +void rtw89_sar_track(struct rtw89_dev *rtwdev); #endif -- 2.51.0 From 5bafc85d71a3212f6115faeb1dba0fa308c7e8aa Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:39 +0800 Subject: [PATCH 13/16] wifi: rtw89: acpi: support loading GEO SAR tables Support to load GEO (geography) SAR tables with ACPI RWGS method. When SAR values could be different by regulatory, GEO SAR can be used. The format of GEO SAR is like the following, where regulatory number, band number, and delta number are determined by header of either static SAR or dynamic SAR. (It also means that no GEO SAR will be considered when neither static SAR nor dynamic SAR is configured.) delta number / \ + +-----+-----------------+ / | | max | delta... | \ / | +-----+-----------------+ band / | | max | delta... | number / | +-----+-----------------+ / | |... | / + +-----+-----------------+ | | max | delta... | \ regulatory | +-----+-----------------+ band number | | max | delta... | number | +-----+-----------------+ | |... | / + +-----+-----------------+ \ | |... | \ | |... | \ | |... | \ | | | \ | | | + +-----+-----------------+ Each entry of GEO SAR contains delta field(s), which are offset(s) used to tweak the loaded static/dynamic SAR table(s) by antenna, and one max field, which describes the maximum of the final SAR values after tweaked. Different entries should be configured based on both regulatory and band. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-9-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/acpi.c | 296 ++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/acpi.h | 89 +++++++ 2 files changed, 385 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c index d66515def211..c97cc29cc663 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.c +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -501,6 +501,265 @@ void rtw89_acpi_sar_load_sml_has_6ghz(struct rtw89_dev *rtwdev, } } +static s16 rtw89_acpi_geo_sar_normalize_delta(s8 delta) +{ + static const u8 fct = 1; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + return delta << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); +} + +static enum rtw89_acpi_geo_sar_regd_hp +rtw89_acpi_geo_sar_regd_convert_hp_idx(enum rtw89_regulation_type regd) +{ + switch (regd) { + case RTW89_FCC: + case RTW89_IC: + case RTW89_NCC: + case RTW89_CHILE: + case RTW89_MEXICO: + return RTW89_ACPI_GEO_SAR_REGD_HP_FCC; + case RTW89_ETSI: + case RTW89_MKK: + case RTW89_ACMA: + return RTW89_ACPI_GEO_SAR_REGD_HP_ETSI; + default: + case RTW89_WW: + case RTW89_NA: + case RTW89_KCC: + return RTW89_ACPI_GEO_SAR_REGD_HP_WW; + } +} + +static enum rtw89_acpi_geo_sar_regd_rt +rtw89_acpi_geo_sar_regd_convert_rt_idx(enum rtw89_regulation_type regd) +{ + switch (regd) { + case RTW89_FCC: + case RTW89_NCC: + case RTW89_CHILE: + case RTW89_MEXICO: + return RTW89_ACPI_GEO_SAR_REGD_RT_FCC; + case RTW89_ETSI: + case RTW89_ACMA: + return RTW89_ACPI_GEO_SAR_REGD_RT_ETSI; + case RTW89_MKK: + return RTW89_ACPI_GEO_SAR_REGD_RT_MKK; + case RTW89_IC: + return RTW89_ACPI_GEO_SAR_REGD_RT_IC; + case RTW89_KCC: + return RTW89_ACPI_GEO_SAR_REGD_RT_KCC; + default: + case RTW89_WW: + case RTW89_NA: + return RTW89_ACPI_GEO_SAR_REGD_RT_WW; + } +} + +static +void rtw89_acpi_geo_sar_load_by_hp(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_hp_val *ptr, + enum rtw89_rf_path path, s16 *val) +{ + u8 antidx = rtw89_acpi_sar_rfpath_to_hp_antidx(path); + s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta[antidx]); + s16 max = rtw89_acpi_sar_normalize_hp_val(ptr->max); + + *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_by_rt(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_rt_val *ptr, + s16 *val) +{ + s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta); + s16 max = rtw89_acpi_sar_normalize_rt_val(ptr->max); + + *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_hp_legacy(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_hp_legacy *ptr = content; + const struct rtw89_acpi_geo_sar_hp_legacy_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_hp geo_idx = + rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + default: + case RTW89_BAND_6G: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_hp_has_6ghz(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_hp_has_6ghz *ptr = content; + const struct rtw89_acpi_geo_sar_hp_has_6ghz_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_hp geo_idx = + rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + case RTW89_BAND_6G: + ptr_ent_val = &ptr_ent->val_6ghz; + break; + default: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_rt_legacy(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_rt_legacy *ptr = content; + const struct rtw89_acpi_geo_sar_rt_legacy_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_rt geo_idx = + rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + default: + case RTW89_BAND_6G: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_rt_has_6ghz(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_rt_has_6ghz *ptr = content; + const struct rtw89_acpi_geo_sar_rt_has_6ghz_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_rt geo_idx = + rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + case RTW89_BAND_6G: + ptr_ent_val = &ptr_ent->val_6ghz; + break; + default: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, + &ent->v[subband][path]); + } +} + +#define RTW89_ACPI_GEO_SAR_DECL_HANDLER(type) \ +static const struct rtw89_acpi_geo_sar_handler \ +rtw89_acpi_geo_sar_handler_ ## type = { \ + .data_size = RTW89_ACPI_GEO_SAR_SIZE_OF(type), \ + .load = rtw89_acpi_geo_sar_load_ ## type, \ +} + +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_has_6ghz); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_has_6ghz); + static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { { .id = { @@ -508,6 +767,7 @@ static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { .rev = RTW89_ACPI_SAR_REV_LEGACY, .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), }, + .geo = &rtw89_acpi_geo_sar_handler_hp_legacy, .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, .normalize = rtw89_acpi_sar_normalize_hp_val, @@ -519,6 +779,7 @@ static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), }, + .geo = &rtw89_acpi_geo_sar_handler_hp_has_6ghz, .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, .normalize = rtw89_acpi_sar_normalize_hp_val, @@ -530,6 +791,7 @@ static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { .rev = RTW89_ACPI_SAR_REV_LEGACY, .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), }, + .geo = &rtw89_acpi_geo_sar_handler_rt_legacy, .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, .normalize = rtw89_acpi_sar_normalize_rt_val, @@ -541,6 +803,7 @@ static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), }, + .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, .normalize = rtw89_acpi_sar_normalize_rt_val, @@ -552,6 +815,7 @@ static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { .rev = RTW89_ACPI_SAR_REV_LEGACY, .size = RTW89_ACPI_SAR_SIZE_OF(sml_legacy), }, + .geo = &rtw89_acpi_geo_sar_handler_rt_legacy, .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, .normalize = rtw89_acpi_sar_normalize_rt_val, @@ -563,6 +827,7 @@ static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, .size = RTW89_ACPI_SAR_SIZE_OF(sml_has_6ghz), }, + .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, .normalize = rtw89_acpi_sar_normalize_rt_val, @@ -798,6 +1063,35 @@ out: return ret; } +static +void rtw89_acpi_evaluate_geo_sar(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_handler *hdl, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_GEO_SAR); + if (!data) + return; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load geo sar\n"); + + len = data->len; + if (len != hdl->data_size) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u (expected %u)\n", + len, hdl->data_size); + goto out; + } + + for (unsigned int i = 0; i < cfg->valid_num; i++) + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + hdl->load(rtwdev, data->buf, regd, &cfg->tables[i].entries[regd]); + +out: + kfree(data); +} + int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, struct rtw89_sar_cfg_acpi *cfg) { @@ -817,6 +1111,8 @@ int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, fetch_indicator = true; recognized: + rtw89_acpi_evaluate_geo_sar(rtwdev, rec->geo, cfg); + switch (rec->id.cid) { case RTW89_ACPI_SAR_CID_HP: cfg->downgrade_2tx = 3 << TXPWR_FACTOR_OF_RTW89_ACPI_SAR; diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h index b03e4af6555e..8ec9011bfefb 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.h +++ b/drivers/net/wireless/realtek/rtw89/acpi.h @@ -91,6 +91,7 @@ enum rtw89_acpi_sar_rev { #define RTW89_ACPI_METHOD_STATIC_SAR "WRDS" #define RTW89_ACPI_METHOD_DYNAMIC_SAR "RWRD" #define RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR "RWSI" +#define RTW89_ACPI_METHOD_GEO_SAR "RWGS" struct rtw89_acpi_sar_std_legacy { u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; @@ -136,6 +137,7 @@ struct rtw89_acpi_sar_identifier { struct rtw89_acpi_sar_recognition { struct rtw89_acpi_sar_identifier id; + const struct rtw89_acpi_geo_sar_handler *geo; u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath); s16 (*normalize)(u8 v); @@ -145,6 +147,93 @@ struct rtw89_acpi_sar_recognition { struct rtw89_sar_entry_from_acpi *ent); }; +struct rtw89_acpi_geo_sar_hp_val { + u8 max; + s8 delta[RTW89_ACPI_SAR_ANT_NR_STD]; +} __packed; + +struct rtw89_acpi_geo_sar_hp_legacy_entry { + struct rtw89_acpi_geo_sar_hp_val val_2ghz; + struct rtw89_acpi_geo_sar_hp_val val_5ghz; +} __packed; + +struct rtw89_acpi_geo_sar_hp_has_6ghz_entry { + struct rtw89_acpi_geo_sar_hp_val val_2ghz; + struct rtw89_acpi_geo_sar_hp_val val_5ghz; + struct rtw89_acpi_geo_sar_hp_val val_6ghz; +} __packed; + +enum rtw89_acpi_geo_sar_regd_hp { + RTW89_ACPI_GEO_SAR_REGD_HP_FCC = 0, + RTW89_ACPI_GEO_SAR_REGD_HP_ETSI = 1, + RTW89_ACPI_GEO_SAR_REGD_HP_WW = 2, + + RTW89_ACPI_GEO_SAR_REGD_NR_HP, +}; + +struct rtw89_acpi_geo_sar_hp_legacy { + struct rtw89_acpi_geo_sar_hp_legacy_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP]; +} __packed; + +struct rtw89_acpi_geo_sar_hp_has_6ghz { + struct rtw89_acpi_geo_sar_hp_has_6ghz_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP]; +} __packed; + +struct rtw89_acpi_geo_sar_rt_val { + u8 max; + s8 delta; +} __packed; + +struct rtw89_acpi_geo_sar_rt_legacy_entry { + struct rtw89_acpi_geo_sar_rt_val val_2ghz; + struct rtw89_acpi_geo_sar_rt_val val_5ghz; +} __packed; + +struct rtw89_acpi_geo_sar_rt_has_6ghz_entry { + struct rtw89_acpi_geo_sar_rt_val val_2ghz; + struct rtw89_acpi_geo_sar_rt_val val_5ghz; + struct rtw89_acpi_geo_sar_rt_val val_6ghz; +} __packed; + +enum rtw89_acpi_geo_sar_regd_rt { + RTW89_ACPI_GEO_SAR_REGD_RT_FCC = 0, + RTW89_ACPI_GEO_SAR_REGD_RT_ETSI = 1, + RTW89_ACPI_GEO_SAR_REGD_RT_MKK = 2, + RTW89_ACPI_GEO_SAR_REGD_RT_IC = 3, + RTW89_ACPI_GEO_SAR_REGD_RT_KCC = 4, + RTW89_ACPI_GEO_SAR_REGD_RT_WW = 5, + + RTW89_ACPI_GEO_SAR_REGD_NR_RT, +}; + +struct rtw89_acpi_geo_sar_rt_legacy { + struct rtw89_acpi_geo_sar_rt_legacy_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT]; +} __packed; + +struct rtw89_acpi_geo_sar_rt_has_6ghz { + struct rtw89_acpi_geo_sar_rt_has_6ghz_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT]; +} __packed; + +struct rtw89_acpi_geo_sar_handler { + u8 data_size; + + void (*load)(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent); +}; + +/* for rtw89_acpi_geo_sar_handler::data_size */ +#define RTW89_ACPI_GEO_SAR_SIZE_MAX U8_MAX +#define RTW89_ACPI_GEO_SAR_SIZE_OF(type) \ + (BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_geo_sar_ ## type) > \ + RTW89_ACPI_GEO_SAR_SIZE_MAX) + \ + sizeof(struct rtw89_acpi_geo_sar_ ## type)) + enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, u32 center_freq); enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, -- 2.51.0 From c6c830b26590933eaebb98d62f198ec655a83777 Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:40 +0800 Subject: [PATCH 14/16] wifi: rtw89: sar: add skeleton for different configs by antenna Some SAR sources, e.g. ACPI, may allow different SAR configs by antenna. Previously, the minimum config between antennas was taken. Because there are differences between HW design, different chips might have different solutions to achieve this. So, it cannot be done through a single common handling. Now, add the relevant skeleton for this purpose ahead. First, add a flag into chip info to describe whether it has implemented this function or not. Second, support to query SAR config for a given RF path. With it, each chip can implement its own handling. Then, if a chip declares to support this function, when it queries SAR config without a given RF path, it gets a maximum config between antennas. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-10-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/core.h | 1 + drivers/net/wireless/realtek/rtw89/phy.h | 14 +++++++++ drivers/net/wireless/realtek/rtw89/rtw8851b.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852a.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852b.c | 1 + .../net/wireless/realtek/rtw89/rtw8852bt.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8852c.c | 1 + drivers/net/wireless/realtek/rtw89/rtw8922a.c | 1 + drivers/net/wireless/realtek/rtw89/sar.c | 29 ++++++++++++++++++- drivers/net/wireless/realtek/rtw89/sar.h | 3 ++ 10 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index 0789a943074f..d49e06f81ba4 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -4284,6 +4284,7 @@ struct rtw89_chip_info { bool support_rnr; bool support_ant_gain; bool support_tas; + bool support_sar_by_ant; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; bool rx_freq_frome_ie; diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 518a100375fb..cafb1a06d7b8 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -935,6 +935,20 @@ static inline s8 rtw89_phy_txpwr_dbm_to_mac(struct rtw89_dev *rtwdev, s8 dbm) return clamp_t(s16, dbm << chip->txpwr_factor_mac, -64, 63); } +static inline s16 rtw89_phy_txpwr_mac_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_mac) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return txpwr_mac << (chip->txpwr_factor_rf - chip->txpwr_factor_mac); +} + +static inline s16 rtw89_phy_txpwr_mac_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_mac) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return txpwr_mac << (chip->txpwr_factor_bb - chip->txpwr_factor_mac); +} + void rtw89_phy_ra_assoc(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); void rtw89_phy_ra_update(struct rtw89_dev *rtwdev); void rtw89_phy_ra_update_sta(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index 0d482cd57f6e..174b90661037 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -2499,6 +2499,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .support_unii4 = true, .support_ant_gain = false, .support_tas = false, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 286334e26c84..408c2f7b3eec 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -2217,6 +2217,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .support_unii4 = false, .support_ant_gain = false, .support_tas = false, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index eceb4fb9880d..47233f0c6ea0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -853,6 +853,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .support_unii4 = true, .support_ant_gain = true, .support_tas = false, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index bbf37442c492..0903e902d8f4 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -786,6 +786,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .support_unii4 = true, .support_ant_gain = true, .support_tas = false, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = true, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index 08bcdf246382..cbbb6a9169d1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -3014,6 +3014,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .support_unii4 = true, .support_ant_gain = true, .support_tas = true, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, .rx_freq_frome_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 8082592db84a..5b45c18fbbf6 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -2823,6 +2823,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .support_unii4 = true, .support_ant_gain = true, .support_tas = false, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = false, diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 120cf2088c9e..d15dafcae39b 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -120,6 +120,7 @@ static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm, s32 *cfg) { + const struct rtw89_chip_info *chip = rtwdev->chip; const struct rtw89_sar_cfg_acpi *rtwsar = &rtwdev->sar.cfg_acpi; const struct rtw89_sar_entry_from_acpi *ent_a, *ent_b; enum rtw89_acpi_sar_subband subband_l, subband_h; @@ -147,7 +148,30 @@ static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev, cfg_a = rtw89_sar_cfg_acpi_get_min(ent_a, RF_PATH_A, subband_l, subband_h); cfg_b = rtw89_sar_cfg_acpi_get_min(ent_b, RF_PATH_B, subband_l, subband_h); - *cfg = min(cfg_a, cfg_b); + + if (chip->support_sar_by_ant) { + /* With declaration of support_sar_by_ant, relax the general + * SAR querying to return the maximum between paths. However, + * expect chip has dealt with the corresponding SAR settings + * by path. (To get SAR for a given path, chip can then query + * with force_path.) + */ + if (sar_parm->force_path) { + switch (sar_parm->path) { + default: + case RF_PATH_A: + *cfg = cfg_a; + break; + case RF_PATH_B: + *cfg = cfg_b; + break; + } + } else { + *cfg = max(cfg_a, cfg_b); + } + } else { + *cfg = min(cfg_a, cfg_b); + } if (sar_parm->ntx == RTW89_2TX) *cfg -= rtwsar->downgrade_2tx; @@ -285,6 +309,7 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_pa return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg); } +EXPORT_SYMBOL(rtw89_query_sar); int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, const struct rtw89_sar_parm *sar_parm) @@ -322,6 +347,8 @@ int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, p += scnprintf(p, end - p, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct)); + p += scnprintf(p, end - p, "support different configs by antenna: %s\n", + str_yes_no(rtwdev->chip->support_sar_by_ant)); out: return p - buf; } diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h index 038a5c0d1e09..4b7f3d44f57b 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.h +++ b/drivers/net/wireless/realtek/rtw89/sar.h @@ -13,6 +13,9 @@ struct rtw89_sar_parm { u32 center_freq; enum rtw89_ntx ntx; + + bool force_path; + enum rtw89_rf_path path; }; struct rtw89_sar_handler { -- 2.51.0 From 711b6ce3e816ff2f00431d88b11ec22e34acc6fa Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:41 +0800 Subject: [PATCH 15/16] wifi: rtw89: 8922a: support different SAR configs by antenna Set SAR configs to the corresponding CRs (control registers) according to RF path. Then, declare to support SAR by antenna in chip info. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-11-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 3 ++ drivers/net/wireless/realtek/rtw89/rtw8922a.c | 29 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 6ef546d1b575..d0840ad95e56 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -9368,6 +9368,9 @@ #define R_TSSI_PWR_P0 0xE610 #define R_TSSI_PWR_P1 0xE710 #define B_TSSI_CONT_EN BIT(3) +#define R_P0_TXPWRB_BE 0xE61C +#define R_P1_TXPWRB_BE 0xE71C +#define B_TXPWRB_MAX_BE GENMASK(20, 12) #define R_TSSI_MAP_OFST_P0 0xE620 #define R_TSSI_MAP_OFST_P1 0xE720 #define B_TSSI_MAP_OFST_OFDM GENMASK(17, 9) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 5b45c18fbbf6..cc595cae53f5 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -12,6 +12,7 @@ #include "reg.h" #include "rtw8922a.h" #include "rtw8922a_rfk.h" +#include "sar.h" #include "util.h" #define RTW8922A_FW_FORMAT_MAX 3 @@ -2233,6 +2234,31 @@ static void rtw8922a_set_tx_shape(struct rtw89_dev *rtwdev, rtw8922a_bb_tx_triangular(rtwdev, true, phy_idx); } +static void rtw8922a_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_sar_parm sar_parm = { + .center_freq = chan->freq, + .force_path = true, + }; + s16 sar_rf; + s8 sar_mac; + + if (phy_idx != RTW89_PHY_0) + return; + + sar_parm.path = RF_PATH_A; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P0_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf); + + sar_parm.path = RF_PATH_B; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf); +} + static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2244,6 +2270,7 @@ static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev, rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); rtw8922a_set_txpwr_diff(rtwdev, chan, phy_idx); rtw8922a_set_txpwr_ref(rtwdev, phy_idx); + rtw8922a_set_txpwr_sar_diff(rtwdev, chan, phy_idx); } static void rtw8922a_set_txpwr_ctrl(struct rtw89_dev *rtwdev, @@ -2823,7 +2850,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .support_unii4 = true, .support_ant_gain = true, .support_tas = false, - .support_sar_by_ant = false, + .support_sar_by_ant = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, .rx_freq_frome_ie = false, -- 2.51.0 From 7b51b39368c00e1cb06546e787ccff6b15b1686c Mon Sep 17 00:00:00 2001 From: Zong-Zhe Yang Date: Wed, 26 Mar 2025 10:06:42 +0800 Subject: [PATCH 16/16] wifi: rtw89: 8852c: support different SAR configs by antenna Set SAR configs to the corresponding CRs (control registers) according to RF path. Then, declare to support SAR by antenna in chip info. Signed-off-by: Zong-Zhe Yang Signed-off-by: Ping-Ke Shih Link: https://patch.msgid.link/20250326020643.14487-12-pkshih@realtek.com --- drivers/net/wireless/realtek/rtw89/reg.h | 2 ++ drivers/net/wireless/realtek/rtw89/rtw8852c.c | 28 ++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index d0840ad95e56..f05c81ae5869 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -8745,8 +8745,10 @@ #define B_DPD_GDIS BIT(13) #define B_IQK_RFC_ON BIT(1) #define R_TXPWRB 0x56CC +#define R_P1_TXPWRB 0x76CC #define B_TXPWRB_ON BIT(28) #define B_TXPWRB_VAL GENMASK(27, 19) +#define B_TXPWRB_MAX GENMASK(8, 0) #define R_DPD_OFT_EN 0x5800 #define B_DPD_OFT_EN BIT(28) #define B_DPD_TSSI_CW GENMASK(26, 18) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index cbbb6a9169d1..a8fd573d5048 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -2079,6 +2079,31 @@ static void rtw8852c_set_txpwr_diff(struct rtw89_dev *rtwdev, rtw8852c_set_txpwr_ref(rtwdev, phy_idx, pwr_ofst); } +static void rtw8852c_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_sar_parm sar_parm = { + .center_freq = chan->freq, + .force_path = true, + }; + s16 sar_rf; + s8 sar_mac; + + if (phy_idx != RTW89_PHY_0) + return; + + sar_parm.path = RF_PATH_A; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_TXPWRB, B_TXPWRB_MAX, sar_rf); + + sar_parm.path = RF_PATH_B; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB, B_TXPWRB_MAX, sar_rf); +} + static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2089,6 +2114,7 @@ static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev, rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); rtw8852c_set_txpwr_diff(rtwdev, chan, phy_idx); + rtw8852c_set_txpwr_sar_diff(rtwdev, chan, phy_idx); } static void rtw8852c_set_txpwr_ctrl(struct rtw89_dev *rtwdev, @@ -3014,7 +3040,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .support_unii4 = true, .support_ant_gain = true, .support_tas = true, - .support_sar_by_ant = false, + .support_sar_by_ant = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, .rx_freq_frome_ie = false, -- 2.51.0