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 | |
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 | |
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, channel: hal->current_channel); |
37 | |
38 | return 0; |
39 | } |
40 | |
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 | static_assert(ARRAY_SIZE(rtw_common_sar_freq_ranges) == RTW_SAR_BAND_NR); |
66 | |
67 | const struct cfg80211_sar_capa rtw_sar_capa = { |
68 | .type = NL80211_SAR_TYPE_POWER, |
69 | .num_freq_ranges = RTW_SAR_BAND_NR, |
70 | .freq_ranges = rtw_common_sar_freq_ranges, |
71 | }; |
72 | |
73 | int rtw_set_sar_specs(struct rtw_dev *rtwdev, |
74 | const struct cfg80211_sar_specs *sar) |
75 | { |
76 | struct rtw_sar_arg arg = {0}; |
77 | struct rtw_sar new = {0}; |
78 | u32 idx, i, j, k; |
79 | s32 power; |
80 | s8 val; |
81 | |
82 | if (sar->type != NL80211_SAR_TYPE_POWER) |
83 | return -EINVAL; |
84 | |
85 | memset(&new, rtwdev->chip->max_power_index, sizeof(new)); |
86 | new.src = RTW_SAR_SOURCE_COMMON; |
87 | |
88 | for (i = 0; i < sar->num_sub_specs; i++) { |
89 | idx = sar->sub_specs[i].freq_range_index; |
90 | if (idx >= RTW_SAR_BAND_NR) |
91 | return -EINVAL; |
92 | |
93 | power = sar->sub_specs[i].power; |
94 | rtw_dbg(rtwdev, mask: RTW_DBG_REGD, fmt: "On freq %u to %u, set SAR %d in 1/%lu dBm\n" , |
95 | rtw_common_sar_freq_ranges[idx].start_freq, |
96 | rtw_common_sar_freq_ranges[idx].end_freq, |
97 | power, BIT(RTW_COMMON_SAR_FCT)); |
98 | |
99 | for (j = 0; j < RTW_RF_PATH_MAX; j++) { |
100 | for (k = 0; k < RTW_RATE_SECTION_MAX; k++) { |
101 | arg = (struct rtw_sar_arg){ |
102 | .sar_band = idx, |
103 | .path = j, |
104 | .rs = k, |
105 | }; |
106 | val = rtw_sar_to_phy(rtwdev, RTW_COMMON_SAR_FCT, |
107 | sar: power, arg: &arg); |
108 | new.cfg[j][k].common[idx] = val; |
109 | } |
110 | } |
111 | } |
112 | |
113 | return rtw_apply_sar(rtwdev, new: &new); |
114 | } |
115 | |