1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /* Copyright(c) 2018-2021 Realtek Corporation
3 */
4
5 #include "sar.h"
6 #include "phy.h"
7 #include "debug.h"
8
rtw_query_sar(struct rtw_dev * rtwdev,const struct rtw_sar_arg * arg)9 s8 rtw_query_sar(struct rtw_dev *rtwdev, const struct rtw_sar_arg *arg)
10 {
11 const struct rtw_hal *hal = &rtwdev->hal;
12 const struct rtw_sar *sar = &hal->sar;
13
14 switch (sar->src) {
15 default:
16 rtw_warn(rtwdev, "unknown SAR source: %d\n", sar->src);
17 fallthrough;
18 case RTW_SAR_SOURCE_NONE:
19 return (s8)rtwdev->chip->max_power_index;
20 case RTW_SAR_SOURCE_COMMON:
21 return sar->cfg[arg->path][arg->rs].common[arg->sar_band];
22 }
23 }
24
rtw_apply_sar(struct rtw_dev * rtwdev,const struct rtw_sar * new)25 static int rtw_apply_sar(struct rtw_dev *rtwdev, const struct rtw_sar *new)
26 {
27 struct rtw_hal *hal = &rtwdev->hal;
28 struct rtw_sar *sar = &hal->sar;
29
30 if (sar->src != RTW_SAR_SOURCE_NONE && new->src != sar->src) {
31 rtw_warn(rtwdev, "SAR source: %d is in use\n", sar->src);
32 return -EBUSY;
33 }
34
35 *sar = *new;
36 rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
37
38 return 0;
39 }
40
rtw_sar_to_phy(struct rtw_dev * rtwdev,u8 fct,s32 sar,const struct rtw_sar_arg * arg)41 static s8 rtw_sar_to_phy(struct rtw_dev *rtwdev, u8 fct, s32 sar,
42 const struct rtw_sar_arg *arg)
43 {
44 struct rtw_hal *hal = &rtwdev->hal;
45 u8 txgi = rtwdev->chip->txgi_factor;
46 u8 max = rtwdev->chip->max_power_index;
47 s32 tmp;
48 s8 base;
49
50 tmp = fct > txgi ? sar >> (fct - txgi) : sar << (txgi - fct);
51 base = arg->sar_band == RTW_SAR_BAND_0 ?
52 hal->tx_pwr_by_rate_base_2g[arg->path][arg->rs] :
53 hal->tx_pwr_by_rate_base_5g[arg->path][arg->rs];
54
55 return (s8)clamp_t(s32, tmp, -max - 1, max) - base;
56 }
57
58 static const struct cfg80211_sar_freq_ranges rtw_common_sar_freq_ranges[] = {
59 [RTW_SAR_BAND_0] = { .start_freq = 2412, .end_freq = 2484, },
60 [RTW_SAR_BAND_1] = { .start_freq = 5180, .end_freq = 5320, },
61 [RTW_SAR_BAND_3] = { .start_freq = 5500, .end_freq = 5720, },
62 [RTW_SAR_BAND_4] = { .start_freq = 5745, .end_freq = 5825, },
63 };
64
65 #if defined(__linux__)
66 static_assert(ARRAY_SIZE(rtw_common_sar_freq_ranges) == RTW_SAR_BAND_NR);
67 #elif defined(__FreeBSD__)
68 rtw88_static_assert(ARRAY_SIZE(rtw_common_sar_freq_ranges) == RTW_SAR_BAND_NR);
69 #endif
70
71 const struct cfg80211_sar_capa rtw_sar_capa = {
72 .type = NL80211_SAR_TYPE_POWER,
73 .num_freq_ranges = RTW_SAR_BAND_NR,
74 .freq_ranges = rtw_common_sar_freq_ranges,
75 };
76
rtw_set_sar_specs(struct rtw_dev * rtwdev,const struct cfg80211_sar_specs * sar)77 int rtw_set_sar_specs(struct rtw_dev *rtwdev,
78 const struct cfg80211_sar_specs *sar)
79 {
80 struct rtw_sar_arg arg = {0};
81 struct rtw_sar new = {0};
82 u32 idx, i, j, k;
83 s32 power;
84 s8 val;
85
86 if (sar->type != NL80211_SAR_TYPE_POWER)
87 return -EINVAL;
88
89 memset(&new, rtwdev->chip->max_power_index, sizeof(new));
90 new.src = RTW_SAR_SOURCE_COMMON;
91
92 for (i = 0; i < sar->num_sub_specs; i++) {
93 idx = sar->sub_specs[i].freq_range_index;
94 if (idx >= RTW_SAR_BAND_NR)
95 return -EINVAL;
96
97 power = sar->sub_specs[i].power;
98 rtw_dbg(rtwdev, RTW_DBG_REGD, "On freq %u to %u, set SAR %d in 1/%lu dBm\n",
99 rtw_common_sar_freq_ranges[idx].start_freq,
100 rtw_common_sar_freq_ranges[idx].end_freq,
101 power, BIT(RTW_COMMON_SAR_FCT));
102
103 for (j = 0; j < RTW_RF_PATH_MAX; j++) {
104 for (k = 0; k < RTW_RATE_SECTION_NUM; k++) {
105 arg = (struct rtw_sar_arg){
106 .sar_band = idx,
107 .path = j,
108 .rs = k,
109 };
110 val = rtw_sar_to_phy(rtwdev, RTW_COMMON_SAR_FCT,
111 power, &arg);
112 new.cfg[j][k].common[idx] = val;
113 }
114 }
115 }
116
117 return rtw_apply_sar(rtwdev, &new);
118 }
119