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 #include <dev/siba/siba_ids.h> 64 #include <dev/siba/sibareg.h> 65 #include <dev/siba/sibavar.h> 66 67 #include <net80211/ieee80211_var.h> 68 #include <net80211/ieee80211_radiotap.h> 69 #include <net80211/ieee80211_regdomain.h> 70 #include <net80211/ieee80211_phy.h> 71 #include <net80211/ieee80211_ratectl.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_ppr.h> 81 82 #define ppr_for_each_entry(ppr, i, entry) \ 83 for (i = 0, entry = &(ppr)->__all_rates[i]; \ 84 i < BWN_PPR_RATES_NUM; \ 85 i++, entry++) 86 87 void bwn_ppr_clear(struct bwn_mac *mac, struct bwn_ppr *ppr) 88 { 89 memset(ppr, 0, sizeof(*ppr)); 90 91 /* Compile-time PPR check */ 92 CTASSERT(sizeof(struct bwn_ppr) == BWN_PPR_RATES_NUM * sizeof(uint8_t)); 93 } 94 95 void bwn_ppr_add(struct bwn_mac *mac, struct bwn_ppr *ppr, int diff) 96 { 97 int i; 98 uint8_t *rate; 99 100 ppr_for_each_entry(ppr, i, rate) { 101 *rate = bwn_clamp_val(*rate + diff, 0, 127); 102 } 103 } 104 105 void bwn_ppr_apply_max(struct bwn_mac *mac, struct bwn_ppr *ppr, uint8_t max) 106 { 107 int i; 108 uint8_t *rate; 109 110 ppr_for_each_entry(ppr, i, rate) { 111 *rate = min(*rate, max); 112 } 113 } 114 115 void bwn_ppr_apply_min(struct bwn_mac *mac, struct bwn_ppr *ppr, uint8_t min) 116 { 117 int i; 118 uint8_t *rate; 119 120 ppr_for_each_entry(ppr, i, rate) { 121 *rate = max(*rate, min); 122 } 123 } 124 125 uint8_t bwn_ppr_get_max(struct bwn_mac *mac, struct bwn_ppr *ppr) 126 { 127 uint8_t res = 0; 128 int i; 129 uint8_t *rate; 130 131 ppr_for_each_entry(ppr, i, rate) { 132 res = max(*rate, res); 133 } 134 135 return res; 136 } 137 138 bool bwn_ppr_load_max_from_sprom(struct bwn_mac *mac, struct bwn_ppr *ppr, 139 bwn_phy_band_t band) 140 { 141 struct bwn_softc *sc = mac->mac_sc; 142 struct siba_sprom_core_pwr_info core_pwr_info[4]; 143 struct bwn_ppr_rates *rates = &ppr->rates; 144 struct bwn_phy *phy = &mac->mac_phy; 145 uint8_t maxpwr, off; 146 uint32_t sprom_ofdm_po; 147 uint16_t sprom_mcs_po[8]; 148 uint8_t extra_cdd_po, extra_stbc_po; 149 int i; 150 151 for (i = 0; i < 4; i++) { 152 bzero(&core_pwr_info[i], sizeof(core_pwr_info[i])); 153 if (siba_sprom_get_core_power_info(sc->sc_dev, i, 154 &core_pwr_info[i]) != 0) { 155 BWN_ERRPRINTF(mac->mac_sc, 156 "%s: failed to get core_pwr_info for core %d\n", 157 __func__, 158 i); 159 } 160 } 161 162 switch (band) { 163 case BWN_PHY_BAND_2G: 164 maxpwr = min(core_pwr_info[0].maxpwr_2g, 165 core_pwr_info[1].maxpwr_2g); 166 sprom_ofdm_po = siba_sprom_get_ofdm2gpo(sc->sc_dev); 167 siba_sprom_get_mcs2gpo(sc->sc_dev, sprom_mcs_po); 168 extra_cdd_po = (siba_sprom_get_cddpo(sc->sc_dev) >> 0) & 0xf; 169 extra_stbc_po = (siba_sprom_get_stbcpo(sc->sc_dev) >> 0) & 0xf; 170 break; 171 case BWN_PHY_BAND_5G_LO: 172 maxpwr = min(core_pwr_info[0].maxpwr_5gl, 173 core_pwr_info[1].maxpwr_5gl); 174 sprom_ofdm_po = siba_sprom_get_ofdm5glpo(sc->sc_dev); 175 siba_sprom_get_mcs5glpo(sc->sc_dev, sprom_mcs_po); 176 extra_cdd_po = (siba_sprom_get_cddpo(sc->sc_dev) >> 8) & 0xf; 177 extra_stbc_po = (siba_sprom_get_stbcpo(sc->sc_dev) >> 8) & 0xf; 178 break; 179 case BWN_PHY_BAND_5G_MI: 180 maxpwr = min(core_pwr_info[0].maxpwr_5g, 181 core_pwr_info[1].maxpwr_5g); 182 sprom_ofdm_po = siba_sprom_get_ofdm5gpo(sc->sc_dev); 183 siba_sprom_get_mcs5gpo(sc->sc_dev, sprom_mcs_po); 184 extra_cdd_po = (siba_sprom_get_cddpo(sc->sc_dev) >> 4) & 0xf; 185 extra_stbc_po = (siba_sprom_get_stbcpo(sc->sc_dev) >> 4) & 0xf; 186 break; 187 case BWN_PHY_BAND_5G_HI: 188 maxpwr = min(core_pwr_info[0].maxpwr_5gh, 189 core_pwr_info[1].maxpwr_5gh); 190 sprom_ofdm_po = siba_sprom_get_ofdm5ghpo(sc->sc_dev); 191 siba_sprom_get_mcs5ghpo(sc->sc_dev, sprom_mcs_po); 192 extra_cdd_po = (siba_sprom_get_cddpo(sc->sc_dev) >> 12) & 0xf; 193 extra_stbc_po = (siba_sprom_get_stbcpo(sc->sc_dev) >> 12) & 0xf; 194 break; 195 default: 196 device_printf(mac->mac_sc->sc_dev, "%s: invalid band (%d)\n", 197 __func__, 198 band); 199 return false; 200 } 201 202 if (band == BWN_BAND_2G) { 203 for (i = 0; i < 4; i++) { 204 off = ((siba_sprom_get_cck2gpo(sc->sc_dev) >> (i * 4)) & 0xf) * 2; 205 rates->cck[i] = maxpwr - off; 206 } 207 } 208 209 /* OFDM */ 210 for (i = 0; i < 8; i++) { 211 off = ((sprom_ofdm_po >> (i * 4)) & 0xf) * 2; 212 rates->ofdm[i] = maxpwr - off; 213 } 214 215 /* MCS 20 SISO */ 216 rates->mcs_20[0] = rates->ofdm[0]; 217 rates->mcs_20[1] = rates->ofdm[2]; 218 rates->mcs_20[2] = rates->ofdm[3]; 219 rates->mcs_20[3] = rates->ofdm[4]; 220 rates->mcs_20[4] = rates->ofdm[5]; 221 rates->mcs_20[5] = rates->ofdm[6]; 222 rates->mcs_20[6] = rates->ofdm[7]; 223 rates->mcs_20[7] = rates->ofdm[7]; 224 225 /* MCS 20 CDD */ 226 for (i = 0; i < 4; i++) { 227 off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; 228 rates->mcs_20_cdd[i] = maxpwr - off; 229 if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3) 230 rates->mcs_20_cdd[i] -= extra_cdd_po; 231 } 232 for (i = 0; i < 4; i++) { 233 off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; 234 rates->mcs_20_cdd[4 + i] = maxpwr - off; 235 if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3) 236 rates->mcs_20_cdd[4 + i] -= extra_cdd_po; 237 } 238 239 /* OFDM 20 CDD */ 240 rates->ofdm_20_cdd[0] = rates->mcs_20_cdd[0]; 241 rates->ofdm_20_cdd[1] = rates->mcs_20_cdd[0]; 242 rates->ofdm_20_cdd[2] = rates->mcs_20_cdd[1]; 243 rates->ofdm_20_cdd[3] = rates->mcs_20_cdd[2]; 244 rates->ofdm_20_cdd[4] = rates->mcs_20_cdd[3]; 245 rates->ofdm_20_cdd[5] = rates->mcs_20_cdd[4]; 246 rates->ofdm_20_cdd[6] = rates->mcs_20_cdd[5]; 247 rates->ofdm_20_cdd[7] = rates->mcs_20_cdd[6]; 248 249 /* MCS 20 STBC */ 250 for (i = 0; i < 4; i++) { 251 off = ((sprom_mcs_po[0] >> (i * 4)) & 0xf) * 2; 252 rates->mcs_20_stbc[i] = maxpwr - off; 253 if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3) 254 rates->mcs_20_stbc[i] -= extra_stbc_po; 255 } 256 for (i = 0; i < 4; i++) { 257 off = ((sprom_mcs_po[1] >> (i * 4)) & 0xf) * 2; 258 rates->mcs_20_stbc[4 + i] = maxpwr - off; 259 if (phy->type == BWN_PHYTYPE_N && phy->rev >= 3) 260 rates->mcs_20_stbc[4 + i] -= extra_stbc_po; 261 } 262 263 /* MCS 20 SDM */ 264 for (i = 0; i < 4; i++) { 265 off = ((sprom_mcs_po[2] >> (i * 4)) & 0xf) * 2; 266 rates->mcs_20_sdm[i] = maxpwr - off; 267 } 268 for (i = 0; i < 4; i++) { 269 off = ((sprom_mcs_po[3] >> (i * 4)) & 0xf) * 2; 270 rates->mcs_20_sdm[4 + i] = maxpwr - off; 271 } 272 273 return true; 274 } 275