18e93258fSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 28e93258fSBjoern A. Zeeb /* Copyright(c) 2019-2020 Realtek Corporation 38e93258fSBjoern A. Zeeb */ 48e93258fSBjoern A. Zeeb 5*6d67aabdSBjoern A. Zeeb #include "acpi.h" 68e93258fSBjoern A. Zeeb #include "debug.h" 7*6d67aabdSBjoern A. Zeeb #include "phy.h" 8*6d67aabdSBjoern A. Zeeb #include "reg.h" 98e93258fSBjoern A. Zeeb #include "sar.h" 108e93258fSBjoern A. Zeeb 11*6d67aabdSBjoern A. Zeeb #define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */ 12*6d67aabdSBjoern A. Zeeb #define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR) 13*6d67aabdSBjoern A. Zeeb #define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR) 14*6d67aabdSBjoern A. Zeeb 158e93258fSBjoern A. Zeeb static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev, 168e93258fSBjoern A. Zeeb u32 center_freq) 178e93258fSBjoern A. Zeeb { 188e93258fSBjoern A. Zeeb switch (center_freq) { 198e93258fSBjoern A. Zeeb default: 208e93258fSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 218e93258fSBjoern A. Zeeb "center freq: %u to SAR subband is unhandled\n", 228e93258fSBjoern A. Zeeb center_freq); 238e93258fSBjoern A. Zeeb fallthrough; 248e93258fSBjoern A. Zeeb case 2412 ... 2484: 258e93258fSBjoern A. Zeeb return RTW89_SAR_2GHZ_SUBBAND; 268e93258fSBjoern A. Zeeb case 5180 ... 5320: 278e93258fSBjoern A. Zeeb return RTW89_SAR_5GHZ_SUBBAND_1_2; 288e93258fSBjoern A. Zeeb case 5500 ... 5720: 298e93258fSBjoern A. Zeeb return RTW89_SAR_5GHZ_SUBBAND_2_E; 308e93258fSBjoern A. Zeeb case 5745 ... 5825: 318e93258fSBjoern A. Zeeb return RTW89_SAR_5GHZ_SUBBAND_3; 328e93258fSBjoern A. Zeeb case 5955 ... 6155: 338e93258fSBjoern A. Zeeb return RTW89_SAR_6GHZ_SUBBAND_5_L; 348e93258fSBjoern A. Zeeb case 6175 ... 6415: 358e93258fSBjoern A. Zeeb return RTW89_SAR_6GHZ_SUBBAND_5_H; 368e93258fSBjoern A. Zeeb case 6435 ... 6515: 378e93258fSBjoern A. Zeeb return RTW89_SAR_6GHZ_SUBBAND_6; 388e93258fSBjoern A. Zeeb case 6535 ... 6695: 398e93258fSBjoern A. Zeeb return RTW89_SAR_6GHZ_SUBBAND_7_L; 408e93258fSBjoern A. Zeeb case 6715 ... 6855: 418e93258fSBjoern A. Zeeb return RTW89_SAR_6GHZ_SUBBAND_7_H; 428e93258fSBjoern A. Zeeb 438e93258fSBjoern A. Zeeb /* freq 6875 (ch 185, 20MHz) spans RTW89_SAR_6GHZ_SUBBAND_7_H 448e93258fSBjoern A. Zeeb * and RTW89_SAR_6GHZ_SUBBAND_8, so directly describe it with 458e93258fSBjoern A. Zeeb * struct rtw89_sar_span in the following. 468e93258fSBjoern A. Zeeb */ 478e93258fSBjoern A. Zeeb 488e93258fSBjoern A. Zeeb case 6895 ... 7115: 498e93258fSBjoern A. Zeeb return RTW89_SAR_6GHZ_SUBBAND_8; 508e93258fSBjoern A. Zeeb } 518e93258fSBjoern A. Zeeb } 528e93258fSBjoern A. Zeeb 538e93258fSBjoern A. Zeeb struct rtw89_sar_span { 548e93258fSBjoern A. Zeeb enum rtw89_sar_subband subband_low; 558e93258fSBjoern A. Zeeb enum rtw89_sar_subband subband_high; 568e93258fSBjoern A. Zeeb }; 578e93258fSBjoern A. Zeeb 588e93258fSBjoern A. Zeeb #define RTW89_SAR_SPAN_VALID(span) ((span)->subband_high) 598e93258fSBjoern A. Zeeb 608e93258fSBjoern A. Zeeb #define RTW89_SAR_6GHZ_SPAN_HEAD 6145 618e93258fSBjoern A. Zeeb #define RTW89_SAR_6GHZ_SPAN_IDX(center_freq) \ 628e93258fSBjoern A. Zeeb ((((int)(center_freq) - RTW89_SAR_6GHZ_SPAN_HEAD) / 5) / 2) 638e93258fSBjoern A. Zeeb 648e93258fSBjoern A. Zeeb #define RTW89_DECL_SAR_6GHZ_SPAN(center_freq, subband_l, subband_h) \ 658e93258fSBjoern A. Zeeb [RTW89_SAR_6GHZ_SPAN_IDX(center_freq)] = { \ 668e93258fSBjoern A. Zeeb .subband_low = RTW89_SAR_6GHZ_ ## subband_l, \ 678e93258fSBjoern A. Zeeb .subband_high = RTW89_SAR_6GHZ_ ## subband_h, \ 688e93258fSBjoern A. Zeeb } 698e93258fSBjoern A. Zeeb 708e93258fSBjoern A. Zeeb /* Since 6GHz SAR subbands are not edge aligned, some cases span two SAR 718e93258fSBjoern A. Zeeb * subbands. In the following, we describe each of them with rtw89_sar_span. 728e93258fSBjoern A. Zeeb */ 738e93258fSBjoern A. Zeeb static const struct rtw89_sar_span rtw89_sar_overlapping_6ghz[] = { 748e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6145, SUBBAND_5_L, SUBBAND_5_H), 758e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6165, SUBBAND_5_L, SUBBAND_5_H), 768e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6185, SUBBAND_5_L, SUBBAND_5_H), 778e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6505, SUBBAND_6, SUBBAND_7_L), 788e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6525, SUBBAND_6, SUBBAND_7_L), 798e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6545, SUBBAND_6, SUBBAND_7_L), 808e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6665, SUBBAND_7_L, SUBBAND_7_H), 818e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6705, SUBBAND_7_L, SUBBAND_7_H), 828e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6825, SUBBAND_7_H, SUBBAND_8), 838e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6865, SUBBAND_7_H, SUBBAND_8), 848e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6875, SUBBAND_7_H, SUBBAND_8), 858e93258fSBjoern A. Zeeb RTW89_DECL_SAR_6GHZ_SPAN(6885, SUBBAND_7_H, SUBBAND_8), 868e93258fSBjoern A. Zeeb }; 878e93258fSBjoern A. Zeeb 88*6d67aabdSBjoern A. Zeeb static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, 89*6d67aabdSBjoern A. Zeeb u32 center_freq, s32 *cfg) 908e93258fSBjoern A. Zeeb { 918e93258fSBjoern A. Zeeb struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common; 928e93258fSBjoern A. Zeeb const struct rtw89_sar_span *span = NULL; 938e93258fSBjoern A. Zeeb enum rtw89_sar_subband subband_l, subband_h; 948e93258fSBjoern A. Zeeb int idx; 958e93258fSBjoern A. Zeeb 96*6d67aabdSBjoern A. Zeeb if (center_freq >= RTW89_SAR_6GHZ_SPAN_HEAD) { 978e93258fSBjoern A. Zeeb idx = RTW89_SAR_6GHZ_SPAN_IDX(center_freq); 988e93258fSBjoern A. Zeeb /* To decrease size of rtw89_sar_overlapping_6ghz[], 998e93258fSBjoern A. Zeeb * RTW89_SAR_6GHZ_SPAN_IDX() truncates the leading NULLs 1008e93258fSBjoern A. Zeeb * to make first span as index 0 of the table. So, if center 1018e93258fSBjoern A. Zeeb * frequency is less than the first one, it will get netative. 1028e93258fSBjoern A. Zeeb */ 1038e93258fSBjoern A. Zeeb if (idx >= 0 && idx < ARRAY_SIZE(rtw89_sar_overlapping_6ghz)) 1048e93258fSBjoern A. Zeeb span = &rtw89_sar_overlapping_6ghz[idx]; 1058e93258fSBjoern A. Zeeb } 1068e93258fSBjoern A. Zeeb 1078e93258fSBjoern A. Zeeb if (span && RTW89_SAR_SPAN_VALID(span)) { 1088e93258fSBjoern A. Zeeb subband_l = span->subband_low; 1098e93258fSBjoern A. Zeeb subband_h = span->subband_high; 1108e93258fSBjoern A. Zeeb } else { 1118e93258fSBjoern A. Zeeb subband_l = rtw89_sar_get_subband(rtwdev, center_freq); 1128e93258fSBjoern A. Zeeb subband_h = subband_l; 1138e93258fSBjoern A. Zeeb } 1148e93258fSBjoern A. Zeeb 1158e93258fSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 116*6d67aabdSBjoern A. Zeeb "center_freq %u: SAR subband {%u, %u}\n", 117*6d67aabdSBjoern A. Zeeb center_freq, subband_l, subband_h); 1188e93258fSBjoern A. Zeeb 1198e93258fSBjoern A. Zeeb if (!rtwsar->set[subband_l] && !rtwsar->set[subband_h]) 1208e93258fSBjoern A. Zeeb return -ENODATA; 1218e93258fSBjoern A. Zeeb 1228e93258fSBjoern A. Zeeb if (!rtwsar->set[subband_l]) 1238e93258fSBjoern A. Zeeb *cfg = rtwsar->cfg[subband_h]; 1248e93258fSBjoern A. Zeeb else if (!rtwsar->set[subband_h]) 1258e93258fSBjoern A. Zeeb *cfg = rtwsar->cfg[subband_l]; 1268e93258fSBjoern A. Zeeb else 1278e93258fSBjoern A. Zeeb *cfg = min(rtwsar->cfg[subband_l], rtwsar->cfg[subband_h]); 1288e93258fSBjoern A. Zeeb 1298e93258fSBjoern A. Zeeb return 0; 1308e93258fSBjoern A. Zeeb } 1318e93258fSBjoern A. Zeeb 1328e93258fSBjoern A. Zeeb static const 1338e93258fSBjoern A. Zeeb struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { 1348e93258fSBjoern A. Zeeb [RTW89_SAR_SOURCE_COMMON] = { 1358e93258fSBjoern A. Zeeb .descr_sar_source = "RTW89_SAR_SOURCE_COMMON", 1368e93258fSBjoern A. Zeeb .txpwr_factor_sar = 2, 1378e93258fSBjoern A. Zeeb .query_sar_config = rtw89_query_sar_config_common, 1388e93258fSBjoern A. Zeeb }, 1398e93258fSBjoern A. Zeeb }; 1408e93258fSBjoern A. Zeeb 1418e93258fSBjoern A. Zeeb #define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data) \ 1428e93258fSBjoern A. Zeeb do { \ 1438e93258fSBjoern A. Zeeb typeof(_src) _s = (_src); \ 1448e93258fSBjoern A. Zeeb typeof(_dev) _d = (_dev); \ 1458e93258fSBjoern A. Zeeb BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source); \ 1468e93258fSBjoern A. Zeeb BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config); \ 1478e93258fSBjoern A. Zeeb lockdep_assert_held(&_d->mutex); \ 1488e93258fSBjoern A. Zeeb _d->sar._cfg_name = *(_cfg_data); \ 1498e93258fSBjoern A. Zeeb _d->sar.src = _s; \ 1508e93258fSBjoern A. Zeeb } while (0) 1518e93258fSBjoern A. Zeeb 1528e93258fSBjoern A. Zeeb static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg) 1538e93258fSBjoern A. Zeeb { 1548e93258fSBjoern A. Zeeb const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; 1558e93258fSBjoern A. Zeeb s32 cfg_mac; 1568e93258fSBjoern A. Zeeb 1578e93258fSBjoern A. Zeeb cfg_mac = fct > fct_mac ? 1588e93258fSBjoern A. Zeeb cfg >> (fct - fct_mac) : cfg << (fct_mac - fct); 1598e93258fSBjoern A. Zeeb 1608e93258fSBjoern A. Zeeb return (s8)clamp_t(s32, cfg_mac, 1618e93258fSBjoern A. Zeeb RTW89_SAR_TXPWR_MAC_MIN, 1628e93258fSBjoern A. Zeeb RTW89_SAR_TXPWR_MAC_MAX); 1638e93258fSBjoern A. Zeeb } 1648e93258fSBjoern A. Zeeb 165*6d67aabdSBjoern A. Zeeb static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, 166*6d67aabdSBjoern A. Zeeb s8 cfg) 167*6d67aabdSBjoern A. Zeeb { 168*6d67aabdSBjoern A. Zeeb const u8 fct = sar_hdl->txpwr_factor_sar; 169*6d67aabdSBjoern A. Zeeb 170*6d67aabdSBjoern A. Zeeb if (fct > RTW89_TAS_FACTOR) 171*6d67aabdSBjoern A. Zeeb return cfg << (fct - RTW89_TAS_FACTOR); 172*6d67aabdSBjoern A. Zeeb else 173*6d67aabdSBjoern A. Zeeb return cfg >> (RTW89_TAS_FACTOR - fct); 174*6d67aabdSBjoern A. Zeeb } 175*6d67aabdSBjoern A. Zeeb 176*6d67aabdSBjoern A. Zeeb static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, 177*6d67aabdSBjoern A. Zeeb s8 cfg) 178*6d67aabdSBjoern A. Zeeb { 179*6d67aabdSBjoern A. Zeeb const u8 fct = sar_hdl->txpwr_factor_sar; 180*6d67aabdSBjoern A. Zeeb 181*6d67aabdSBjoern A. Zeeb if (fct > RTW89_TAS_FACTOR) 182*6d67aabdSBjoern A. Zeeb return cfg >> (fct - RTW89_TAS_FACTOR); 183*6d67aabdSBjoern A. Zeeb else 184*6d67aabdSBjoern A. Zeeb return cfg << (RTW89_TAS_FACTOR - fct); 185*6d67aabdSBjoern A. Zeeb } 186*6d67aabdSBjoern A. Zeeb 187*6d67aabdSBjoern A. Zeeb s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) 1888e93258fSBjoern A. Zeeb { 1898e93258fSBjoern A. Zeeb const enum rtw89_sar_sources src = rtwdev->sar.src; 1908e93258fSBjoern A. Zeeb /* its members are protected by rtw89_sar_set_src() */ 1918e93258fSBjoern A. Zeeb const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; 192*6d67aabdSBjoern A. Zeeb struct rtw89_tas_info *tas = &rtwdev->tas; 193*6d67aabdSBjoern A. Zeeb s8 delta; 1948e93258fSBjoern A. Zeeb int ret; 1958e93258fSBjoern A. Zeeb s32 cfg; 1968e93258fSBjoern A. Zeeb u8 fct; 1978e93258fSBjoern A. Zeeb 1988e93258fSBjoern A. Zeeb lockdep_assert_held(&rtwdev->mutex); 1998e93258fSBjoern A. Zeeb 2008e93258fSBjoern A. Zeeb if (src == RTW89_SAR_SOURCE_NONE) 2018e93258fSBjoern A. Zeeb return RTW89_SAR_TXPWR_MAC_MAX; 2028e93258fSBjoern A. Zeeb 203*6d67aabdSBjoern A. Zeeb ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); 2048e93258fSBjoern A. Zeeb if (ret) 2058e93258fSBjoern A. Zeeb return RTW89_SAR_TXPWR_MAC_MAX; 2068e93258fSBjoern A. Zeeb 207*6d67aabdSBjoern A. Zeeb if (tas->enable) { 208*6d67aabdSBjoern A. Zeeb switch (tas->state) { 209*6d67aabdSBjoern A. Zeeb case RTW89_TAS_STATE_DPR_OFF: 210*6d67aabdSBjoern A. Zeeb return RTW89_SAR_TXPWR_MAC_MAX; 211*6d67aabdSBjoern A. Zeeb case RTW89_TAS_STATE_DPR_ON: 212*6d67aabdSBjoern A. Zeeb delta = rtw89_txpwr_tas_to_sar(sar_hdl, tas->delta); 213*6d67aabdSBjoern A. Zeeb cfg -= delta; 214*6d67aabdSBjoern A. Zeeb break; 215*6d67aabdSBjoern A. Zeeb case RTW89_TAS_STATE_DPR_FORBID: 216*6d67aabdSBjoern A. Zeeb default: 217*6d67aabdSBjoern A. Zeeb break; 218*6d67aabdSBjoern A. Zeeb } 219*6d67aabdSBjoern A. Zeeb } 220*6d67aabdSBjoern A. Zeeb 2218e93258fSBjoern A. Zeeb fct = sar_hdl->txpwr_factor_sar; 2228e93258fSBjoern A. Zeeb 2238e93258fSBjoern A. Zeeb return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg); 2248e93258fSBjoern A. Zeeb } 2258e93258fSBjoern A. Zeeb 226*6d67aabdSBjoern A. Zeeb void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq) 2278e93258fSBjoern A. Zeeb { 2288e93258fSBjoern A. Zeeb const enum rtw89_sar_sources src = rtwdev->sar.src; 2298e93258fSBjoern A. Zeeb /* its members are protected by rtw89_sar_set_src() */ 2308e93258fSBjoern A. Zeeb const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; 2318e93258fSBjoern A. Zeeb const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; 2328e93258fSBjoern A. Zeeb int ret; 2338e93258fSBjoern A. Zeeb s32 cfg; 2348e93258fSBjoern A. Zeeb u8 fct; 2358e93258fSBjoern A. Zeeb 2368e93258fSBjoern A. Zeeb lockdep_assert_held(&rtwdev->mutex); 2378e93258fSBjoern A. Zeeb 2388e93258fSBjoern A. Zeeb if (src == RTW89_SAR_SOURCE_NONE) { 2398e93258fSBjoern A. Zeeb seq_puts(m, "no SAR is applied\n"); 2408e93258fSBjoern A. Zeeb return; 2418e93258fSBjoern A. Zeeb } 2428e93258fSBjoern A. Zeeb 2438e93258fSBjoern A. Zeeb seq_printf(m, "source: %d (%s)\n", src, sar_hdl->descr_sar_source); 2448e93258fSBjoern A. Zeeb 245*6d67aabdSBjoern A. Zeeb ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); 2468e93258fSBjoern A. Zeeb if (ret) { 2478e93258fSBjoern A. Zeeb seq_printf(m, "config: return code: %d\n", ret); 2488e93258fSBjoern A. Zeeb seq_printf(m, "assign: max setting: %d (unit: 1/%lu dBm)\n", 2498e93258fSBjoern A. Zeeb RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); 2508e93258fSBjoern A. Zeeb return; 2518e93258fSBjoern A. Zeeb } 2528e93258fSBjoern A. Zeeb 2538e93258fSBjoern A. Zeeb fct = sar_hdl->txpwr_factor_sar; 2548e93258fSBjoern A. Zeeb 2558e93258fSBjoern A. Zeeb seq_printf(m, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct)); 2568e93258fSBjoern A. Zeeb } 2578e93258fSBjoern A. Zeeb 258*6d67aabdSBjoern A. Zeeb void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev) 259*6d67aabdSBjoern A. Zeeb { 260*6d67aabdSBjoern A. Zeeb struct rtw89_tas_info *tas = &rtwdev->tas; 261*6d67aabdSBjoern A. Zeeb 262*6d67aabdSBjoern A. Zeeb if (!tas->enable) { 263*6d67aabdSBjoern A. Zeeb seq_puts(m, "no TAS is applied\n"); 264*6d67aabdSBjoern A. Zeeb return; 265*6d67aabdSBjoern A. Zeeb } 266*6d67aabdSBjoern A. Zeeb 267*6d67aabdSBjoern A. Zeeb seq_printf(m, "DPR gap: %d\n", tas->dpr_gap); 268*6d67aabdSBjoern A. Zeeb seq_printf(m, "TAS delta: %d\n", tas->delta); 269*6d67aabdSBjoern A. Zeeb } 270*6d67aabdSBjoern A. Zeeb 2718e93258fSBjoern A. Zeeb static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, 2728e93258fSBjoern A. Zeeb const struct rtw89_sar_cfg_common *sar) 2738e93258fSBjoern A. Zeeb { 2748e93258fSBjoern A. Zeeb enum rtw89_sar_sources src; 2758e93258fSBjoern A. Zeeb int ret = 0; 2768e93258fSBjoern A. Zeeb 2778e93258fSBjoern A. Zeeb mutex_lock(&rtwdev->mutex); 2788e93258fSBjoern A. Zeeb 2798e93258fSBjoern A. Zeeb src = rtwdev->sar.src; 2808e93258fSBjoern A. Zeeb if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { 2818e93258fSBjoern A. Zeeb rtw89_warn(rtwdev, "SAR source: %d is in use", src); 2828e93258fSBjoern A. Zeeb ret = -EBUSY; 2838e93258fSBjoern A. Zeeb goto exit; 2848e93258fSBjoern A. Zeeb } 2858e93258fSBjoern A. Zeeb 2868e93258fSBjoern A. Zeeb rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); 2878e93258fSBjoern A. Zeeb rtw89_core_set_chip_txpwr(rtwdev); 2888e93258fSBjoern A. Zeeb 2898e93258fSBjoern A. Zeeb exit: 2908e93258fSBjoern A. Zeeb mutex_unlock(&rtwdev->mutex); 2918e93258fSBjoern A. Zeeb return ret; 2928e93258fSBjoern A. Zeeb } 2938e93258fSBjoern A. Zeeb 2948e93258fSBjoern A. Zeeb static const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = { 2958e93258fSBjoern A. Zeeb { .start_freq = 2412, .end_freq = 2484, }, 2968e93258fSBjoern A. Zeeb { .start_freq = 5180, .end_freq = 5320, }, 2978e93258fSBjoern A. Zeeb { .start_freq = 5500, .end_freq = 5720, }, 2988e93258fSBjoern A. Zeeb { .start_freq = 5745, .end_freq = 5825, }, 2998e93258fSBjoern A. Zeeb { .start_freq = 5955, .end_freq = 6155, }, 3008e93258fSBjoern A. Zeeb { .start_freq = 6175, .end_freq = 6415, }, 3018e93258fSBjoern A. Zeeb { .start_freq = 6435, .end_freq = 6515, }, 3028e93258fSBjoern A. Zeeb { .start_freq = 6535, .end_freq = 6695, }, 3038e93258fSBjoern A. Zeeb { .start_freq = 6715, .end_freq = 6875, }, 3048e93258fSBjoern A. Zeeb { .start_freq = 6875, .end_freq = 7115, }, 3058e93258fSBjoern A. Zeeb }; 3068e93258fSBjoern A. Zeeb 3078e93258fSBjoern A. Zeeb #if defined(__linux__) 3088e93258fSBjoern A. Zeeb static_assert(RTW89_SAR_SUBBAND_NR == 3098e93258fSBjoern A. Zeeb #elif defined(__FreeBSD__) 3108e93258fSBjoern A. Zeeb rtw89_static_assert(RTW89_SAR_SUBBAND_NR == 3118e93258fSBjoern A. Zeeb #endif 3128e93258fSBjoern A. Zeeb ARRAY_SIZE(rtw89_common_sar_freq_ranges)); 3138e93258fSBjoern A. Zeeb 3148e93258fSBjoern A. Zeeb const struct cfg80211_sar_capa rtw89_sar_capa = { 3158e93258fSBjoern A. Zeeb .type = NL80211_SAR_TYPE_POWER, 3168e93258fSBjoern A. Zeeb .num_freq_ranges = ARRAY_SIZE(rtw89_common_sar_freq_ranges), 3178e93258fSBjoern A. Zeeb .freq_ranges = rtw89_common_sar_freq_ranges, 3188e93258fSBjoern A. Zeeb }; 3198e93258fSBjoern A. Zeeb 3208e93258fSBjoern A. Zeeb int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, 3218e93258fSBjoern A. Zeeb const struct cfg80211_sar_specs *sar) 3228e93258fSBjoern A. Zeeb { 3238e93258fSBjoern A. Zeeb struct rtw89_dev *rtwdev = hw->priv; 3248e93258fSBjoern A. Zeeb struct rtw89_sar_cfg_common sar_common = {0}; 3258e93258fSBjoern A. Zeeb u8 fct; 3268e93258fSBjoern A. Zeeb u32 freq_start; 3278e93258fSBjoern A. Zeeb u32 freq_end; 3288e93258fSBjoern A. Zeeb s32 power; 3298e93258fSBjoern A. Zeeb u32 i, idx; 3308e93258fSBjoern A. Zeeb 3318e93258fSBjoern A. Zeeb if (sar->type != NL80211_SAR_TYPE_POWER) 3328e93258fSBjoern A. Zeeb return -EINVAL; 3338e93258fSBjoern A. Zeeb 3348e93258fSBjoern A. Zeeb fct = rtw89_sar_handlers[RTW89_SAR_SOURCE_COMMON].txpwr_factor_sar; 3358e93258fSBjoern A. Zeeb 3368e93258fSBjoern A. Zeeb for (i = 0; i < sar->num_sub_specs; i++) { 3378e93258fSBjoern A. Zeeb idx = sar->sub_specs[i].freq_range_index; 3388e93258fSBjoern A. Zeeb if (idx >= ARRAY_SIZE(rtw89_common_sar_freq_ranges)) 3398e93258fSBjoern A. Zeeb return -EINVAL; 3408e93258fSBjoern A. Zeeb 3418e93258fSBjoern A. Zeeb freq_start = rtw89_common_sar_freq_ranges[idx].start_freq; 3428e93258fSBjoern A. Zeeb freq_end = rtw89_common_sar_freq_ranges[idx].end_freq; 3438e93258fSBjoern A. Zeeb power = sar->sub_specs[i].power; 3448e93258fSBjoern A. Zeeb 3458e93258fSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 3468e93258fSBjoern A. Zeeb "On freq %u to %u, set SAR limit %d (unit: 1/%lu dBm)\n", 3478e93258fSBjoern A. Zeeb freq_start, freq_end, power, BIT(fct)); 3488e93258fSBjoern A. Zeeb 3498e93258fSBjoern A. Zeeb sar_common.set[idx] = true; 3508e93258fSBjoern A. Zeeb sar_common.cfg[idx] = power; 3518e93258fSBjoern A. Zeeb } 3528e93258fSBjoern A. Zeeb 3538e93258fSBjoern A. Zeeb return rtw89_apply_sar_common(rtwdev, &sar_common); 3548e93258fSBjoern A. Zeeb } 355*6d67aabdSBjoern A. Zeeb 356*6d67aabdSBjoern A. Zeeb static void rtw89_tas_state_update(struct rtw89_dev *rtwdev) 357*6d67aabdSBjoern A. Zeeb { 358*6d67aabdSBjoern A. Zeeb const enum rtw89_sar_sources src = rtwdev->sar.src; 359*6d67aabdSBjoern A. Zeeb /* its members are protected by rtw89_sar_set_src() */ 360*6d67aabdSBjoern A. Zeeb const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; 361*6d67aabdSBjoern A. Zeeb struct rtw89_tas_info *tas = &rtwdev->tas; 362*6d67aabdSBjoern A. Zeeb s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT; 363*6d67aabdSBjoern A. Zeeb s32 dpr_on_threshold, dpr_off_threshold, cfg; 364*6d67aabdSBjoern A. Zeeb enum rtw89_tas_state state = tas->state; 365*6d67aabdSBjoern A. Zeeb const struct rtw89_chan *chan; 366*6d67aabdSBjoern A. Zeeb int ret; 367*6d67aabdSBjoern A. Zeeb 368*6d67aabdSBjoern A. Zeeb lockdep_assert_held(&rtwdev->mutex); 369*6d67aabdSBjoern A. Zeeb 370*6d67aabdSBjoern A. Zeeb if (src == RTW89_SAR_SOURCE_NONE) 371*6d67aabdSBjoern A. Zeeb return; 372*6d67aabdSBjoern A. Zeeb 373*6d67aabdSBjoern A. Zeeb chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); 374*6d67aabdSBjoern A. Zeeb ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg); 375*6d67aabdSBjoern A. Zeeb if (ret) 376*6d67aabdSBjoern A. Zeeb return; 377*6d67aabdSBjoern A. Zeeb 378*6d67aabdSBjoern A. Zeeb cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg); 379*6d67aabdSBjoern A. Zeeb 380*6d67aabdSBjoern A. Zeeb if (tas->delta >= cfg) { 381*6d67aabdSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 382*6d67aabdSBjoern A. Zeeb "TAS delta exceed SAR limit\n"); 383*6d67aabdSBjoern A. Zeeb state = RTW89_TAS_STATE_DPR_FORBID; 384*6d67aabdSBjoern A. Zeeb goto out; 385*6d67aabdSBjoern A. Zeeb } 386*6d67aabdSBjoern A. Zeeb 387*6d67aabdSBjoern A. Zeeb dpr_on_threshold = cfg; 388*6d67aabdSBjoern A. Zeeb dpr_off_threshold = cfg - tas->dpr_gap; 389*6d67aabdSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 390*6d67aabdSBjoern A. Zeeb "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n", 391*6d67aabdSBjoern A. Zeeb dpr_on_threshold, dpr_off_threshold, txpwr_avg); 392*6d67aabdSBjoern A. Zeeb 393*6d67aabdSBjoern A. Zeeb if (txpwr_avg >= dpr_on_threshold) 394*6d67aabdSBjoern A. Zeeb state = RTW89_TAS_STATE_DPR_ON; 395*6d67aabdSBjoern A. Zeeb else if (txpwr_avg < dpr_off_threshold) 396*6d67aabdSBjoern A. Zeeb state = RTW89_TAS_STATE_DPR_OFF; 397*6d67aabdSBjoern A. Zeeb 398*6d67aabdSBjoern A. Zeeb out: 399*6d67aabdSBjoern A. Zeeb if (tas->state == state) 400*6d67aabdSBjoern A. Zeeb return; 401*6d67aabdSBjoern A. Zeeb 402*6d67aabdSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 403*6d67aabdSBjoern A. Zeeb "TAS old state: %d, new state: %d\n", tas->state, state); 404*6d67aabdSBjoern A. Zeeb tas->state = state; 405*6d67aabdSBjoern A. Zeeb rtw89_core_set_chip_txpwr(rtwdev); 406*6d67aabdSBjoern A. Zeeb } 407*6d67aabdSBjoern A. Zeeb 408*6d67aabdSBjoern A. Zeeb void rtw89_tas_init(struct rtw89_dev *rtwdev) 409*6d67aabdSBjoern A. Zeeb { 410*6d67aabdSBjoern A. Zeeb struct rtw89_tas_info *tas = &rtwdev->tas; 411*6d67aabdSBjoern A. Zeeb struct rtw89_acpi_dsm_result res = {}; 412*6d67aabdSBjoern A. Zeeb int ret; 413*6d67aabdSBjoern A. Zeeb u8 val; 414*6d67aabdSBjoern A. Zeeb 415*6d67aabdSBjoern A. Zeeb ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res); 416*6d67aabdSBjoern A. Zeeb if (ret) { 417*6d67aabdSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 418*6d67aabdSBjoern A. Zeeb "acpi: cannot get TAS: %d\n", ret); 419*6d67aabdSBjoern A. Zeeb return; 420*6d67aabdSBjoern A. Zeeb } 421*6d67aabdSBjoern A. Zeeb 422*6d67aabdSBjoern A. Zeeb val = res.u.value; 423*6d67aabdSBjoern A. Zeeb switch (val) { 424*6d67aabdSBjoern A. Zeeb case 0: 425*6d67aabdSBjoern A. Zeeb tas->enable = false; 426*6d67aabdSBjoern A. Zeeb break; 427*6d67aabdSBjoern A. Zeeb case 1: 428*6d67aabdSBjoern A. Zeeb tas->enable = true; 429*6d67aabdSBjoern A. Zeeb break; 430*6d67aabdSBjoern A. Zeeb default: 431*6d67aabdSBjoern A. Zeeb break; 432*6d67aabdSBjoern A. Zeeb } 433*6d67aabdSBjoern A. Zeeb 434*6d67aabdSBjoern A. Zeeb if (!tas->enable) { 435*6d67aabdSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, "TAS not enable\n"); 436*6d67aabdSBjoern A. Zeeb return; 437*6d67aabdSBjoern A. Zeeb } 438*6d67aabdSBjoern A. Zeeb 439*6d67aabdSBjoern A. Zeeb tas->dpr_gap = RTW89_TAS_DPR_GAP; 440*6d67aabdSBjoern A. Zeeb tas->delta = RTW89_TAS_DELTA; 441*6d67aabdSBjoern A. Zeeb } 442*6d67aabdSBjoern A. Zeeb 443*6d67aabdSBjoern A. Zeeb void rtw89_tas_reset(struct rtw89_dev *rtwdev) 444*6d67aabdSBjoern A. Zeeb { 445*6d67aabdSBjoern A. Zeeb struct rtw89_tas_info *tas = &rtwdev->tas; 446*6d67aabdSBjoern A. Zeeb 447*6d67aabdSBjoern A. Zeeb if (!tas->enable) 448*6d67aabdSBjoern A. Zeeb return; 449*6d67aabdSBjoern A. Zeeb 450*6d67aabdSBjoern A. Zeeb memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history)); 451*6d67aabdSBjoern A. Zeeb tas->total_txpwr = 0; 452*6d67aabdSBjoern A. Zeeb tas->cur_idx = 0; 453*6d67aabdSBjoern A. Zeeb tas->state = RTW89_TAS_STATE_DPR_OFF; 454*6d67aabdSBjoern A. Zeeb } 455*6d67aabdSBjoern A. Zeeb 456*6d67aabdSBjoern A. Zeeb static const struct rtw89_reg_def txpwr_regs[] = { 457*6d67aabdSBjoern A. Zeeb {R_PATH0_TXPWR, B_PATH0_TXPWR}, 458*6d67aabdSBjoern A. Zeeb {R_PATH1_TXPWR, B_PATH1_TXPWR}, 459*6d67aabdSBjoern A. Zeeb }; 460*6d67aabdSBjoern A. Zeeb 461*6d67aabdSBjoern A. Zeeb void rtw89_tas_track(struct rtw89_dev *rtwdev) 462*6d67aabdSBjoern A. Zeeb { 463*6d67aabdSBjoern A. Zeeb struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; 464*6d67aabdSBjoern A. Zeeb const enum rtw89_sar_sources src = rtwdev->sar.src; 465*6d67aabdSBjoern A. Zeeb u8 max_nss_num = rtwdev->chip->rf_path_num; 466*6d67aabdSBjoern A. Zeeb struct rtw89_tas_info *tas = &rtwdev->tas; 467*6d67aabdSBjoern A. Zeeb s16 tmp, txpwr, instant_txpwr = 0; 468*6d67aabdSBjoern A. Zeeb u32 val; 469*6d67aabdSBjoern A. Zeeb int i; 470*6d67aabdSBjoern A. Zeeb 471*6d67aabdSBjoern A. Zeeb if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) 472*6d67aabdSBjoern A. Zeeb return; 473*6d67aabdSBjoern A. Zeeb 474*6d67aabdSBjoern A. Zeeb if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM) 475*6d67aabdSBjoern A. Zeeb return; 476*6d67aabdSBjoern A. Zeeb 477*6d67aabdSBjoern A. Zeeb for (i = 0; i < max_nss_num; i++) { 478*6d67aabdSBjoern A. Zeeb val = rtw89_phy_read32_mask(rtwdev, txpwr_regs[i].addr, 479*6d67aabdSBjoern A. Zeeb txpwr_regs[i].mask); 480*6d67aabdSBjoern A. Zeeb tmp = sign_extend32(val, 8); 481*6d67aabdSBjoern A. Zeeb if (tmp <= 0) 482*6d67aabdSBjoern A. Zeeb return; 483*6d67aabdSBjoern A. Zeeb instant_txpwr += tmp; 484*6d67aabdSBjoern A. Zeeb } 485*6d67aabdSBjoern A. Zeeb 486*6d67aabdSBjoern A. Zeeb instant_txpwr /= max_nss_num; 487*6d67aabdSBjoern A. Zeeb /* in unit of 0.25 dBm multiply by percentage */ 488*6d67aabdSBjoern A. Zeeb txpwr = instant_txpwr * env->ifs_clm_tx_ratio; 489*6d67aabdSBjoern A. Zeeb tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx]; 490*6d67aabdSBjoern A. Zeeb tas->txpwr_history[tas->cur_idx] = txpwr; 491*6d67aabdSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_SAR, 492*6d67aabdSBjoern A. Zeeb "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n", 493*6d67aabdSBjoern A. Zeeb instant_txpwr, env->ifs_clm_tx_ratio, txpwr); 494*6d67aabdSBjoern A. Zeeb 495*6d67aabdSBjoern A. Zeeb tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW; 496*6d67aabdSBjoern A. Zeeb 497*6d67aabdSBjoern A. Zeeb rtw89_tas_state_update(rtwdev); 498*6d67aabdSBjoern A. Zeeb } 499