1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Broadcom B43 wireless driver |
4 | * PPR (Power Per Rate) management |
5 | * |
6 | * Copyright (c) 2014 Rafał Miłecki <zajec5@gmail.com> |
7 | */ |
8 | |
9 | #include "ppr.h" |
10 | #include "b43.h" |
11 | |
12 | #define ppr_for_each_entry(ppr, i, entry) \ |
13 | for (i = 0, entry = &(ppr)->__all_rates[i]; \ |
14 | i < B43_PPR_RATES_NUM; \ |
15 | i++, entry++) |
16 | |
17 | void b43_ppr_clear(struct b43_wldev *dev, struct b43_ppr *ppr) |
18 | { |
19 | memset(ppr, 0, sizeof(*ppr)); |
20 | |
21 | /* Compile-time PPR check */ |
22 | BUILD_BUG_ON(sizeof(struct b43_ppr) != B43_PPR_RATES_NUM * sizeof(u8)); |
23 | } |
24 | |
25 | void b43_ppr_add(struct b43_wldev *dev, struct b43_ppr *ppr, int diff) |
26 | { |
27 | int i; |
28 | u8 *rate; |
29 | |
30 | ppr_for_each_entry(ppr, i, rate) { |
31 | *rate = clamp_val(*rate + diff, 0, 127); |
32 | } |
33 | } |
34 | |
35 | void b43_ppr_apply_max(struct b43_wldev *dev, struct b43_ppr *ppr, u8 max) |
36 | { |
37 | int i; |
38 | u8 *rate; |
39 | |
40 | ppr_for_each_entry(ppr, i, rate) { |
41 | *rate = min(*rate, max); |
42 | } |
43 | } |
44 | |
45 | void b43_ppr_apply_min(struct b43_wldev *dev, struct b43_ppr *ppr, u8 min) |
46 | { |
47 | int i; |
48 | u8 *rate; |
49 | |
50 | ppr_for_each_entry(ppr, i, rate) { |
51 | *rate = max(*rate, min); |
52 | } |
53 | } |
54 | |
55 | u8 b43_ppr_get_max(struct b43_wldev *dev, struct b43_ppr *ppr) |
56 | { |
57 | u8 res = 0; |
58 | int i; |
59 | u8 *rate; |
60 | |
61 | ppr_for_each_entry(ppr, i, rate) { |
62 | res = max(*rate, res); |
63 | } |
64 | |
65 | return res; |
66 | } |
67 | |
68 | bool b43_ppr_load_max_from_sprom(struct b43_wldev *dev, struct b43_ppr *ppr, |
69 | enum b43_band band) |
70 | { |
71 | struct b43_ppr_rates *rates = &ppr->rates; |
72 | struct ssb_sprom *sprom = dev->dev->bus_sprom; |
73 | struct b43_phy *phy = &dev->phy; |
74 | u8 maxpwr, off; |
75 | u32 sprom_ofdm_po; |
76 | u16 *sprom_mcs_po; |
77 | u8 , ; |
78 | int i; |
79 | |
80 | switch (band) { |
81 | case B43_BAND_2G: |
82 | maxpwr = min(sprom->core_pwr_info[0].maxpwr_2g, |
83 | sprom->core_pwr_info[1].maxpwr_2g); |
84 | sprom_ofdm_po = sprom->ofdm2gpo; |
85 | sprom_mcs_po = sprom->mcs2gpo; |
86 | extra_cdd_po = (sprom->cddpo >> 0) & 0xf; |
87 | extra_stbc_po = (sprom->stbcpo >> 0) & 0xf; |
88 | break; |
89 | case B43_BAND_5G_LO: |
90 | maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gl, |
91 | sprom->core_pwr_info[1].maxpwr_5gl); |
92 | sprom_ofdm_po = sprom->ofdm5glpo; |
93 | sprom_mcs_po = sprom->mcs5glpo; |
94 | extra_cdd_po = (sprom->cddpo >> 8) & 0xf; |
95 | extra_stbc_po = (sprom->stbcpo >> 8) & 0xf; |
96 | break; |
97 | case B43_BAND_5G_MI: |
98 | maxpwr = min(sprom->core_pwr_info[0].maxpwr_5g, |
99 | sprom->core_pwr_info[1].maxpwr_5g); |
100 | sprom_ofdm_po = sprom->ofdm5gpo; |
101 | sprom_mcs_po = sprom->mcs5gpo; |
102 | extra_cdd_po = (sprom->cddpo >> 4) & 0xf; |
103 | extra_stbc_po = (sprom->stbcpo >> 4) & 0xf; |
104 | break; |
105 | case B43_BAND_5G_HI: |
106 | maxpwr = min(sprom->core_pwr_info[0].maxpwr_5gh, |
107 | sprom->core_pwr_info[1].maxpwr_5gh); |
108 | sprom_ofdm_po = sprom->ofdm5ghpo; |
109 | sprom_mcs_po = sprom->mcs5ghpo; |
110 | extra_cdd_po = (sprom->cddpo >> 12) & 0xf; |
111 | extra_stbc_po = (sprom->stbcpo >> 12) & 0xf; |
112 | break; |
113 | default: |
114 | WARN_ON_ONCE(1); |
115 | return false; |
116 | } |
117 | |
118 | if (band == B43_BAND_2G) { |
119 | for (i = 0; i < 4; i++) { |
120 | off = ((sprom->cck2gpo >> (i * 4)) & 0xf) * 2; |
121 | rates->cck[i] = maxpwr - off; |
122 | } |
123 | } |
124 | |
125 | /* OFDM */ |
126 | for (i = 0; i < 8; i++) { |
127 | off = ((sprom_ofdm_po >> (i * 4)) & 0xf) * 2; |
128 | rates->ofdm[i] = maxpwr - off; |
129 | } |
130 | |
131 | /* MCS 20 SISO */ |
132 | rates->mcs_20[0] = rates->ofdm[0]; |
133 | rates->mcs_20[1] = rates->ofdm[2]; |
134 | rates->mcs_20[2] = rates->ofdm[3]; |
135 | rates->mcs_20[3] = rates->ofdm[4]; |
136 | rates->mcs_20[4] = rates->ofdm[5]; |
137 | rates->mcs_20[5] = rates->ofdm[6]; |
138 | rates->mcs_20[6] = rates->ofdm[7]; |
139 | rates->mcs_20[7] = rates->ofdm[7]; |
140 | |
141 | /* MCS 20 CDD */ |
142 | for (i = 0; i < 4; i++) { |
143 | off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; |
144 | rates->mcs_20_cdd[i] = maxpwr - off; |
145 | if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) |
146 | rates->mcs_20_cdd[i] -= extra_cdd_po; |
147 | } |
148 | for (i = 0; i < 4; i++) { |
149 | off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; |
150 | rates->mcs_20_cdd[4 + i] = maxpwr - off; |
151 | if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) |
152 | rates->mcs_20_cdd[4 + i] -= extra_cdd_po; |
153 | } |
154 | |
155 | /* OFDM 20 CDD */ |
156 | rates->ofdm_20_cdd[0] = rates->mcs_20_cdd[0]; |
157 | rates->ofdm_20_cdd[1] = rates->mcs_20_cdd[0]; |
158 | rates->ofdm_20_cdd[2] = rates->mcs_20_cdd[1]; |
159 | rates->ofdm_20_cdd[3] = rates->mcs_20_cdd[2]; |
160 | rates->ofdm_20_cdd[4] = rates->mcs_20_cdd[3]; |
161 | rates->ofdm_20_cdd[5] = rates->mcs_20_cdd[4]; |
162 | rates->ofdm_20_cdd[6] = rates->mcs_20_cdd[5]; |
163 | rates->ofdm_20_cdd[7] = rates->mcs_20_cdd[6]; |
164 | |
165 | /* MCS 20 STBC */ |
166 | for (i = 0; i < 4; i++) { |
167 | off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; |
168 | rates->mcs_20_stbc[i] = maxpwr - off; |
169 | if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) |
170 | rates->mcs_20_stbc[i] -= extra_stbc_po; |
171 | } |
172 | for (i = 0; i < 4; i++) { |
173 | off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; |
174 | rates->mcs_20_stbc[4 + i] = maxpwr - off; |
175 | if (phy->type == B43_PHYTYPE_N && phy->rev >= 3) |
176 | rates->mcs_20_stbc[4 + i] -= extra_stbc_po; |
177 | } |
178 | |
179 | /* MCS 20 SDM */ |
180 | for (i = 0; i < 4; i++) { |
181 | off = ((sprom_mcs_po[2] >> (i * 4)) & 0xf) * 2; |
182 | rates->mcs_20_sdm[i] = maxpwr - off; |
183 | } |
184 | for (i = 0; i < 4; i++) { |
185 | off = ((sprom_mcs_po[3] >> (i * 4)) & 0xf) * 2; |
186 | rates->mcs_20_sdm[4 + i] = maxpwr - off; |
187 | } |
188 | |
189 | return true; |
190 | } |
191 | |