@@ -8,6 +8,7 @@
#include "fw.h"
#include "mac.h"
#include "ps.h"
+#include "sar.h"
#include "util.h"
static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev,
@@ -2673,6 +2674,7 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
struct rtw89_hal *hal = &rtwdev->hal;
struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt;
struct rtw89_entity_weight w = {};
+ int ret;
rtwvif_link->chanctx_idx = cfg->idx;
rtwvif_link->chanctx_assigned = true;
@@ -2692,7 +2694,13 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev,
rtw89_swap_chanctx(rtwdev, cfg->idx, RTW89_CHANCTX_0);
out:
- return rtw89_set_channel(rtwdev);
+ ret = rtw89_set_channel(rtwdev);
+ if (ret)
+ return ret;
+
+ rtw89_tas_reset(rtwdev, true);
+
+ return 0;
}
void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev,
@@ -4601,8 +4601,6 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
rtw89_mac_cfg_phy_rpt_bands(rtwdev, true);
rtw89_mac_update_rts_threshold(rtwdev);
- rtw89_tas_reset(rtwdev);
-
ret = rtw89_hci_start(rtwdev);
if (ret) {
rtw89_err(rtwdev, "failed to start hci\n");
@@ -4956,6 +4954,7 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv
rtw89_chip_rfk_scan(rtwdev, rtwvif_link, true);
rtw89_hci_recalc_int_mit(rtwdev);
rtw89_phy_config_edcca(rtwdev, bb, true);
+ rtw89_tas_scan(rtwdev, true);
rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr);
}
@@ -4982,6 +4981,7 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev,
rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx);
bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx);
rtw89_phy_config_edcca(rtwdev, bb, false);
+ rtw89_tas_scan(rtwdev, false);
rtwdev->scanning = false;
rtw89_for_each_active_bb(rtwdev, bb)
@@ -4240,6 +4240,7 @@ enum rtw89_chanctx_state {
enum rtw89_chanctx_callbacks {
RTW89_CHANCTX_CALLBACK_PLACEHOLDER,
RTW89_CHANCTX_CALLBACK_RFK,
+ RTW89_CHANCTX_CALLBACK_TAS,
NUM_OF_RTW89_CHANCTX_CALLBACKS,
};
@@ -4281,6 +4282,7 @@ struct rtw89_chip_info {
bool support_unii4;
bool support_rnr;
bool support_ant_gain;
+ bool support_tas;
bool ul_tb_waveform_ctrl;
bool ul_tb_pwr_diff;
bool rx_freq_frome_ie;
@@ -4679,18 +4681,29 @@ struct rtw89_6ghz_span {
enum rtw89_tas_state {
RTW89_TAS_STATE_DPR_OFF,
RTW89_TAS_STATE_DPR_ON,
- RTW89_TAS_STATE_DPR_FORBID,
+ RTW89_TAS_STATE_STATIC_SAR,
};
-#define RTW89_TAS_MAX_WINDOW 50
+#define RTW89_TAS_TX_RATIO_WINDOW 6
+#define RTW89_TAS_TXPWR_WINDOW 180
struct rtw89_tas_info {
- s16 txpwr_history[RTW89_TAS_MAX_WINDOW];
- s32 total_txpwr;
- u8 cur_idx;
- s8 dpr_gap;
- s8 delta;
+ u16 tx_ratio_history[RTW89_TAS_TX_RATIO_WINDOW];
+ u64 txpwr_history[RTW89_TAS_TXPWR_WINDOW];
+ u8 txpwr_head_idx;
+ u8 txpwr_tail_idx;
+ u8 tx_ratio_idx;
+ u16 total_tx_ratio;
+ u64 total_txpwr;
+ u64 instant_txpwr;
+ u32 window_size;
+ s8 dpr_on_threshold;
+ s8 dpr_off_threshold;
+ enum rtw89_tas_state backup_state;
enum rtw89_tas_state state;
+ bool keep_history;
+ bool block_regd;
bool enable;
+ bool pause;
};
struct rtw89_chanctx_cfg {
@@ -4748,6 +4761,7 @@ struct rtw89_edcca_bak {
enum rtw89_dm_type {
RTW89_DM_DYNAMIC_EDCCA,
RTW89_DM_THERMAL_PROTECT,
+ RTW89_DM_TAS,
};
#define RTW89_THERMAL_PROT_LV_MAX 5
@@ -4154,6 +4154,7 @@ static const struct rtw89_disabled_dm_info {
} rtw89_disabled_dm_infos[] = {
DM_INFO(DYNAMIC_EDCCA),
DM_INFO(THERMAL_PROTECT),
+ DM_INFO(TAS),
};
static ssize_t
@@ -4543,6 +4543,12 @@ struct rtw89_c2h_rfk_report {
u8 version;
} __packed;
+struct rtw89_c2h_rf_tas_info {
+ struct rtw89_c2h_hdr hdr;
+ __le32 cur_idx;
+ __le16 txpwr_history[20];
+} __packed;
+
#define RTW89_FW_RSVD_PLE_SIZE 0x800
#define RTW89_FW_BACKTRACE_INFO_SIZE 8
@@ -3459,6 +3459,30 @@ rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u3
static void
rtw89_phy_c2h_rfk_log_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len)
{
+ const struct rtw89_c2h_rf_tas_info *rf_tas =
+ (const struct rtw89_c2h_rf_tas_info *)c2h->data;
+ const enum rtw89_sar_sources src = rtwdev->sar.src;
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ u64 linear = 0;
+ u32 i, cur_idx;
+ s16 txpwr;
+
+ if (!tas->enable || src == RTW89_SAR_SOURCE_NONE)
+ return;
+
+ cur_idx = le32_to_cpu(rf_tas->cur_idx);
+ for (i = 0; i < cur_idx; i++) {
+ txpwr = (s16)le16_to_cpu(rf_tas->txpwr_history[i]);
+ linear += rtw89_db_quarter_to_linear(txpwr);
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "tas: index: %u, txpwr: %d\n", i, txpwr);
+ }
+
+ if (cur_idx == 0)
+ tas->instant_txpwr = rtw89_db_to_linear(0);
+ else
+ tas->instant_txpwr = DIV_ROUND_DOWN_ULL(linear, cur_idx);
}
static
@@ -721,6 +721,18 @@ static void rtw89_regd_apply_policy_6ghz(struct rtw89_dev *rtwdev,
sband->channels[i].flags |= IEEE80211_CHAN_DISABLED;
}
+static void rtw89_regd_apply_policy_tas(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory;
+ const struct rtw89_regd *regd = regulatory->regd;
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+
+ if (!tas->enable)
+ return;
+
+ tas->block_regd = !test_bit(RTW89_REGD_FUNC_TAS, regd->func_bitmap);
+}
+
static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
struct wiphy *wiphy,
struct regulatory_request *request)
@@ -738,6 +750,7 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev,
rtw89_regd_apply_policy_unii4(rtwdev, wiphy);
rtw89_regd_apply_policy_6ghz(rtwdev, wiphy);
+ rtw89_regd_apply_policy_tas(rtwdev);
}
static
@@ -2498,6 +2498,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = true,
.support_ant_gain = false,
+ .support_tas = false,
.ul_tb_waveform_ctrl = true,
.ul_tb_pwr_diff = false,
.rx_freq_frome_ie = true,
@@ -2216,6 +2216,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = false,
.support_ant_gain = false,
+ .support_tas = false,
.ul_tb_waveform_ctrl = false,
.ul_tb_pwr_diff = false,
.rx_freq_frome_ie = true,
@@ -852,6 +852,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = true,
.support_ant_gain = true,
+ .support_tas = false,
.ul_tb_waveform_ctrl = true,
.ul_tb_pwr_diff = false,
.rx_freq_frome_ie = true,
@@ -785,6 +785,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = {
BIT(NL80211_CHAN_WIDTH_80),
.support_unii4 = true,
.support_ant_gain = true,
+ .support_tas = false,
.ul_tb_waveform_ctrl = true,
.ul_tb_pwr_diff = false,
.rx_freq_frome_ie = true,
@@ -12,6 +12,7 @@
#include "rtw8852c.h"
#include "rtw8852c_rfk.h"
#include "rtw8852c_table.h"
+#include "sar.h"
#include "util.h"
#define RTW8852C_FW_FORMAT_MAX 1
@@ -2880,6 +2881,7 @@ static int rtw8852c_mac_disable_bb_rf(struct rtw89_dev *rtwdev)
static const struct rtw89_chanctx_listener rtw8852c_chanctx_listener = {
.callbacks[RTW89_CHANCTX_CALLBACK_RFK] = rtw8852c_rfk_chanctx_cb,
+ .callbacks[RTW89_CHANCTX_CALLBACK_TAS] = rtw89_tas_chanctx_cb,
};
#ifdef CONFIG_PM
@@ -3011,6 +3013,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = {
BIT(NL80211_CHAN_WIDTH_160),
.support_unii4 = true,
.support_ant_gain = true,
+ .support_tas = true,
.ul_tb_waveform_ctrl = false,
.ul_tb_pwr_diff = true,
.rx_freq_frome_ie = false,
@@ -2770,6 +2770,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = {
BIT(NL80211_CHAN_WIDTH_160),
.support_unii4 = true,
.support_ant_gain = false,
+ .support_tas = false,
.ul_tb_waveform_ctrl = false,
.ul_tb_pwr_diff = false,
.rx_freq_frome_ie = false,
@@ -7,10 +7,16 @@
#include "phy.h"
#include "reg.h"
#include "sar.h"
+#include "util.h"
#define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */
+#define RTW89_TAS_SAR_GAP (1 << RTW89_TAS_FACTOR)
#define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR)
#define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR)
+#define RTW89_TAS_TX_RATIO_THRESHOLD 70
+#define RTW89_TAS_DFLT_TX_RATIO 80
+#define RTW89_TAS_DPR_ON_OFFSET (RTW89_TAS_DELTA + RTW89_TAS_SAR_GAP)
+#define RTW89_TAS_DPR_OFF_OFFSET (4 << RTW89_TAS_FACTOR)
static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev,
u32 center_freq)
@@ -117,8 +123,8 @@ static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg)
RTW89_SAR_TXPWR_MAC_MAX);
}
-static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl,
- s8 cfg)
+static s32 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl,
+ s32 cfg)
{
const u8 fct = sar_hdl->txpwr_factor_sar;
@@ -128,8 +134,8 @@ static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl,
return cfg >> (RTW89_TAS_FACTOR - fct);
}
-static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl,
- s8 cfg)
+static s32 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl,
+ s32 cfg)
{
const u8 fct = sar_hdl->txpwr_factor_sar;
@@ -139,13 +145,43 @@ static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl,
return cfg << (RTW89_TAS_FACTOR - fct);
}
+static bool rtw89_tas_is_active(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ struct rtw89_vif *rtwvif;
+
+ if (!tas->enable)
+ return false;
+
+ rtw89_for_each_rtwvif(rtwdev, rtwvif) {
+ if (ieee80211_vif_is_mld(rtwvif_to_vif(rtwvif)))
+ return false;
+ }
+
+ return true;
+}
+
+static const char *rtw89_tas_state_str(enum rtw89_tas_state state)
+{
+ switch (state) {
+ case RTW89_TAS_STATE_DPR_OFF:
+ return "DPR OFF";
+ case RTW89_TAS_STATE_DPR_ON:
+ return "DPR ON";
+ case RTW89_TAS_STATE_STATIC_SAR:
+ return "STATIC SAR";
+ default:
+ return NULL;
+ }
+}
+
s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq)
{
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_tas_info *tas = &rtwdev->tas;
- s8 delta;
+ s32 offset;
int ret;
s32 cfg;
u8 fct;
@@ -159,15 +195,17 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq)
if (ret)
return RTW89_SAR_TXPWR_MAC_MAX;
- if (tas->enable) {
+ if (rtw89_tas_is_active(rtwdev)) {
switch (tas->state) {
case RTW89_TAS_STATE_DPR_OFF:
- return RTW89_SAR_TXPWR_MAC_MAX;
+ offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_OFF_OFFSET);
+ cfg += offset;
+ break;
case RTW89_TAS_STATE_DPR_ON:
- delta = rtw89_txpwr_tas_to_sar(sar_hdl, tas->delta);
- cfg -= delta;
+ offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_ON_OFFSET);
+ cfg -= offset;
break;
- case RTW89_TAS_STATE_DPR_FORBID:
+ case RTW89_TAS_STATE_STATIC_SAR:
default:
break;
}
@@ -223,13 +261,23 @@ int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz)
struct rtw89_tas_info *tas = &rtwdev->tas;
char *p = buf, *end = buf + bufsz;
- if (!tas->enable) {
+ if (!rtw89_tas_is_active(rtwdev)) {
p += scnprintf(p, end - p, "no TAS is applied\n");
goto out;
}
- p += scnprintf(p, end - p, "DPR gap: %d\n", tas->dpr_gap);
- p += scnprintf(p, end - p, "TAS delta: %d\n", tas->delta);
+ p += scnprintf(p, end - p, "State: %s\n",
+ rtw89_tas_state_str(tas->state));
+ p += scnprintf(p, end - p, "Average time: %d\n",
+ tas->window_size * 2);
+ p += scnprintf(p, end - p, "SAR gap: %d dBm\n",
+ RTW89_TAS_SAR_GAP >> RTW89_TAS_FACTOR);
+ p += scnprintf(p, end - p, "DPR gap: %d dBm\n",
+ RTW89_TAS_DPR_GAP >> RTW89_TAS_FACTOR);
+ p += scnprintf(p, end - p, "DPR ON offset: %d dBm\n",
+ RTW89_TAS_DPR_ON_OFFSET >> RTW89_TAS_FACTOR);
+ p += scnprintf(p, end - p, "DPR OFF offset: %d dBm\n",
+ RTW89_TAS_DPR_OFF_OFFSET >> RTW89_TAS_FACTOR);
out:
return p - buf;
@@ -250,6 +298,7 @@ static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev,
rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar);
rtw89_core_set_chip_txpwr(rtwdev);
+ rtw89_tas_reset(rtwdev, false);
return 0;
}
@@ -314,65 +363,174 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw,
return rtw89_apply_sar_common(rtwdev, &sar_common);
}
-static void rtw89_tas_state_update(struct rtw89_dev *rtwdev)
+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);
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_tas_info *tas = &rtwdev->tas;
- s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT;
- s32 dpr_on_threshold, dpr_off_threshold, cfg;
- enum rtw89_tas_state state = tas->state;
- const struct rtw89_chan *chan;
int ret;
- lockdep_assert_wiphy(rtwdev->hw->wiphy);
-
if (src == RTW89_SAR_SOURCE_NONE)
- return;
+ return false;
- chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
- ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg);
+ ret = sar_hdl->query_sar_config(rtwdev, chan->freq, cfg);
if (ret)
+ return false;
+
+ *cfg = rtw89_txpwr_sar_to_tas(sar_hdl, *cfg);
+
+ return true;
+}
+
+static void 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;
- cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg);
+ 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;
+ rtw89_core_set_chip_txpwr(rtwdev);
+}
- if (tas->delta >= cfg) {
+static u32 rtw89_tas_get_window_size(struct rtw89_dev *rtwdev)
+{
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
+ u8 band = chan->band_type;
+ u8 regd = rtw89_regd_get(rtwdev, band);
+
+ switch (regd) {
+ default:
rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "TAS delta exceed SAR limit\n");
- state = RTW89_TAS_STATE_DPR_FORBID;
- goto out;
+ "tas: regd: %u is unhandled\n", regd);
+ fallthrough;
+ case RTW89_IC:
+ case RTW89_KCC:
+ return 180;
+ case RTW89_FCC:
+ switch (band) {
+ case RTW89_BAND_2G:
+ return 50;
+ case RTW89_BAND_5G:
+ return 30;
+ case RTW89_BAND_6G:
+ default:
+ return 15;
+ }
+ break;
}
+}
+
+static void rtw89_tas_window_update(struct rtw89_dev *rtwdev)
+{
+ u32 window_size = rtw89_tas_get_window_size(rtwdev);
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ u64 total_txpwr = 0;
+ u8 head_idx;
+ u32 i, j;
+
+ WARN_ON_ONCE(tas->window_size > RTW89_TAS_TXPWR_WINDOW);
+
+ if (tas->window_size == window_size)
+ return;
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: window update: %u -> %u\n",
+ tas->window_size, window_size);
+
+ head_idx = (tas->txpwr_tail_idx - window_size + 1 + RTW89_TAS_TXPWR_WINDOW) %
+ RTW89_TAS_TXPWR_WINDOW;
+ for (i = 0; i < window_size; i++) {
+ j = (head_idx + i) % RTW89_TAS_TXPWR_WINDOW;
+ total_txpwr += tas->txpwr_history[j];
+ }
+
+ tas->window_size = window_size;
+ tas->total_txpwr = total_txpwr;
+ tas->txpwr_head_idx = head_idx;
+}
+
+static void rtw89_tas_history_update(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0);
+ struct rtw89_env_monitor_info *env = &bb->env_monitor;
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ u8 tx_ratio = env->ifs_clm_tx_ratio;
+ u64 instant_txpwr, txpwr;
+
+ /* txpwr in unit of linear(mW) multiply by percentage */
+ if (tx_ratio == 0) {
+ /* special case: idle tx power
+ * use -40 dBm * 100 tx ratio
+ */
+ instant_txpwr = rtw89_db_to_linear(-40);
+ txpwr = instant_txpwr * 100;
+ } else {
+ instant_txpwr = tas->instant_txpwr;
+ txpwr = instant_txpwr * tx_ratio;
+ }
+
+ tas->total_txpwr += txpwr - tas->txpwr_history[tas->txpwr_head_idx];
+ tas->total_tx_ratio += tx_ratio - tas->tx_ratio_history[tas->tx_ratio_idx];
+ tas->tx_ratio_history[tas->tx_ratio_idx] = tx_ratio;
+
+ tas->txpwr_head_idx = (tas->txpwr_head_idx + 1) % RTW89_TAS_TXPWR_WINDOW;
+ tas->txpwr_tail_idx = (tas->txpwr_tail_idx + 1) % RTW89_TAS_TXPWR_WINDOW;
+ tas->tx_ratio_idx = (tas->tx_ratio_idx + 1) % RTW89_TAS_TX_RATIO_WINDOW;
+ tas->txpwr_history[tas->txpwr_tail_idx] = txpwr;
- dpr_on_threshold = cfg;
- dpr_off_threshold = cfg - tas->dpr_gap;
rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n",
- dpr_on_threshold, dpr_off_threshold, txpwr_avg);
+ "tas: instant_txpwr: %d, tx_ratio: %u, txpwr: %d\n",
+ rtw89_linear_to_db_quarter(instant_txpwr), tx_ratio,
+ rtw89_linear_to_db_quarter(div_u64(txpwr, PERCENT)));
+}
- if (txpwr_avg >= dpr_on_threshold)
+static void rtw89_tas_rolling_average(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ s32 dpr_on_threshold, dpr_off_threshold;
+ enum rtw89_tas_state state;
+ u16 tx_ratio_avg;
+ s32 txpwr_avg;
+ u64 linear;
+
+ linear = DIV_ROUND_DOWN_ULL(tas->total_txpwr, tas->window_size * PERCENT);
+ txpwr_avg = rtw89_linear_to_db_quarter(linear);
+ tx_ratio_avg = tas->total_tx_ratio / RTW89_TAS_TX_RATIO_WINDOW;
+ dpr_on_threshold = tas->dpr_on_threshold;
+ dpr_off_threshold = tas->dpr_off_threshold;
+
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "tas: DPR_ON: %d, DPR_OFF: %d, txpwr_avg: %d, tx_ratio_avg: %u\n",
+ dpr_on_threshold, dpr_off_threshold, txpwr_avg, tx_ratio_avg);
+
+ if (tx_ratio_avg >= RTW89_TAS_TX_RATIO_THRESHOLD)
+ state = RTW89_TAS_STATE_STATIC_SAR;
+ else if (txpwr_avg >= dpr_on_threshold)
state = RTW89_TAS_STATE_DPR_ON;
else if (txpwr_avg < dpr_off_threshold)
state = RTW89_TAS_STATE_DPR_OFF;
-
-out:
- if (tas->state == state)
+ else
return;
- rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "TAS old state: %d, new state: %d\n", tas->state, state);
- tas->state = state;
- rtw89_core_set_chip_txpwr(rtwdev);
+ rtw89_tas_state_update(rtwdev, state);
}
void rtw89_tas_init(struct rtw89_dev *rtwdev)
{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_tas_info *tas = &rtwdev->tas;
struct rtw89_acpi_dsm_result res = {};
int ret;
u8 val;
+ if (!chip->support_tas)
+ return;
+
ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res);
if (ret) {
rtw89_debug(rtwdev, RTW89_DBG_SAR,
@@ -396,65 +554,116 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev)
rtw89_debug(rtwdev, RTW89_DBG_SAR, "TAS not enable\n");
return;
}
-
- tas->dpr_gap = RTW89_TAS_DPR_GAP;
- tas->delta = RTW89_TAS_DELTA;
}
-void rtw89_tas_reset(struct rtw89_dev *rtwdev)
+void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force)
{
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0);
struct rtw89_tas_info *tas = &rtwdev->tas;
+ u64 linear;
+ s32 cfg;
+ int i;
- if (!tas->enable)
+ if (!rtw89_tas_is_active(rtwdev))
+ return;
+
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg))
+ return;
+
+ tas->dpr_on_threshold = cfg - RTW89_TAS_SAR_GAP;
+ tas->dpr_off_threshold = cfg - RTW89_TAS_SAR_GAP - RTW89_TAS_DPR_GAP;
+
+ /* avoid history reset after new SAR apply */
+ if (!force && tas->keep_history)
return;
- memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history));
- tas->total_txpwr = 0;
- tas->cur_idx = 0;
+ linear = rtw89_db_quarter_to_linear(cfg) * RTW89_TAS_DFLT_TX_RATIO;
+ for (i = 0; i < RTW89_TAS_TXPWR_WINDOW; i++)
+ tas->txpwr_history[i] = linear;
+
+ for (i = 0; i < RTW89_TAS_TX_RATIO_WINDOW; i++)
+ tas->tx_ratio_history[i] = RTW89_TAS_DFLT_TX_RATIO;
+
+ tas->total_tx_ratio = RTW89_TAS_DFLT_TX_RATIO * RTW89_TAS_TX_RATIO_WINDOW;
+ tas->total_txpwr = linear * RTW89_TAS_TXPWR_WINDOW;
+ tas->window_size = RTW89_TAS_TXPWR_WINDOW;
+ tas->txpwr_head_idx = 0;
+ tas->txpwr_tail_idx = RTW89_TAS_TXPWR_WINDOW - 1;
+ tas->tx_ratio_idx = 0;
tas->state = RTW89_TAS_STATE_DPR_OFF;
-}
+ tas->backup_state = RTW89_TAS_STATE_DPR_OFF;
+ tas->keep_history = true;
-static const struct rtw89_reg_def txpwr_regs[] = {
- {R_PATH0_TXPWR, B_PATH0_TXPWR},
- {R_PATH1_TXPWR, B_PATH1_TXPWR},
-};
+ rtw89_debug(rtwdev, RTW89_DBG_SAR,
+ "tas: band: %u, freq: %u\n", chan->band_type, chan->freq);
+}
void rtw89_tas_track(struct rtw89_dev *rtwdev)
{
- struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0);
- struct rtw89_env_monitor_info *env = &bb->env_monitor;
- const enum rtw89_sar_sources src = rtwdev->sar.src;
- u8 max_nss_num = rtwdev->chip->rf_path_num;
struct rtw89_tas_info *tas = &rtwdev->tas;
- s16 tmp, txpwr, instant_txpwr = 0;
- u32 val;
- int i;
+ struct rtw89_hal *hal = &rtwdev->hal;
+ s32 cfg;
- if (!tas->enable || src == RTW89_SAR_SOURCE_NONE)
+ if (hal->disabled_dm_bitmap & BIT(RTW89_DM_TAS))
return;
- if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM)
+ if (!rtw89_tas_is_active(rtwdev))
return;
- for (i = 0; i < max_nss_num; i++) {
- val = rtw89_phy_read32_mask(rtwdev, txpwr_regs[i].addr,
- txpwr_regs[i].mask);
- tmp = sign_extend32(val, 8);
- if (tmp <= 0)
- return;
- instant_txpwr += tmp;
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg) || tas->block_regd) {
+ rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR);
+ return;
}
- instant_txpwr /= max_nss_num;
- /* in unit of 0.25 dBm multiply by percentage */
- txpwr = instant_txpwr * env->ifs_clm_tx_ratio;
- tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx];
- tas->txpwr_history[tas->cur_idx] = txpwr;
- rtw89_debug(rtwdev, RTW89_DBG_SAR,
- "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n",
- instant_txpwr, env->ifs_clm_tx_ratio, txpwr);
+ if (tas->pause)
+ return;
+
+ rtw89_tas_window_update(rtwdev);
+ rtw89_tas_history_update(rtwdev);
+ rtw89_tas_rolling_average(rtwdev);
+}
+
+void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ s32 cfg;
+
+ if (!rtw89_tas_is_active(rtwdev))
+ return;
- tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW;
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg))
+ return;
- rtw89_tas_state_update(rtwdev);
+ if (start) {
+ tas->backup_state = tas->state;
+ rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR);
+ } else {
+ rtw89_tas_state_update(rtwdev, tas->backup_state);
+ }
+}
+
+void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev,
+ enum rtw89_chanctx_state state)
+{
+ struct rtw89_tas_info *tas = &rtwdev->tas;
+ s32 cfg;
+
+ if (!rtw89_tas_is_active(rtwdev))
+ return;
+
+ if (!rtw89_tas_query_sar_config(rtwdev, &cfg))
+ return;
+
+ switch (state) {
+ case RTW89_CHANCTX_STATE_MCC_START:
+ tas->pause = true;
+ rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR);
+ break;
+ case RTW89_CHANCTX_STATE_MCC_STOP:
+ tas->pause = false;
+ break;
+ default:
+ break;
+ }
}
+EXPORT_SYMBOL(rtw89_tas_chanctx_cb);
@@ -25,7 +25,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_init(struct rtw89_dev *rtwdev);
-void rtw89_tas_reset(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);
#endif