xref: /freebsd/sys/gnu/dev/bwn/phy_n/if_bwn_phy_n_ppr.c (revision 42249ef2)
1 /*
2 
3   Broadcom B43 wireless driver
4   IEEE 802.11n PHY data tables
5 
6   Copyright (c) 2008 Michael Buesch <m@bues.ch>
7   Copyright (c) 2010 Rafał Miłecki <zajec5@gmail.com>
8 
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13 
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18 
19   You should have received a copy of the GNU General Public License
20   along with this program; see the file COPYING.  If not, write to
21   the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
22   Boston, MA 02110-1301, USA.
23 
24 */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 /*
30  * The Broadcom Wireless LAN controller driver.
31  */
32 #include "opt_wlan.h"
33 #include "opt_bwn.h"
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/endian.h>
41 #include <sys/errno.h>
42 #include <sys/firmware.h>
43 #include <sys/lock.h>
44 #include <sys/mutex.h>
45 #include <machine/bus.h>
46 #include <machine/resource.h>
47 #include <sys/bus.h>
48 #include <sys/rman.h>
49 #include <sys/socket.h>
50 #include <sys/sockio.h>
51 
52 #include <net/ethernet.h>
53 #include <net/if.h>
54 #include <net/if_var.h>
55 #include <net/if_arp.h>
56 #include <net/if_dl.h>
57 #include <net/if_llc.h>
58 #include <net/if_media.h>
59 #include <net/if_types.h>
60 
61 #include <dev/pci/pcivar.h>
62 #include <dev/pci/pcireg.h>
63 
64 #include <net80211/ieee80211_var.h>
65 #include <net80211/ieee80211_radiotap.h>
66 #include <net80211/ieee80211_regdomain.h>
67 #include <net80211/ieee80211_phy.h>
68 #include <net80211/ieee80211_ratectl.h>
69 
70 #include <dev/bhnd/bhnd.h>
71 #include <dev/bhnd/cores/pmu/bhnd_pmu.h>
72 
73 #include <dev/bwn/if_bwnreg.h>
74 #include <dev/bwn/if_bwnvar.h>
75 #include <dev/bwn/if_bwn_debug.h>
76 #include <dev/bwn/if_bwn_util.h>
77 #include <dev/bwn/if_bwn_phy_common.h>
78 
79 #include <gnu/dev/bwn/phy_n/if_bwn_phy_n_regs.h>
80 #include <gnu/dev/bwn/phy_n/if_bwn_phy_n_sprom.h>
81 #include <gnu/dev/bwn/phy_n/if_bwn_phy_n_ppr.h>
82 
83 #include "bhnd_nvram_map.h"
84 
85 #define ppr_for_each_entry(ppr, i, entry)				\
86 	for (i = 0, entry = &(ppr)->__all_rates[i];			\
87 	     i < BWN_PPR_RATES_NUM;					\
88 	     i++, entry++)
89 
90 void bwn_ppr_clear(struct bwn_mac *mac, struct bwn_ppr *ppr)
91 {
92 	memset(ppr, 0, sizeof(*ppr));
93 
94 	/* Compile-time PPR check */
95 	CTASSERT(sizeof(struct bwn_ppr) == BWN_PPR_RATES_NUM * sizeof(uint8_t));
96 }
97 
98 void bwn_ppr_add(struct bwn_mac *mac, struct bwn_ppr *ppr, int diff)
99 {
100 	int i;
101 	uint8_t *rate;
102 
103 	ppr_for_each_entry(ppr, i, rate) {
104 		*rate = bwn_clamp_val(*rate + diff, 0, 127);
105 	}
106 }
107 
108 void bwn_ppr_apply_max(struct bwn_mac *mac, struct bwn_ppr *ppr, uint8_t max)
109 {
110 	int i;
111 	uint8_t *rate;
112 
113 	ppr_for_each_entry(ppr, i, rate) {
114 		*rate = min(*rate, max);
115 	}
116 }
117 
118 void bwn_ppr_apply_min(struct bwn_mac *mac, struct bwn_ppr *ppr, uint8_t min)
119 {
120 	int i;
121 	uint8_t *rate;
122 
123 	ppr_for_each_entry(ppr, i, rate) {
124 		*rate = max(*rate, min);
125 	}
126 }
127 
128 uint8_t bwn_ppr_get_max(struct bwn_mac *mac, struct bwn_ppr *ppr)
129 {
130 	uint8_t res = 0;
131 	int i;
132 	uint8_t *rate;
133 
134 	ppr_for_each_entry(ppr, i, rate) {
135 		res = max(*rate, res);
136 	}
137 
138 	return res;
139 }
140 
141 bool bwn_ppr_load_max_from_sprom(struct bwn_mac *mac, struct bwn_ppr *ppr,
142 				 bwn_phy_band_t band)
143 {
144 	struct bwn_softc *sc = mac->mac_sc;
145 	struct bwn_phy_n_core_pwr_info core_pwr_info[4];
146 	struct bwn_ppr_rates *rates = &ppr->rates;
147 	struct bwn_phy *phy = &mac->mac_phy;
148 	const char *var_ofdmgpo, *var_mcsgpo_prefix;
149 	uint8_t maxpwr, off;
150 	uint32_t sprom_ofdm_po;
151 	uint16_t sprom_mcs_po[8];
152 	uint16_t cddpo, stbcpo;
153 	uint8_t extra_cdd_po, extra_stbc_po;
154 	int error;
155 	int i;
156 
157 	for (i = 0; i < 4; i++) {
158 		bzero(&core_pwr_info[i], sizeof(core_pwr_info[i]));
159 		if (bwn_nphy_get_core_power_info(mac, i,
160 		    &core_pwr_info[i]) != 0) {
161 			BWN_ERRPRINTF(mac->mac_sc,
162 			    "%s: failed to get core_pwr_info for core %d\n",
163 			    __func__,
164 			    i);
165 		}
166 	}
167 
168 	error = bhnd_nvram_getvar_uint16(sc->sc_dev, BHND_NVAR_CDDPO, &cddpo);
169 	if (error) {
170 		BWN_ERRPRINTF(mac->mac_sc, "NVRAM variable %s unreadable: %d\n",
171 		     BHND_NVAR_CDDPO, error);
172 		return (false);
173 	}
174 
175 	error = bhnd_nvram_getvar_uint16(sc->sc_dev, BHND_NVAR_STBCPO, &stbcpo);
176 	if (error) {
177 		BWN_ERRPRINTF(mac->mac_sc, "NVRAM variable %s unreadable: %d\n",
178 		     BHND_NVAR_STBCPO, error);
179 		return (false);
180 	}
181 
182 	switch (band) {
183 	case BWN_PHY_BAND_2G:
184 		maxpwr = min(core_pwr_info[0].maxpwr_2g,
185 			     core_pwr_info[1].maxpwr_2g);
186 
187 		var_ofdmgpo = BHND_NVAR_OFDM2GPO;
188 		var_mcsgpo_prefix = "mcs2gpo";
189 		extra_cdd_po = (cddpo >> 0) & 0xf;
190 		extra_stbc_po = (stbcpo >> 0) & 0xf;
191 		break;
192 	case BWN_PHY_BAND_5G_LO:
193 		maxpwr = min(core_pwr_info[0].maxpwr_5gl,
194 			     core_pwr_info[1].maxpwr_5gl);
195 		var_ofdmgpo = BHND_NVAR_OFDM5GLPO;
196 		var_mcsgpo_prefix = "mcs5glpo";
197 		extra_cdd_po = (cddpo >> 8) & 0xf;
198 		extra_stbc_po = (stbcpo >> 8) & 0xf;
199 		break;
200 	case BWN_PHY_BAND_5G_MI:
201 		maxpwr = min(core_pwr_info[0].maxpwr_5g,
202 			     core_pwr_info[1].maxpwr_5g);
203 		var_ofdmgpo = BHND_NVAR_OFDM5GPO;
204 		var_mcsgpo_prefix = "mcs5gpo";
205 		extra_cdd_po = (cddpo >> 4) & 0xf;
206 		extra_stbc_po = (stbcpo >> 4) & 0xf;
207 		break;
208 	case BWN_PHY_BAND_5G_HI:
209 		maxpwr = min(core_pwr_info[0].maxpwr_5gh,
210 			     core_pwr_info[1].maxpwr_5gh);
211 		var_ofdmgpo = BHND_NVAR_OFDM5GHPO;
212 		var_mcsgpo_prefix = "mcs5ghpo";
213 		extra_cdd_po = (cddpo >> 12) & 0xf;
214 		extra_stbc_po = (stbcpo >> 12) & 0xf;
215 		break;
216 	default:
217 		device_printf(mac->mac_sc->sc_dev, "%s: invalid band (%d)\n",
218 		    __func__,
219 		    band);
220 		return false;
221 	}
222 
223 	error = bhnd_nvram_getvar_uint32(sc->sc_dev, var_ofdmgpo,
224 	    &sprom_ofdm_po);
225 	if (error) {
226 		device_printf(sc->sc_dev, "NVRAM variable %s unreadable: %d\n",
227 		     var_ofdmgpo, error);
228 		return (false);
229 	}
230 
231 	for (size_t i = 0; i < nitems(sprom_mcs_po); i++) {
232 		char	var[strlen(var_mcsgpo_prefix) + sizeof("XX")];
233 		int	ret;
234 
235 		/* mcs[25]g[lh]?po[0-9] */
236 		ret = snprintf(var, sizeof(var), "%s%zu", var_mcsgpo_prefix, i);
237 		if (ret >= sizeof(var)) {
238 			device_printf(sc->sc_dev, "buffer too small for "
239 			    "%s%zu\n", var_mcsgpo_prefix, i);
240 			return (false);
241 		}
242 
243 		error = bhnd_nvram_getvar_uint16(sc->sc_dev, var,
244 		    &sprom_mcs_po[i]);
245 		if (error) {
246 			device_printf(sc->sc_dev, "NVRAM variable %s "
247 			    "unreadable: %d\n", var, error);
248 			return (false);
249 		}
250 	}
251 
252 	if (band == BWN_BAND_2G) {
253 		uint16_t ck2gpo;
254 
255 		error = bhnd_nvram_getvar_uint16(sc->sc_dev, BHND_NVAR_CCK2GPO,
256 		    &ck2gpo);
257 		if (error) {
258 			device_printf(sc->sc_dev, "NVRAM variable %s "
259 			    "unreadable: %d\n", BHND_NVAR_CCK2GPO, error);
260 			return (false);
261 		}
262 
263 		for (i = 0; i < 4; i++) {
264 			off = ((ck2gpo >> (i * 4)) & 0xf) * 2;
265 			rates->cck[i] = maxpwr - off;
266 		}
267 	}
268 
269 	/* OFDM */
270 	for (i = 0; i < 8; i++) {
271 		off = ((sprom_ofdm_po >> (i * 4)) & 0xf) * 2;
272 		rates->ofdm[i] = maxpwr - off;
273 	}
274 
275 	/* MCS 20 SISO */
276 	rates->mcs_20[0] = rates->ofdm[0];
277 	rates->mcs_20[1] = rates->ofdm[2];
278 	rates->mcs_20[2] = rates->ofdm[3];
279 	rates->mcs_20[3] = rates->ofdm[4];
280 	rates->mcs_20[4] = rates->ofdm[5];
281 	rates->mcs_20[5] = rates->ofdm[6];
282 	rates->mcs_20[6] = rates->ofdm[7];
283 	rates->mcs_20[7] = rates->ofdm[7];
284 
285 	/* MCS 20 CDD */
286 	for (i = 0; i < 4; i++) {
287 		off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2;
288 		rates->mcs_20_cdd[i] = maxpwr - off;
289 		if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3)
290 			rates->mcs_20_cdd[i] -= extra_cdd_po;
291 	}
292 	for (i = 0; i < 4; i++) {
293 		off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2;
294 		rates->mcs_20_cdd[4 + i] = maxpwr - off;
295 		if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3)
296 			rates->mcs_20_cdd[4 + i] -= extra_cdd_po;
297 	}
298 
299 	/* OFDM 20 CDD */
300 	rates->ofdm_20_cdd[0] = rates->mcs_20_cdd[0];
301 	rates->ofdm_20_cdd[1] = rates->mcs_20_cdd[0];
302 	rates->ofdm_20_cdd[2] = rates->mcs_20_cdd[1];
303 	rates->ofdm_20_cdd[3] = rates->mcs_20_cdd[2];
304 	rates->ofdm_20_cdd[4] = rates->mcs_20_cdd[3];
305 	rates->ofdm_20_cdd[5] = rates->mcs_20_cdd[4];
306 	rates->ofdm_20_cdd[6] = rates->mcs_20_cdd[5];
307 	rates->ofdm_20_cdd[7] = rates->mcs_20_cdd[6];
308 
309 	/* MCS 20 STBC */
310 	for (i = 0; i < 4; i++) {
311 		off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2;
312 		rates->mcs_20_stbc[i] = maxpwr - off;
313 		if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3)
314 			rates->mcs_20_stbc[i] -= extra_stbc_po;
315 	}
316 	for (i = 0; i < 4; i++) {
317 		off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2;
318 		rates->mcs_20_stbc[4 + i] = maxpwr - off;
319 		if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3)
320 			rates->mcs_20_stbc[4 + i] -= extra_stbc_po;
321 	}
322 
323 	/* MCS 20 SDM */
324 	for (i = 0; i < 4; i++) {
325 		off = ((sprom_mcs_po[2] >> (i * 4)) & 0xf) * 2;
326 		rates->mcs_20_sdm[i] = maxpwr - off;
327 	}
328 	for (i = 0; i < 4; i++) {
329 		off = ((sprom_mcs_po[3] >> (i * 4)) & 0xf) * 2;
330 		rates->mcs_20_sdm[4 + i] = maxpwr - off;
331 	}
332 
333 	return true;
334 }
335