162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* Copyright(c) 2019-2020 Realtek Corporation 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include "acpi.h" 662306a36Sopenharmony_ci#include "debug.h" 762306a36Sopenharmony_ci#include "phy.h" 862306a36Sopenharmony_ci#include "reg.h" 962306a36Sopenharmony_ci#include "sar.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */ 1262306a36Sopenharmony_ci#define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR) 1362306a36Sopenharmony_ci#define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR) 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev, 1662306a36Sopenharmony_ci u32 center_freq) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci switch (center_freq) { 1962306a36Sopenharmony_ci default: 2062306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 2162306a36Sopenharmony_ci "center freq: %u to SAR subband is unhandled\n", 2262306a36Sopenharmony_ci center_freq); 2362306a36Sopenharmony_ci fallthrough; 2462306a36Sopenharmony_ci case 2412 ... 2484: 2562306a36Sopenharmony_ci return RTW89_SAR_2GHZ_SUBBAND; 2662306a36Sopenharmony_ci case 5180 ... 5320: 2762306a36Sopenharmony_ci return RTW89_SAR_5GHZ_SUBBAND_1_2; 2862306a36Sopenharmony_ci case 5500 ... 5720: 2962306a36Sopenharmony_ci return RTW89_SAR_5GHZ_SUBBAND_2_E; 3062306a36Sopenharmony_ci case 5745 ... 5825: 3162306a36Sopenharmony_ci return RTW89_SAR_5GHZ_SUBBAND_3; 3262306a36Sopenharmony_ci case 5955 ... 6155: 3362306a36Sopenharmony_ci return RTW89_SAR_6GHZ_SUBBAND_5_L; 3462306a36Sopenharmony_ci case 6175 ... 6415: 3562306a36Sopenharmony_ci return RTW89_SAR_6GHZ_SUBBAND_5_H; 3662306a36Sopenharmony_ci case 6435 ... 6515: 3762306a36Sopenharmony_ci return RTW89_SAR_6GHZ_SUBBAND_6; 3862306a36Sopenharmony_ci case 6535 ... 6695: 3962306a36Sopenharmony_ci return RTW89_SAR_6GHZ_SUBBAND_7_L; 4062306a36Sopenharmony_ci case 6715 ... 6855: 4162306a36Sopenharmony_ci return RTW89_SAR_6GHZ_SUBBAND_7_H; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* freq 6875 (ch 185, 20MHz) spans RTW89_SAR_6GHZ_SUBBAND_7_H 4462306a36Sopenharmony_ci * and RTW89_SAR_6GHZ_SUBBAND_8, so directly describe it with 4562306a36Sopenharmony_ci * struct rtw89_sar_span in the following. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci case 6895 ... 7115: 4962306a36Sopenharmony_ci return RTW89_SAR_6GHZ_SUBBAND_8; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct rtw89_sar_span { 5462306a36Sopenharmony_ci enum rtw89_sar_subband subband_low; 5562306a36Sopenharmony_ci enum rtw89_sar_subband subband_high; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define RTW89_SAR_SPAN_VALID(span) ((span)->subband_high) 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define RTW89_SAR_6GHZ_SPAN_HEAD 6145 6162306a36Sopenharmony_ci#define RTW89_SAR_6GHZ_SPAN_IDX(center_freq) \ 6262306a36Sopenharmony_ci ((((int)(center_freq) - RTW89_SAR_6GHZ_SPAN_HEAD) / 5) / 2) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define RTW89_DECL_SAR_6GHZ_SPAN(center_freq, subband_l, subband_h) \ 6562306a36Sopenharmony_ci [RTW89_SAR_6GHZ_SPAN_IDX(center_freq)] = { \ 6662306a36Sopenharmony_ci .subband_low = RTW89_SAR_6GHZ_ ## subband_l, \ 6762306a36Sopenharmony_ci .subband_high = RTW89_SAR_6GHZ_ ## subband_h, \ 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Since 6GHz SAR subbands are not edge aligned, some cases span two SAR 7162306a36Sopenharmony_ci * subbands. In the following, we describe each of them with rtw89_sar_span. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic const struct rtw89_sar_span rtw89_sar_overlapping_6ghz[] = { 7462306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6145, SUBBAND_5_L, SUBBAND_5_H), 7562306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6165, SUBBAND_5_L, SUBBAND_5_H), 7662306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6185, SUBBAND_5_L, SUBBAND_5_H), 7762306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6505, SUBBAND_6, SUBBAND_7_L), 7862306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6525, SUBBAND_6, SUBBAND_7_L), 7962306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6545, SUBBAND_6, SUBBAND_7_L), 8062306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6665, SUBBAND_7_L, SUBBAND_7_H), 8162306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6705, SUBBAND_7_L, SUBBAND_7_H), 8262306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6825, SUBBAND_7_H, SUBBAND_8), 8362306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6865, SUBBAND_7_H, SUBBAND_8), 8462306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6875, SUBBAND_7_H, SUBBAND_8), 8562306a36Sopenharmony_ci RTW89_DECL_SAR_6GHZ_SPAN(6885, SUBBAND_7_H, SUBBAND_8), 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, 8962306a36Sopenharmony_ci u32 center_freq, s32 *cfg) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common; 9262306a36Sopenharmony_ci const struct rtw89_sar_span *span = NULL; 9362306a36Sopenharmony_ci enum rtw89_sar_subband subband_l, subband_h; 9462306a36Sopenharmony_ci int idx; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (center_freq >= RTW89_SAR_6GHZ_SPAN_HEAD) { 9762306a36Sopenharmony_ci idx = RTW89_SAR_6GHZ_SPAN_IDX(center_freq); 9862306a36Sopenharmony_ci /* To decrease size of rtw89_sar_overlapping_6ghz[], 9962306a36Sopenharmony_ci * RTW89_SAR_6GHZ_SPAN_IDX() truncates the leading NULLs 10062306a36Sopenharmony_ci * to make first span as index 0 of the table. So, if center 10162306a36Sopenharmony_ci * frequency is less than the first one, it will get netative. 10262306a36Sopenharmony_ci */ 10362306a36Sopenharmony_ci if (idx >= 0 && idx < ARRAY_SIZE(rtw89_sar_overlapping_6ghz)) 10462306a36Sopenharmony_ci span = &rtw89_sar_overlapping_6ghz[idx]; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (span && RTW89_SAR_SPAN_VALID(span)) { 10862306a36Sopenharmony_ci subband_l = span->subband_low; 10962306a36Sopenharmony_ci subband_h = span->subband_high; 11062306a36Sopenharmony_ci } else { 11162306a36Sopenharmony_ci subband_l = rtw89_sar_get_subband(rtwdev, center_freq); 11262306a36Sopenharmony_ci subband_h = subband_l; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 11662306a36Sopenharmony_ci "center_freq %u: SAR subband {%u, %u}\n", 11762306a36Sopenharmony_ci center_freq, subband_l, subband_h); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (!rtwsar->set[subband_l] && !rtwsar->set[subband_h]) 12062306a36Sopenharmony_ci return -ENODATA; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (!rtwsar->set[subband_l]) 12362306a36Sopenharmony_ci *cfg = rtwsar->cfg[subband_h]; 12462306a36Sopenharmony_ci else if (!rtwsar->set[subband_h]) 12562306a36Sopenharmony_ci *cfg = rtwsar->cfg[subband_l]; 12662306a36Sopenharmony_ci else 12762306a36Sopenharmony_ci *cfg = min(rtwsar->cfg[subband_l], rtwsar->cfg[subband_h]); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci return 0; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic const 13362306a36Sopenharmony_cistruct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { 13462306a36Sopenharmony_ci [RTW89_SAR_SOURCE_COMMON] = { 13562306a36Sopenharmony_ci .descr_sar_source = "RTW89_SAR_SOURCE_COMMON", 13662306a36Sopenharmony_ci .txpwr_factor_sar = 2, 13762306a36Sopenharmony_ci .query_sar_config = rtw89_query_sar_config_common, 13862306a36Sopenharmony_ci }, 13962306a36Sopenharmony_ci}; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data) \ 14262306a36Sopenharmony_ci do { \ 14362306a36Sopenharmony_ci typeof(_src) _s = (_src); \ 14462306a36Sopenharmony_ci typeof(_dev) _d = (_dev); \ 14562306a36Sopenharmony_ci BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source); \ 14662306a36Sopenharmony_ci BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config); \ 14762306a36Sopenharmony_ci lockdep_assert_held(&_d->mutex); \ 14862306a36Sopenharmony_ci _d->sar._cfg_name = *(_cfg_data); \ 14962306a36Sopenharmony_ci _d->sar.src = _s; \ 15062306a36Sopenharmony_ci } while (0) 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; 15562306a36Sopenharmony_ci s32 cfg_mac; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci cfg_mac = fct > fct_mac ? 15862306a36Sopenharmony_ci cfg >> (fct - fct_mac) : cfg << (fct_mac - fct); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return (s8)clamp_t(s32, cfg_mac, 16162306a36Sopenharmony_ci RTW89_SAR_TXPWR_MAC_MIN, 16262306a36Sopenharmony_ci RTW89_SAR_TXPWR_MAC_MAX); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, 16662306a36Sopenharmony_ci s8 cfg) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci const u8 fct = sar_hdl->txpwr_factor_sar; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (fct > RTW89_TAS_FACTOR) 17162306a36Sopenharmony_ci return cfg << (fct - RTW89_TAS_FACTOR); 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci return cfg >> (RTW89_TAS_FACTOR - fct); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, 17762306a36Sopenharmony_ci s8 cfg) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci const u8 fct = sar_hdl->txpwr_factor_sar; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (fct > RTW89_TAS_FACTOR) 18262306a36Sopenharmony_ci return cfg >> (fct - RTW89_TAS_FACTOR); 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci return cfg << (RTW89_TAS_FACTOR - fct); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cis8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci const enum rtw89_sar_sources src = rtwdev->sar.src; 19062306a36Sopenharmony_ci /* its members are protected by rtw89_sar_set_src() */ 19162306a36Sopenharmony_ci const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; 19262306a36Sopenharmony_ci struct rtw89_tas_info *tas = &rtwdev->tas; 19362306a36Sopenharmony_ci s8 delta; 19462306a36Sopenharmony_ci int ret; 19562306a36Sopenharmony_ci s32 cfg; 19662306a36Sopenharmony_ci u8 fct; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (src == RTW89_SAR_SOURCE_NONE) 20162306a36Sopenharmony_ci return RTW89_SAR_TXPWR_MAC_MAX; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); 20462306a36Sopenharmony_ci if (ret) 20562306a36Sopenharmony_ci return RTW89_SAR_TXPWR_MAC_MAX; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (tas->enable) { 20862306a36Sopenharmony_ci switch (tas->state) { 20962306a36Sopenharmony_ci case RTW89_TAS_STATE_DPR_OFF: 21062306a36Sopenharmony_ci return RTW89_SAR_TXPWR_MAC_MAX; 21162306a36Sopenharmony_ci case RTW89_TAS_STATE_DPR_ON: 21262306a36Sopenharmony_ci delta = rtw89_txpwr_tas_to_sar(sar_hdl, tas->delta); 21362306a36Sopenharmony_ci cfg -= delta; 21462306a36Sopenharmony_ci break; 21562306a36Sopenharmony_ci case RTW89_TAS_STATE_DPR_FORBID: 21662306a36Sopenharmony_ci default: 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci fct = sar_hdl->txpwr_factor_sar; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_civoid rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci const enum rtw89_sar_sources src = rtwdev->sar.src; 22962306a36Sopenharmony_ci /* its members are protected by rtw89_sar_set_src() */ 23062306a36Sopenharmony_ci const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; 23162306a36Sopenharmony_ci const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; 23262306a36Sopenharmony_ci int ret; 23362306a36Sopenharmony_ci s32 cfg; 23462306a36Sopenharmony_ci u8 fct; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (src == RTW89_SAR_SOURCE_NONE) { 23962306a36Sopenharmony_ci seq_puts(m, "no SAR is applied\n"); 24062306a36Sopenharmony_ci return; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci seq_printf(m, "source: %d (%s)\n", src, sar_hdl->descr_sar_source); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); 24662306a36Sopenharmony_ci if (ret) { 24762306a36Sopenharmony_ci seq_printf(m, "config: return code: %d\n", ret); 24862306a36Sopenharmony_ci seq_printf(m, "assign: max setting: %d (unit: 1/%lu dBm)\n", 24962306a36Sopenharmony_ci RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci fct = sar_hdl->txpwr_factor_sar; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci seq_printf(m, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct)); 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_civoid rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct rtw89_tas_info *tas = &rtwdev->tas; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (!tas->enable) { 26362306a36Sopenharmony_ci seq_puts(m, "no TAS is applied\n"); 26462306a36Sopenharmony_ci return; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci seq_printf(m, "DPR gap: %d\n", tas->dpr_gap); 26862306a36Sopenharmony_ci seq_printf(m, "TAS delta: %d\n", tas->delta); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, 27262306a36Sopenharmony_ci const struct rtw89_sar_cfg_common *sar) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci enum rtw89_sar_sources src; 27562306a36Sopenharmony_ci int ret = 0; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci mutex_lock(&rtwdev->mutex); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci src = rtwdev->sar.src; 28062306a36Sopenharmony_ci if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { 28162306a36Sopenharmony_ci rtw89_warn(rtwdev, "SAR source: %d is in use", src); 28262306a36Sopenharmony_ci ret = -EBUSY; 28362306a36Sopenharmony_ci goto exit; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); 28762306a36Sopenharmony_ci rtw89_core_set_chip_txpwr(rtwdev); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciexit: 29062306a36Sopenharmony_ci mutex_unlock(&rtwdev->mutex); 29162306a36Sopenharmony_ci return ret; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = { 29562306a36Sopenharmony_ci { .start_freq = 2412, .end_freq = 2484, }, 29662306a36Sopenharmony_ci { .start_freq = 5180, .end_freq = 5320, }, 29762306a36Sopenharmony_ci { .start_freq = 5500, .end_freq = 5720, }, 29862306a36Sopenharmony_ci { .start_freq = 5745, .end_freq = 5825, }, 29962306a36Sopenharmony_ci { .start_freq = 5955, .end_freq = 6155, }, 30062306a36Sopenharmony_ci { .start_freq = 6175, .end_freq = 6415, }, 30162306a36Sopenharmony_ci { .start_freq = 6435, .end_freq = 6515, }, 30262306a36Sopenharmony_ci { .start_freq = 6535, .end_freq = 6695, }, 30362306a36Sopenharmony_ci { .start_freq = 6715, .end_freq = 6875, }, 30462306a36Sopenharmony_ci { .start_freq = 6875, .end_freq = 7115, }, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic_assert(RTW89_SAR_SUBBAND_NR == 30862306a36Sopenharmony_ci ARRAY_SIZE(rtw89_common_sar_freq_ranges)); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ciconst struct cfg80211_sar_capa rtw89_sar_capa = { 31162306a36Sopenharmony_ci .type = NL80211_SAR_TYPE_POWER, 31262306a36Sopenharmony_ci .num_freq_ranges = ARRAY_SIZE(rtw89_common_sar_freq_ranges), 31362306a36Sopenharmony_ci .freq_ranges = rtw89_common_sar_freq_ranges, 31462306a36Sopenharmony_ci}; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciint rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, 31762306a36Sopenharmony_ci const struct cfg80211_sar_specs *sar) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct rtw89_dev *rtwdev = hw->priv; 32062306a36Sopenharmony_ci struct rtw89_sar_cfg_common sar_common = {0}; 32162306a36Sopenharmony_ci u8 fct; 32262306a36Sopenharmony_ci u32 freq_start; 32362306a36Sopenharmony_ci u32 freq_end; 32462306a36Sopenharmony_ci s32 power; 32562306a36Sopenharmony_ci u32 i, idx; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (sar->type != NL80211_SAR_TYPE_POWER) 32862306a36Sopenharmony_ci return -EINVAL; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci fct = rtw89_sar_handlers[RTW89_SAR_SOURCE_COMMON].txpwr_factor_sar; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci for (i = 0; i < sar->num_sub_specs; i++) { 33362306a36Sopenharmony_ci idx = sar->sub_specs[i].freq_range_index; 33462306a36Sopenharmony_ci if (idx >= ARRAY_SIZE(rtw89_common_sar_freq_ranges)) 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci freq_start = rtw89_common_sar_freq_ranges[idx].start_freq; 33862306a36Sopenharmony_ci freq_end = rtw89_common_sar_freq_ranges[idx].end_freq; 33962306a36Sopenharmony_ci power = sar->sub_specs[i].power; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 34262306a36Sopenharmony_ci "On freq %u to %u, set SAR limit %d (unit: 1/%lu dBm)\n", 34362306a36Sopenharmony_ci freq_start, freq_end, power, BIT(fct)); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci sar_common.set[idx] = true; 34662306a36Sopenharmony_ci sar_common.cfg[idx] = power; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return rtw89_apply_sar_common(rtwdev, &sar_common); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void rtw89_tas_state_update(struct rtw89_dev *rtwdev) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci const enum rtw89_sar_sources src = rtwdev->sar.src; 35562306a36Sopenharmony_ci /* its members are protected by rtw89_sar_set_src() */ 35662306a36Sopenharmony_ci const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; 35762306a36Sopenharmony_ci struct rtw89_tas_info *tas = &rtwdev->tas; 35862306a36Sopenharmony_ci s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT; 35962306a36Sopenharmony_ci s32 dpr_on_threshold, dpr_off_threshold, cfg; 36062306a36Sopenharmony_ci enum rtw89_tas_state state = tas->state; 36162306a36Sopenharmony_ci const struct rtw89_chan *chan; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (src == RTW89_SAR_SOURCE_NONE) 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); 37062306a36Sopenharmony_ci ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg); 37162306a36Sopenharmony_ci if (ret) 37262306a36Sopenharmony_ci return; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (tas->delta >= cfg) { 37762306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 37862306a36Sopenharmony_ci "TAS delta exceed SAR limit\n"); 37962306a36Sopenharmony_ci state = RTW89_TAS_STATE_DPR_FORBID; 38062306a36Sopenharmony_ci goto out; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci dpr_on_threshold = cfg; 38462306a36Sopenharmony_ci dpr_off_threshold = cfg - tas->dpr_gap; 38562306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 38662306a36Sopenharmony_ci "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n", 38762306a36Sopenharmony_ci dpr_on_threshold, dpr_off_threshold, txpwr_avg); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (txpwr_avg >= dpr_on_threshold) 39062306a36Sopenharmony_ci state = RTW89_TAS_STATE_DPR_ON; 39162306a36Sopenharmony_ci else if (txpwr_avg < dpr_off_threshold) 39262306a36Sopenharmony_ci state = RTW89_TAS_STATE_DPR_OFF; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ciout: 39562306a36Sopenharmony_ci if (tas->state == state) 39662306a36Sopenharmony_ci return; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 39962306a36Sopenharmony_ci "TAS old state: %d, new state: %d\n", tas->state, state); 40062306a36Sopenharmony_ci tas->state = state; 40162306a36Sopenharmony_ci rtw89_core_set_chip_txpwr(rtwdev); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_civoid rtw89_tas_init(struct rtw89_dev *rtwdev) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct rtw89_tas_info *tas = &rtwdev->tas; 40762306a36Sopenharmony_ci int ret; 40862306a36Sopenharmony_ci u8 val; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &val); 41162306a36Sopenharmony_ci if (ret) { 41262306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 41362306a36Sopenharmony_ci "acpi: cannot get TAS: %d\n", ret); 41462306a36Sopenharmony_ci return; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci switch (val) { 41862306a36Sopenharmony_ci case 0: 41962306a36Sopenharmony_ci tas->enable = false; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case 1: 42262306a36Sopenharmony_ci tas->enable = true; 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci default: 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if (!tas->enable) { 42962306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, "TAS not enable\n"); 43062306a36Sopenharmony_ci return; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci tas->dpr_gap = RTW89_TAS_DPR_GAP; 43462306a36Sopenharmony_ci tas->delta = RTW89_TAS_DELTA; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_civoid rtw89_tas_reset(struct rtw89_dev *rtwdev) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct rtw89_tas_info *tas = &rtwdev->tas; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (!tas->enable) 44262306a36Sopenharmony_ci return; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history)); 44562306a36Sopenharmony_ci tas->total_txpwr = 0; 44662306a36Sopenharmony_ci tas->cur_idx = 0; 44762306a36Sopenharmony_ci tas->state = RTW89_TAS_STATE_DPR_OFF; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cistatic const struct rtw89_reg_def txpwr_regs[] = { 45162306a36Sopenharmony_ci {R_PATH0_TXPWR, B_PATH0_TXPWR}, 45262306a36Sopenharmony_ci {R_PATH1_TXPWR, B_PATH1_TXPWR}, 45362306a36Sopenharmony_ci}; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_civoid rtw89_tas_track(struct rtw89_dev *rtwdev) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; 45862306a36Sopenharmony_ci const enum rtw89_sar_sources src = rtwdev->sar.src; 45962306a36Sopenharmony_ci u8 max_nss_num = rtwdev->chip->rf_path_num; 46062306a36Sopenharmony_ci struct rtw89_tas_info *tas = &rtwdev->tas; 46162306a36Sopenharmony_ci s16 tmp, txpwr, instant_txpwr = 0; 46262306a36Sopenharmony_ci u32 val; 46362306a36Sopenharmony_ci int i; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM) 46962306a36Sopenharmony_ci return; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci for (i = 0; i < max_nss_num; i++) { 47262306a36Sopenharmony_ci val = rtw89_phy_read32_mask(rtwdev, txpwr_regs[i].addr, 47362306a36Sopenharmony_ci txpwr_regs[i].mask); 47462306a36Sopenharmony_ci tmp = sign_extend32(val, 8); 47562306a36Sopenharmony_ci if (tmp <= 0) 47662306a36Sopenharmony_ci return; 47762306a36Sopenharmony_ci instant_txpwr += tmp; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci instant_txpwr /= max_nss_num; 48162306a36Sopenharmony_ci /* in unit of 0.25 dBm multiply by percentage */ 48262306a36Sopenharmony_ci txpwr = instant_txpwr * env->ifs_clm_tx_ratio; 48362306a36Sopenharmony_ci tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx]; 48462306a36Sopenharmony_ci tas->txpwr_history[tas->cur_idx] = txpwr; 48562306a36Sopenharmony_ci rtw89_debug(rtwdev, RTW89_DBG_SAR, 48662306a36Sopenharmony_ci "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n", 48762306a36Sopenharmony_ci instant_txpwr, env->ifs_clm_tx_ratio, txpwr); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci rtw89_tas_state_update(rtwdev); 49262306a36Sopenharmony_ci} 493