| 1 | /* $NetBSD: arn9380.c,v 1.3 2014/01/22 17:29:29 matt Exp $ */ |
| 2 | /* $OpenBSD: ar9380.c,v 1.17 2012/10/20 09:54:20 stsp Exp $ */ |
| 3 | |
| 4 | /*- |
| 5 | * Copyright (c) 2011 Damien Bergamini <damien.bergamini@free.fr> |
| 6 | * Copyright (c) 2010 Atheros Communications Inc. |
| 7 | * |
| 8 | * Permission to use, copy, modify, and/or distribute this software for any |
| 9 | * purpose with or without fee is hereby granted, provided that the above |
| 10 | * copyright notice and this permission notice appear in all copies. |
| 11 | * |
| 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 19 | */ |
| 20 | |
| 21 | /* |
| 22 | * Driver for Atheros 802.11a/g/n chipsets. |
| 23 | * Routines for AR9380 and AR9485 chipsets. |
| 24 | */ |
| 25 | |
| 26 | #include <sys/cdefs.h> |
| 27 | __KERNEL_RCSID(0, "$NetBSD: arn9380.c,v 1.3 2014/01/22 17:29:29 matt Exp $" ); |
| 28 | |
| 29 | #include <sys/param.h> |
| 30 | #include <sys/sockio.h> |
| 31 | #include <sys/mbuf.h> |
| 32 | #include <sys/kernel.h> |
| 33 | #include <sys/socket.h> |
| 34 | #include <sys/systm.h> |
| 35 | #include <sys/malloc.h> |
| 36 | #include <sys/queue.h> |
| 37 | #include <sys/conf.h> |
| 38 | #include <sys/device.h> |
| 39 | |
| 40 | #include <sys/bus.h> |
| 41 | #include <sys/endian.h> |
| 42 | |
| 43 | #include <net/bpf.h> |
| 44 | #include <net/if.h> |
| 45 | #include <net/if_arp.h> |
| 46 | #include <net/if_dl.h> |
| 47 | #include <net/if_ether.h> |
| 48 | #include <net/if_media.h> |
| 49 | #include <net/if_types.h> |
| 50 | |
| 51 | #include <netinet/in.h> |
| 52 | #include <netinet/in_systm.h> |
| 53 | #include <netinet/in_var.h> |
| 54 | |
| 55 | #include <net80211/ieee80211_var.h> |
| 56 | #include <net80211/ieee80211_amrr.h> |
| 57 | #include <net80211/ieee80211_radiotap.h> |
| 58 | |
| 59 | #include <dev/ic/athnreg.h> |
| 60 | #include <dev/ic/athnvar.h> |
| 61 | |
| 62 | #include <dev/ic/arn9003reg.h> |
| 63 | #include <dev/ic/arn9380reg.h> |
| 64 | |
| 65 | #include <dev/ic/arn9003.h> |
| 66 | #include <dev/ic/arn9380.h> |
| 67 | |
| 68 | #define Static static |
| 69 | |
| 70 | Static void ar9380_get_correction(struct athn_softc *, |
| 71 | struct ieee80211_channel *, int, int *, int *); |
| 72 | Static void ar9380_get_paprd_masks(struct athn_softc *, |
| 73 | struct ieee80211_channel *, uint32_t *, uint32_t *); |
| 74 | Static const uint8_t * |
| 75 | ar9380_get_rom_template(struct athn_softc *, uint8_t); |
| 76 | Static void ar9380_init_from_rom(struct athn_softc *, |
| 77 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 78 | Static void ar9380_set_correction(struct athn_softc *, |
| 79 | struct ieee80211_channel *); |
| 80 | Static int ar9380_set_synth(struct athn_softc *, |
| 81 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 82 | Static void ar9380_set_txpower(struct athn_softc *, |
| 83 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 84 | Static void ar9380_setup(struct athn_softc *); |
| 85 | Static void ar9380_spur_mitigate(struct athn_softc *, |
| 86 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 87 | Static void ar9380_spur_mitigate_cck(struct athn_softc *, |
| 88 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 89 | Static void ar9380_spur_mitigate_ofdm(struct athn_softc *, |
| 90 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 91 | Static void ar9380_swap_rom(struct athn_softc *); |
| 92 | |
| 93 | Static void ar9485_init_swreg(struct athn_softc *); |
| 94 | #define ar9485_pmu_read AR_READ |
| 95 | Static int ar9485_pmu_write(struct athn_softc *, uint32_t, uint32_t); |
| 96 | |
| 97 | #ifdef notused |
| 98 | Static void ar9380_init_swreg(struct athn_softc *); |
| 99 | #endif /* notused */ |
| 100 | |
| 101 | PUBLIC int |
| 102 | ar9380_attach(struct athn_softc *sc) |
| 103 | { |
| 104 | |
| 105 | sc->sc_ngpiopins = 17; |
| 106 | sc->sc_ops.setup = ar9380_setup; |
| 107 | sc->sc_ops.get_rom_template = ar9380_get_rom_template; |
| 108 | sc->sc_ops.swap_rom = ar9380_swap_rom; |
| 109 | sc->sc_ops.init_from_rom = ar9380_init_from_rom; |
| 110 | sc->sc_ops.set_txpower = ar9380_set_txpower; |
| 111 | sc->sc_ops.set_synth = ar9380_set_synth; |
| 112 | sc->sc_ops.spur_mitigate = ar9380_spur_mitigate; |
| 113 | sc->sc_ops.get_paprd_masks = ar9380_get_paprd_masks; |
| 114 | sc->sc_cca_min_2g = AR9380_PHY_CCA_MIN_GOOD_VAL_2GHZ; |
| 115 | sc->sc_cca_max_2g = AR9380_PHY_CCA_MAX_GOOD_VAL_2GHZ; |
| 116 | sc->sc_cca_min_5g = AR9380_PHY_CCA_MIN_GOOD_VAL_5GHZ; |
| 117 | sc->sc_cca_max_5g = AR9380_PHY_CCA_MAX_GOOD_VAL_5GHZ; |
| 118 | if (AR_SREV_9485(sc)) { |
| 119 | sc->sc_ini = &ar9485_1_1_ini; |
| 120 | sc->sc_serdes = &ar9485_1_1_serdes; |
| 121 | } |
| 122 | else { |
| 123 | sc->sc_ini = &ar9380_2_2_ini; |
| 124 | sc->sc_serdes = &ar9380_2_2_serdes; |
| 125 | } |
| 126 | |
| 127 | return ar9003_attach(sc); |
| 128 | } |
| 129 | |
| 130 | Static void |
| 131 | ar9380_setup(struct athn_softc *sc) |
| 132 | { |
| 133 | struct ieee80211com *ic = &sc->sc_ic; |
| 134 | struct ar9380_eeprom *eep = sc->sc_eep; |
| 135 | struct ar9380_base_eep_hdr *base = &eep->baseEepHeader; |
| 136 | uint8_t type; |
| 137 | |
| 138 | if (base->opFlags & AR_OPFLAGS_11A) |
| 139 | sc->sc_flags |= ATHN_FLAG_11A; |
| 140 | if (base->opFlags & AR_OPFLAGS_11G) |
| 141 | sc->sc_flags |= ATHN_FLAG_11G; |
| 142 | if (base->opFlags & AR_OPFLAGS_11N) |
| 143 | sc->sc_flags |= ATHN_FLAG_11N; |
| 144 | |
| 145 | IEEE80211_ADDR_COPY(ic->ic_myaddr, eep->macAddr); |
| 146 | sc->sc_led_pin = base->wlanLedGpio; |
| 147 | |
| 148 | /* Check if we have a hardware radio switch. */ |
| 149 | if (base->rfSilent & AR_EEP_RFSILENT_ENABLED) { |
| 150 | sc->sc_flags |= ATHN_FLAG_RFSILENT; |
| 151 | /* Get GPIO pin used by hardware radio switch. */ |
| 152 | sc->sc_rfsilent_pin = MS(base->rfSilent, |
| 153 | AR_EEP_RFSILENT_GPIO_SEL); |
| 154 | /* Get polarity of hardware radio switch. */ |
| 155 | if (base->rfSilent & AR_EEP_RFSILENT_POLARITY) |
| 156 | sc->sc_flags |= ATHN_FLAG_RFSILENT_REVERSED; |
| 157 | } |
| 158 | |
| 159 | /* Set the number of HW key cache entries. */ |
| 160 | sc->sc_kc_entries = AR_KEYTABLE_SIZE; |
| 161 | |
| 162 | sc->sc_txchainmask = MS(base->txrxMask, AR_EEP_TX_MASK); |
| 163 | sc->sc_rxchainmask = MS(base->txrxMask, AR_EEP_RX_MASK); |
| 164 | |
| 165 | /* Fast PLL clock is always supported. */ |
| 166 | sc->sc_flags |= ATHN_FLAG_FAST_PLL_CLOCK; |
| 167 | |
| 168 | /* Enable PA predistortion if supported. */ |
| 169 | if (base->featureEnable & AR_EEP_PAPRD) |
| 170 | sc->sc_flags |= ATHN_FLAG_PAPRD; |
| 171 | /* |
| 172 | * Some 3-stream chips may exceed the PCIe power requirements, |
| 173 | * requiring to reduce the number of Tx chains in some cases. |
| 174 | */ |
| 175 | if ((base->miscConfiguration & AR_EEP_CHAIN_MASK_REDUCE) && |
| 176 | sc->sc_txchainmask == 0x7) |
| 177 | sc->sc_flags |= ATHN_FLAG_3TREDUCE_CHAIN; |
| 178 | |
| 179 | /* Select initialization values based on ROM. */ |
| 180 | type = MS(eep->baseEepHeader.txrxgain, AR_EEP_RX_GAIN); |
| 181 | if (!AR_SREV_9485(sc)) { |
| 182 | if (type == AR_EEP_RX_GAIN_WO_XLNA) |
| 183 | sc->sc_rx_gain = &ar9380_2_2_rx_gain_wo_xlna; |
| 184 | else |
| 185 | sc->sc_rx_gain = &ar9380_2_2_rx_gain; |
| 186 | } |
| 187 | else |
| 188 | sc->sc_rx_gain = &ar9485_1_1_rx_gain; |
| 189 | |
| 190 | /* Select initialization values based on ROM. */ |
| 191 | type = MS(eep->baseEepHeader.txrxgain, AR_EEP_TX_GAIN); |
| 192 | if (!AR_SREV_9485(sc)) { |
| 193 | if (type == AR_EEP_TX_GAIN_HIGH_OB_DB) |
| 194 | sc->sc_tx_gain = &ar9380_2_2_tx_gain_high_ob_db; |
| 195 | else if (type == AR_EEP_TX_GAIN_LOW_OB_DB) |
| 196 | sc->sc_tx_gain = &ar9380_2_2_tx_gain_low_ob_db; |
| 197 | else if (type == AR_EEP_TX_GAIN_HIGH_POWER) |
| 198 | sc->sc_tx_gain = &ar9380_2_2_tx_gain_high_power; |
| 199 | else |
| 200 | sc->sc_tx_gain = &ar9380_2_2_tx_gain; |
| 201 | } |
| 202 | else |
| 203 | sc->sc_tx_gain = &ar9485_1_1_tx_gain; |
| 204 | } |
| 205 | |
| 206 | Static const uint8_t * |
| 207 | ar9380_get_rom_template(struct athn_softc *sc, uint8_t ref) |
| 208 | { |
| 209 | size_t i; |
| 210 | |
| 211 | /* Retrieve template ROM image for given reference. */ |
| 212 | for (i = 0; i < __arraycount(ar9380_rom_templates); i++) |
| 213 | if (ar9380_rom_templates[i][1] == ref) |
| 214 | return ar9380_rom_templates[i]; |
| 215 | return NULL; |
| 216 | } |
| 217 | |
| 218 | Static void |
| 219 | ar9380_swap_rom(struct athn_softc *sc) |
| 220 | { |
| 221 | #if BYTE_ORDER == BIG_ENDIAN |
| 222 | struct ar9380_eeprom *eep = sc->sc_eep; |
| 223 | struct ar9380_base_eep_hdr *base = &eep->baseEepHeader; |
| 224 | struct ar9380_modal_eep_header *modal; |
| 225 | int i; |
| 226 | |
| 227 | base->regDmn[0] = bswap16(base->regDmn[0]); |
| 228 | base->regDmn[1] = bswap16(base->regDmn[1]); |
| 229 | base->swreg = bswap32(base->swreg); |
| 230 | |
| 231 | modal = &eep->modalHeader2G; |
| 232 | modal->antCtrlCommon = bswap32(modal->antCtrlCommon); |
| 233 | modal->antCtrlCommon2 = bswap32(modal->antCtrlCommon2); |
| 234 | modal->papdRateMaskHt20 = bswap32(modal->papdRateMaskHt20); |
| 235 | modal->papdRateMaskHt40 = bswap32(modal->papdRateMaskHt40); |
| 236 | for (i = 0; i < AR9380_MAX_CHAINS; i++) |
| 237 | modal->antCtrlChain[i] = bswap16(modal->antCtrlChain[i]); |
| 238 | |
| 239 | modal = &eep->modalHeader5G; |
| 240 | modal->antCtrlCommon = bswap32(modal->antCtrlCommon); |
| 241 | modal->antCtrlCommon2 = bswap32(modal->antCtrlCommon2); |
| 242 | modal->papdRateMaskHt20 = bswap32(modal->papdRateMaskHt20); |
| 243 | modal->papdRateMaskHt40 = bswap32(modal->papdRateMaskHt40); |
| 244 | for (i = 0; i < AR9380_MAX_CHAINS; i++) |
| 245 | modal->antCtrlChain[i] = bswap16(modal->antCtrlChain[i]); |
| 246 | #endif |
| 247 | } |
| 248 | |
| 249 | Static void |
| 250 | ar9380_get_paprd_masks(struct athn_softc *sc, struct ieee80211_channel *c, |
| 251 | uint32_t *ht20mask, uint32_t *ht40mask) |
| 252 | { |
| 253 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 254 | const struct ar9380_modal_eep_header *modal; |
| 255 | |
| 256 | if (IEEE80211_IS_CHAN_2GHZ(c)) |
| 257 | modal = &eep->modalHeader2G; |
| 258 | else |
| 259 | modal = &eep->modalHeader5G; |
| 260 | *ht20mask = modal->papdRateMaskHt20; |
| 261 | *ht40mask = modal->papdRateMaskHt40; |
| 262 | } |
| 263 | |
| 264 | Static int |
| 265 | ar9380_set_synth(struct athn_softc *sc, struct ieee80211_channel *c, |
| 266 | struct ieee80211_channel *extc) |
| 267 | { |
| 268 | uint32_t freq = c->ic_freq; |
| 269 | uint32_t chansel, phy; |
| 270 | |
| 271 | if (IEEE80211_IS_CHAN_2GHZ(c)) { |
| 272 | if (AR_SREV_9485(sc)) |
| 273 | chansel = ((freq << 16) - 215) / 15; |
| 274 | else |
| 275 | chansel = (freq << 16) / 15; |
| 276 | AR_WRITE(sc, AR_PHY_SYNTH_CONTROL, AR9380_BMODE); |
| 277 | } |
| 278 | else { |
| 279 | chansel = (freq << 15) / 15; |
| 280 | chansel >>= 1; |
| 281 | AR_WRITE(sc, AR_PHY_SYNTH_CONTROL, 0); |
| 282 | } |
| 283 | |
| 284 | /* Enable Long Shift Select for synthesizer. */ |
| 285 | AR_SETBITS(sc, AR_PHY_65NM_CH0_SYNTH4, |
| 286 | AR_PHY_SYNTH4_LONG_SHIFT_SELECT); |
| 287 | AR_WRITE_BARRIER(sc); |
| 288 | |
| 289 | /* Program synthesizer. */ |
| 290 | phy = (chansel << 2) | AR9380_FRACMODE; |
| 291 | DPRINTFN(DBG_RF, sc, "AR_PHY_65NM_CH0_SYNTH7=0x%08x\n" , phy); |
| 292 | AR_WRITE(sc, AR_PHY_65NM_CH0_SYNTH7, phy); |
| 293 | AR_WRITE_BARRIER(sc); |
| 294 | /* Toggle Load Synth Channel bit. */ |
| 295 | AR_WRITE(sc, AR_PHY_65NM_CH0_SYNTH7, phy | AR9380_LOAD_SYNTH); |
| 296 | AR_WRITE_BARRIER(sc); |
| 297 | return 0; |
| 298 | } |
| 299 | |
| 300 | Static void |
| 301 | ar9380_init_from_rom(struct athn_softc *sc, struct ieee80211_channel *c, |
| 302 | struct ieee80211_channel *extc) |
| 303 | { |
| 304 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 305 | const struct ar9380_modal_eep_header *modal; |
| 306 | uint8_t db, margin, ant_div_ctrl; |
| 307 | uint32_t reg; |
| 308 | int i, maxchains; |
| 309 | |
| 310 | if (IEEE80211_IS_CHAN_2GHZ(c)) |
| 311 | modal = &eep->modalHeader2G; |
| 312 | else |
| 313 | modal = &eep->modalHeader5G; |
| 314 | |
| 315 | /* Apply XPA bias level. */ |
| 316 | if (AR_SREV_9485(sc)) { |
| 317 | reg = AR_READ(sc, AR9485_PHY_65NM_CH0_TOP2); |
| 318 | reg = RW(reg, AR9485_PHY_65NM_CH0_TOP2_XPABIASLVL, |
| 319 | modal->xpaBiasLvl); |
| 320 | AR_WRITE(sc, AR9485_PHY_65NM_CH0_TOP2, reg); |
| 321 | } |
| 322 | else { |
| 323 | reg = AR_READ(sc, AR_PHY_65NM_CH0_TOP); |
| 324 | reg = RW(reg, AR_PHY_65NM_CH0_TOP_XPABIASLVL, |
| 325 | modal->xpaBiasLvl & 0x3); |
| 326 | AR_WRITE(sc, AR_PHY_65NM_CH0_TOP, reg); |
| 327 | reg = AR_READ(sc, AR_PHY_65NM_CH0_THERM); |
| 328 | reg = RW(reg, AR_PHY_65NM_CH0_THERM_XPABIASLVL_MSB, |
| 329 | modal->xpaBiasLvl >> 2); |
| 330 | reg |= AR_PHY_65NM_CH0_THERM_XPASHORT2GND; |
| 331 | AR_WRITE(sc, AR_PHY_65NM_CH0_THERM, reg); |
| 332 | } |
| 333 | |
| 334 | /* Apply antenna control. */ |
| 335 | reg = AR_READ(sc, AR_PHY_SWITCH_COM); |
| 336 | reg = RW(reg, AR_SWITCH_TABLE_COM_ALL, modal->antCtrlCommon); |
| 337 | AR_WRITE(sc, AR_PHY_SWITCH_COM, reg); |
| 338 | reg = AR_READ(sc, AR_PHY_SWITCH_COM_2); |
| 339 | reg = RW(reg, AR_SWITCH_TABLE_COM_2_ALL, modal->antCtrlCommon2); |
| 340 | AR_WRITE(sc, AR_PHY_SWITCH_COM_2, reg); |
| 341 | |
| 342 | maxchains = AR_SREV_9485(sc) ? 1 : AR9380_MAX_CHAINS; |
| 343 | for (i = 0; i < maxchains; i++) { |
| 344 | reg = AR_READ(sc, AR_PHY_SWITCH_CHAIN(i)); |
| 345 | reg = RW(reg, AR_SWITCH_TABLE_ALL, modal->antCtrlChain[i]); |
| 346 | AR_WRITE(sc, AR_PHY_SWITCH_CHAIN(i), reg); |
| 347 | } |
| 348 | |
| 349 | if (AR_SREV_9485(sc)) { |
| 350 | ant_div_ctrl = eep->base_ext1.ant_div_control; |
| 351 | reg = AR_READ(sc, AR_PHY_MC_GAIN_CTRL); |
| 352 | reg = RW(reg, AR_PHY_MC_GAIN_CTRL_ANT_DIV_CTRL_ALL, |
| 353 | MS(ant_div_ctrl, AR_EEP_ANT_DIV_CTRL_ALL)); |
| 354 | if (ant_div_ctrl & AR_EEP_ANT_DIV_CTRL_ANT_DIV) |
| 355 | reg |= AR_PHY_MC_GAIN_CTRL_ENABLE_ANT_DIV; |
| 356 | else |
| 357 | reg &= ~AR_PHY_MC_GAIN_CTRL_ENABLE_ANT_DIV; |
| 358 | AR_WRITE(sc, AR_PHY_MC_GAIN_CTRL, reg); |
| 359 | reg = AR_READ(sc, AR_PHY_CCK_DETECT); |
| 360 | if (ant_div_ctrl & AR_EEP_ANT_DIV_CTRL_FAST_DIV) |
| 361 | reg |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; |
| 362 | else |
| 363 | reg &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; |
| 364 | AR_WRITE(sc, AR_PHY_CCK_DETECT, reg); |
| 365 | } |
| 366 | |
| 367 | if (eep->baseEepHeader.miscConfiguration & AR_EEP_DRIVE_STRENGTH) { |
| 368 | /* Apply drive strength. */ |
| 369 | reg = AR_READ(sc, AR_PHY_65NM_CH0_BIAS1); |
| 370 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS1_0, 5); |
| 371 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS1_1, 5); |
| 372 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS1_2, 5); |
| 373 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS1_3, 5); |
| 374 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS1_4, 5); |
| 375 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS1_5, 5); |
| 376 | AR_WRITE(sc, AR_PHY_65NM_CH0_BIAS1, reg); |
| 377 | |
| 378 | reg = AR_READ(sc, AR_PHY_65NM_CH0_BIAS2); |
| 379 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_0, 5); |
| 380 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_1, 5); |
| 381 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_2, 5); |
| 382 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_3, 5); |
| 383 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_4, 5); |
| 384 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_5, 5); |
| 385 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_6, 5); |
| 386 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_7, 5); |
| 387 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS2_8, 5); |
| 388 | AR_WRITE(sc, AR_PHY_65NM_CH0_BIAS2, reg); |
| 389 | |
| 390 | reg = AR_READ(sc, AR_PHY_65NM_CH0_BIAS4); |
| 391 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS4_0, 5); |
| 392 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS4_1, 5); |
| 393 | reg = RW(reg, AR_PHY_65NM_CH0_BIAS4_2, 5); |
| 394 | AR_WRITE(sc, AR_PHY_65NM_CH0_BIAS4, reg); |
| 395 | } |
| 396 | |
| 397 | /* Apply attenuation settings. */ |
| 398 | maxchains = AR_SREV_9485(sc) ? 1 : AR9380_MAX_CHAINS; |
| 399 | for (i = 0; i < maxchains; i++) { |
| 400 | if (IEEE80211_IS_CHAN_5GHZ(c) && |
| 401 | eep->base_ext2.xatten1DBLow[i] != 0) { |
| 402 | if (c->ic_freq <= 5500) { |
| 403 | db = athn_interpolate(c->ic_freq, |
| 404 | 5180, eep->base_ext2.xatten1DBLow[i], |
| 405 | 5500, modal->xatten1DB[i]); |
| 406 | } |
| 407 | else { |
| 408 | db = athn_interpolate(c->ic_freq, |
| 409 | 5500, modal->xatten1DB[i], |
| 410 | 5785, eep->base_ext2.xatten1DBHigh[i]); |
| 411 | } |
| 412 | } |
| 413 | else |
| 414 | db = modal->xatten1DB[i]; |
| 415 | if (IEEE80211_IS_CHAN_5GHZ(c) && |
| 416 | eep->base_ext2.xatten1MarginLow[i] != 0) { |
| 417 | if (c->ic_freq <= 5500) { |
| 418 | margin = athn_interpolate(c->ic_freq, |
| 419 | 5180, eep->base_ext2.xatten1MarginLow[i], |
| 420 | 5500, modal->xatten1Margin[i]); |
| 421 | } |
| 422 | else { |
| 423 | margin = athn_interpolate(c->ic_freq, |
| 424 | 5500, modal->xatten1Margin[i], |
| 425 | 5785, eep->base_ext2.xatten1MarginHigh[i]); |
| 426 | } |
| 427 | } |
| 428 | else |
| 429 | margin = modal->xatten1Margin[i]; |
| 430 | reg = AR_READ(sc, AR_PHY_EXT_ATTEN_CTL(i)); |
| 431 | reg = RW(reg, AR_PHY_EXT_ATTEN_CTL_XATTEN1_DB, db); |
| 432 | reg = RW(reg, AR_PHY_EXT_ATTEN_CTL_XATTEN1_MARGIN, margin); |
| 433 | AR_WRITE(sc, AR_PHY_EXT_ATTEN_CTL(i), reg); |
| 434 | } |
| 435 | |
| 436 | /* Initialize switching regulator. */ |
| 437 | if (AR_SREV_9485(sc)) |
| 438 | ar9485_init_swreg(sc); |
| 439 | else |
| 440 | ar9485_init_swreg(sc); |
| 441 | |
| 442 | /* Apply tuning capabilities. */ |
| 443 | if (AR_SREV_9485(sc) && |
| 444 | (eep->baseEepHeader.featureEnable & AR_EEP_TUNING_CAPS)) { |
| 445 | reg = AR_READ(sc, AR9485_PHY_CH0_XTAL); |
| 446 | reg = RW(reg, AR9485_PHY_CH0_XTAL_CAPINDAC, |
| 447 | eep->baseEepHeader.params_for_tuning_caps[0]); |
| 448 | reg = RW(reg, AR9485_PHY_CH0_XTAL_CAPOUTDAC, |
| 449 | eep->baseEepHeader.params_for_tuning_caps[0]); |
| 450 | AR_WRITE(sc, AR9485_PHY_CH0_XTAL, reg); |
| 451 | } |
| 452 | AR_WRITE_BARRIER(sc); |
| 453 | } |
| 454 | |
| 455 | #ifdef notused |
| 456 | Static void |
| 457 | ar9380_init_swreg(struct athn_softc *sc) |
| 458 | { |
| 459 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 460 | |
| 461 | if (eep->baseEepHeader.featureEnable & AR_EEP_INTERNAL_REGULATOR) { |
| 462 | /* Internal regulator is ON. */ |
| 463 | AR_CLRBITS(sc, AR_RTC_REG_CONTROL1, |
| 464 | AR_RTC_REG_CONTROL1_SWREG_PROGRAM); |
| 465 | AR_WRITE(sc, AR_RTC_REG_CONTROL0, eep->baseEepHeader.swreg); |
| 466 | AR_SETBITS(sc, AR_RTC_REG_CONTROL1, |
| 467 | AR_RTC_REG_CONTROL1_SWREG_PROGRAM); |
| 468 | } |
| 469 | else |
| 470 | AR_SETBITS(sc, AR_RTC_SLEEP_CLK, AR_RTC_FORCE_SWREG_PRD); |
| 471 | AR_WRITE_BARRIER(sc); |
| 472 | } |
| 473 | #endif /* notused */ |
| 474 | |
| 475 | Static int |
| 476 | ar9485_pmu_write(struct athn_softc *sc, uint32_t addr, uint32_t val) |
| 477 | { |
| 478 | int ntries; |
| 479 | |
| 480 | AR_WRITE(sc, addr, val); |
| 481 | /* Wait for write to complete. */ |
| 482 | for (ntries = 0; ntries < 100; ntries++) { |
| 483 | if (AR_READ(sc, addr) == val) |
| 484 | return 0; |
| 485 | AR_WRITE(sc, addr, val); /* Insist. */ |
| 486 | AR_WRITE_BARRIER(sc); |
| 487 | DELAY(10); |
| 488 | } |
| 489 | return ETIMEDOUT; |
| 490 | } |
| 491 | |
| 492 | Static void |
| 493 | ar9485_init_swreg(struct athn_softc *sc) |
| 494 | { |
| 495 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 496 | uint32_t reg; |
| 497 | |
| 498 | ar9485_pmu_write(sc, AR_PHY_PMU2, |
| 499 | ar9485_pmu_read(sc, AR_PHY_PMU2) & ~AR_PHY_PMU2_PGM); |
| 500 | |
| 501 | if (eep->baseEepHeader.featureEnable & AR_EEP_INTERNAL_REGULATOR) { |
| 502 | ar9485_pmu_write(sc, AR_PHY_PMU1, 0x131dc17a); |
| 503 | |
| 504 | reg = ar9485_pmu_read(sc, AR_PHY_PMU2); |
| 505 | reg = (reg & ~0xffc00000) | 0x10000000; |
| 506 | ar9485_pmu_write(sc, AR_PHY_PMU2, reg); |
| 507 | } |
| 508 | else { |
| 509 | ar9485_pmu_write(sc, AR_PHY_PMU1, |
| 510 | ar9485_pmu_read(sc, AR_PHY_PMU1) | AR_PHY_PMU1_PWD); |
| 511 | } |
| 512 | |
| 513 | ar9485_pmu_write(sc, AR_PHY_PMU2, |
| 514 | ar9485_pmu_read(sc, AR_PHY_PMU2) | AR_PHY_PMU2_PGM); |
| 515 | } |
| 516 | |
| 517 | /* |
| 518 | * NB: It is safe to call this function for 5GHz channels. |
| 519 | */ |
| 520 | Static void |
| 521 | ar9380_spur_mitigate_cck(struct athn_softc *sc, struct ieee80211_channel *c, |
| 522 | struct ieee80211_channel *extc) |
| 523 | { |
| 524 | static const int16_t freqs[] = { 2420, 2440, 2464, 2480 }; |
| 525 | size_t i; |
| 526 | int spur, freq; |
| 527 | uint32_t reg; |
| 528 | |
| 529 | for (i = 0; i < __arraycount(freqs); i++) { |
| 530 | spur = freqs[i] - c->ic_freq; |
| 531 | if (abs(spur) < 10) /* +/- 10MHz range. */ |
| 532 | break; |
| 533 | } |
| 534 | if (i == __arraycount(freqs)) { |
| 535 | /* Disable CCK spur mitigation. */ |
| 536 | reg = AR_READ(sc, AR_PHY_AGC_CONTROL); |
| 537 | reg = RW(reg, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x5); |
| 538 | AR_WRITE(sc, AR_PHY_AGC_CONTROL, reg); |
| 539 | reg = AR_READ(sc, AR_PHY_CCK_SPUR_MIT); |
| 540 | reg = RW(reg, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, 0); |
| 541 | reg &= ~AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT; |
| 542 | AR_WRITE(sc, AR_PHY_CCK_SPUR_MIT, reg); |
| 543 | AR_WRITE_BARRIER(sc); |
| 544 | return; |
| 545 | } |
| 546 | freq = (spur * 524288) / 11; |
| 547 | |
| 548 | reg = AR_READ(sc, AR_PHY_AGC_CONTROL); |
| 549 | reg = RW(reg, AR_PHY_AGC_CONTROL_YCOK_MAX, 0x7); |
| 550 | AR_WRITE(sc, AR_PHY_AGC_CONTROL, reg); |
| 551 | |
| 552 | reg = AR_READ(sc, AR_PHY_CCK_SPUR_MIT); |
| 553 | reg = RW(reg, AR_PHY_CCK_SPUR_MIT_CCK_SPUR_FREQ, freq); |
| 554 | reg = RW(reg, AR_PHY_CCK_SPUR_MIT_SPUR_RSSI_THR, 0x7f); |
| 555 | reg = RW(reg, AR_PHY_CCK_SPUR_MIT_SPUR_FILTER_TYPE, 0x2); |
| 556 | reg |= AR_PHY_CCK_SPUR_MIT_USE_CCK_SPUR_MIT; |
| 557 | AR_WRITE(sc, AR_PHY_CCK_SPUR_MIT, reg); |
| 558 | AR_WRITE_BARRIER(sc); |
| 559 | } |
| 560 | |
| 561 | Static void |
| 562 | ar9380_spur_mitigate_ofdm(struct athn_softc *sc, struct ieee80211_channel *c, |
| 563 | struct ieee80211_channel *extc) |
| 564 | { |
| 565 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 566 | const uint8_t *spurchans; |
| 567 | uint32_t reg; |
| 568 | int idx, spur_delta_phase, spur_off, range, i; |
| 569 | int freq, spur, spur_freq_sd, spur_subchannel_sd; |
| 570 | |
| 571 | if (IEEE80211_IS_CHAN_2GHZ(c)) |
| 572 | spurchans = eep->modalHeader2G.spurChans; |
| 573 | else |
| 574 | spurchans = eep->modalHeader5G.spurChans; |
| 575 | if (spurchans[0] == 0) |
| 576 | return; |
| 577 | |
| 578 | /* Disable OFDM spur mitigation. */ |
| 579 | AR_CLRBITS(sc, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER); |
| 580 | |
| 581 | reg = AR_READ(sc, AR_PHY_TIMING11); |
| 582 | reg = RW(reg, AR_PHY_TIMING11_SPUR_FREQ_SD, 0); |
| 583 | reg = RW(reg, AR_PHY_TIMING11_SPUR_DELTA_PHASE, 0); |
| 584 | reg &= ~AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC; |
| 585 | reg &= ~AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR; |
| 586 | AR_WRITE(sc, AR_PHY_TIMING11, reg); |
| 587 | |
| 588 | AR_CLRBITS(sc, AR_PHY_SFCORR_EXT, |
| 589 | AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD); |
| 590 | |
| 591 | AR_CLRBITS(sc, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI); |
| 592 | |
| 593 | reg = AR_READ(sc, AR_PHY_SPUR_REG); |
| 594 | reg = RW(reg, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0); |
| 595 | reg &= ~AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI; |
| 596 | reg &= ~AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT; |
| 597 | reg &= ~AR_PHY_SPUR_REG_ENABLE_MASK_PPM; |
| 598 | AR_WRITE(sc, AR_PHY_SPUR_REG, reg); |
| 599 | AR_WRITE_BARRIER(sc); |
| 600 | |
| 601 | freq = c->ic_freq; |
| 602 | #ifndef IEEE80211_NO_HT |
| 603 | if (extc != NULL) { |
| 604 | range = 19; /* +/- 19MHz range. */ |
| 605 | if (AR_READ(sc, AR_PHY_GEN_CTRL) & AR_PHY_GC_DYN2040_PRI_CH) |
| 606 | freq += 10; |
| 607 | else |
| 608 | freq -= 10; |
| 609 | } |
| 610 | else |
| 611 | #endif |
| 612 | range = 10; /* +/- 10MHz range. */ |
| 613 | for (i = 0; i < AR9380_EEPROM_MODAL_SPURS; i++) { |
| 614 | spur = spurchans[i]; |
| 615 | if (spur == 0) |
| 616 | return; |
| 617 | /* Convert to frequency. */ |
| 618 | if (IEEE80211_IS_CHAN_2GHZ(c)) |
| 619 | spur = 2300 + spur; |
| 620 | else |
| 621 | spur = 4900 + (spur * 5); |
| 622 | spur -= freq; |
| 623 | if (abs(spur) < range) |
| 624 | break; |
| 625 | } |
| 626 | if (i == AR9380_EEPROM_MODAL_SPURS) |
| 627 | return; |
| 628 | |
| 629 | /* Enable OFDM spur mitigation. */ |
| 630 | #ifndef IEEE80211_NO_HT |
| 631 | if (extc != NULL) { |
| 632 | spur_delta_phase = (spur * 131072) / 5; |
| 633 | reg = AR_READ(sc, AR_PHY_GEN_CTRL); |
| 634 | if (spur < 0) { |
| 635 | spur_subchannel_sd = |
| 636 | (reg & AR_PHY_GC_DYN2040_PRI_CH) == 0; |
| 637 | spur_off = spur + 10; |
| 638 | } |
| 639 | else { |
| 640 | spur_subchannel_sd = |
| 641 | (reg & AR_PHY_GC_DYN2040_PRI_CH) != 0; |
| 642 | spur_off = spur - 10; |
| 643 | } |
| 644 | } |
| 645 | else |
| 646 | #endif |
| 647 | { |
| 648 | spur_delta_phase = (spur * 262144) / 5; |
| 649 | spur_subchannel_sd = 0; |
| 650 | spur_off = spur; |
| 651 | } |
| 652 | spur_freq_sd = (spur_off * 512) / 11; |
| 653 | |
| 654 | AR_SETBITS(sc, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_FILTER); |
| 655 | |
| 656 | reg = AR_READ(sc, AR_PHY_TIMING11); |
| 657 | reg = RW(reg, AR_PHY_TIMING11_SPUR_FREQ_SD, spur_freq_sd); |
| 658 | reg = RW(reg, AR_PHY_TIMING11_SPUR_DELTA_PHASE, spur_delta_phase); |
| 659 | reg |= AR_PHY_TIMING11_USE_SPUR_FILTER_IN_AGC; |
| 660 | reg |= AR_PHY_TIMING11_USE_SPUR_FILTER_IN_SELFCOR; |
| 661 | AR_WRITE(sc, AR_PHY_TIMING11, reg); |
| 662 | |
| 663 | reg = AR_READ(sc, AR_PHY_SFCORR_EXT); |
| 664 | if (spur_subchannel_sd) |
| 665 | reg |= AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD; |
| 666 | else |
| 667 | reg &= ~AR_PHY_SFCORR_EXT_SPUR_SUBCHANNEL_SD; |
| 668 | AR_WRITE(sc, AR_PHY_SFCORR_EXT, reg); |
| 669 | |
| 670 | AR_SETBITS(sc, AR_PHY_TIMING4, AR_PHY_TIMING4_ENABLE_SPUR_RSSI); |
| 671 | |
| 672 | reg = AR_READ(sc, AR_PHY_SPUR_REG); |
| 673 | reg = RW(reg, AR_PHY_SPUR_REG_MASK_RATE_CNTL, 0xff); |
| 674 | reg = RW(reg, AR_PHY_SPUR_REG_SPUR_RSSI_THRESH, 34); |
| 675 | reg |= AR_PHY_SPUR_REG_EN_VIT_SPUR_RSSI; |
| 676 | if (AR_READ(sc, AR_PHY_MODE) & AR_PHY_MODE_DYNAMIC) |
| 677 | reg |= AR_PHY_SPUR_REG_ENABLE_NF_RSSI_SPUR_MIT; |
| 678 | reg |= AR_PHY_SPUR_REG_ENABLE_MASK_PPM; |
| 679 | AR_WRITE(sc, AR_PHY_SPUR_REG, reg); |
| 680 | |
| 681 | idx = (spur * 16) / 5; |
| 682 | if (idx < 0) |
| 683 | idx--; |
| 684 | |
| 685 | /* Write pilot mask. */ |
| 686 | AR_SETBITS(sc, AR_PHY_TIMING4, |
| 687 | AR_PHY_TIMING4_ENABLE_PILOT_MASK | |
| 688 | AR_PHY_TIMING4_ENABLE_CHAN_MASK); |
| 689 | |
| 690 | reg = AR_READ(sc, AR_PHY_PILOT_SPUR_MASK); |
| 691 | reg = RW(reg, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_IDX_A, idx); |
| 692 | reg = RW(reg, AR_PHY_PILOT_SPUR_MASK_CF_PILOT_MASK_A, 0x0c); |
| 693 | AR_WRITE(sc, AR_PHY_PILOT_SPUR_MASK, reg); |
| 694 | |
| 695 | reg = AR_READ(sc, AR_PHY_SPUR_MASK_A); |
| 696 | reg = RW(reg, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A, idx); |
| 697 | reg = RW(reg, AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A, 0xa0); |
| 698 | AR_WRITE(sc, AR_PHY_SPUR_MASK_A, reg); |
| 699 | |
| 700 | reg = AR_READ(sc, AR_PHY_CHAN_SPUR_MASK); |
| 701 | reg = RW(reg, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_IDX_A, idx); |
| 702 | reg = RW(reg, AR_PHY_CHAN_SPUR_MASK_CF_CHAN_MASK_A, 0x0c); |
| 703 | AR_WRITE(sc, AR_PHY_CHAN_SPUR_MASK, reg); |
| 704 | AR_WRITE_BARRIER(sc); |
| 705 | } |
| 706 | |
| 707 | Static void |
| 708 | ar9380_spur_mitigate(struct athn_softc *sc, struct ieee80211_channel *c, |
| 709 | struct ieee80211_channel *extc) |
| 710 | { |
| 711 | |
| 712 | /* NB: We call spur_mitigate_cck for 5GHz too, just to disable it. */ |
| 713 | ar9380_spur_mitigate_cck(sc, c, extc); |
| 714 | ar9380_spur_mitigate_ofdm(sc, c, extc); |
| 715 | } |
| 716 | |
| 717 | Static void |
| 718 | ar9380_set_txpower(struct athn_softc *sc, struct ieee80211_channel *c, |
| 719 | struct ieee80211_channel *extc) |
| 720 | { |
| 721 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 722 | uint8_t tpow_cck[4], tpow_ofdm[4]; |
| 723 | uint8_t tpow_ht20[14], tpow_ht40[14]; |
| 724 | int16_t power[ATHN_POWER_COUNT]; |
| 725 | |
| 726 | if (IEEE80211_IS_CHAN_2GHZ(c)) { |
| 727 | /* Get CCK target powers. */ |
| 728 | ar9003_get_lg_tpow(sc, c, AR_CTL_11B, |
| 729 | eep->calTargetFbinCck, eep->calTargetPowerCck, |
| 730 | AR9380_NUM_2G_CCK_TARGET_POWERS, tpow_cck); |
| 731 | |
| 732 | /* Get OFDM target powers. */ |
| 733 | ar9003_get_lg_tpow(sc, c, AR_CTL_11G, |
| 734 | eep->calTargetFbin2G, eep->calTargetPower2G, |
| 735 | AR9380_NUM_2G_20_TARGET_POWERS, tpow_ofdm); |
| 736 | |
| 737 | /* Get HT-20 target powers. */ |
| 738 | ar9003_get_ht_tpow(sc, c, AR_CTL_2GHT20, |
| 739 | eep->calTargetFbin2GHT20, eep->calTargetPower2GHT20, |
| 740 | AR9380_NUM_2G_20_TARGET_POWERS, tpow_ht20); |
| 741 | |
| 742 | if (extc != NULL) { |
| 743 | /* Get HT-40 target powers. */ |
| 744 | ar9003_get_ht_tpow(sc, c, AR_CTL_2GHT40, |
| 745 | eep->calTargetFbin2GHT40, |
| 746 | eep->calTargetPower2GHT40, |
| 747 | AR9380_NUM_2G_40_TARGET_POWERS, tpow_ht40); |
| 748 | } |
| 749 | } |
| 750 | else { |
| 751 | /* Get OFDM target powers. */ |
| 752 | ar9003_get_lg_tpow(sc, c, AR_CTL_11A, |
| 753 | eep->calTargetFbin5G, eep->calTargetPower5G, |
| 754 | AR9380_NUM_5G_20_TARGET_POWERS, tpow_ofdm); |
| 755 | |
| 756 | /* Get HT-20 target powers. */ |
| 757 | ar9003_get_ht_tpow(sc, c, AR_CTL_5GHT20, |
| 758 | eep->calTargetFbin5GHT20, eep->calTargetPower5GHT20, |
| 759 | AR9380_NUM_5G_20_TARGET_POWERS, tpow_ht20); |
| 760 | |
| 761 | if (extc != NULL) { |
| 762 | /* Get HT-40 target powers. */ |
| 763 | ar9003_get_ht_tpow(sc, c, AR_CTL_5GHT40, |
| 764 | eep->calTargetFbin5GHT40, |
| 765 | eep->calTargetPower5GHT40, |
| 766 | AR9380_NUM_5G_40_TARGET_POWERS, tpow_ht40); |
| 767 | } |
| 768 | } |
| 769 | |
| 770 | memset(power, 0, sizeof(power)); |
| 771 | /* Shuffle target powers accross transmit rates. */ |
| 772 | power[ATHN_POWER_OFDM6 ] = |
| 773 | power[ATHN_POWER_OFDM9 ] = |
| 774 | power[ATHN_POWER_OFDM12] = |
| 775 | power[ATHN_POWER_OFDM18] = |
| 776 | power[ATHN_POWER_OFDM24] = tpow_ofdm[0]; |
| 777 | power[ATHN_POWER_OFDM36] = tpow_ofdm[1]; |
| 778 | power[ATHN_POWER_OFDM48] = tpow_ofdm[2]; |
| 779 | power[ATHN_POWER_OFDM54] = tpow_ofdm[3]; |
| 780 | if (IEEE80211_IS_CHAN_2GHZ(c)) { |
| 781 | power[ATHN_POWER_CCK1_LP ] = |
| 782 | power[ATHN_POWER_CCK2_LP ] = |
| 783 | power[ATHN_POWER_CCK2_SP ] = |
| 784 | power[ATHN_POWER_CCK55_LP] = tpow_cck[0]; |
| 785 | power[ATHN_POWER_CCK55_SP] = tpow_cck[1]; |
| 786 | power[ATHN_POWER_CCK11_LP] = tpow_cck[2]; |
| 787 | power[ATHN_POWER_CCK11_SP] = tpow_cck[3]; |
| 788 | } |
| 789 | /* Next entry covers MCS0, MCS8 and MCS16. */ |
| 790 | power[ATHN_POWER_HT20( 0)] = tpow_ht20[ 0]; |
| 791 | /* Next entry covers MCS1-3, MCS9-11 and MCS17-19. */ |
| 792 | power[ATHN_POWER_HT20( 1)] = tpow_ht20[ 1]; |
| 793 | power[ATHN_POWER_HT20( 4)] = tpow_ht20[ 2]; |
| 794 | power[ATHN_POWER_HT20( 5)] = tpow_ht20[ 3]; |
| 795 | power[ATHN_POWER_HT20( 6)] = tpow_ht20[ 4]; |
| 796 | power[ATHN_POWER_HT20( 7)] = tpow_ht20[ 5]; |
| 797 | power[ATHN_POWER_HT20(12)] = tpow_ht20[ 6]; |
| 798 | power[ATHN_POWER_HT20(13)] = tpow_ht20[ 7]; |
| 799 | power[ATHN_POWER_HT20(14)] = tpow_ht20[ 8]; |
| 800 | power[ATHN_POWER_HT20(15)] = tpow_ht20[ 9]; |
| 801 | power[ATHN_POWER_HT20(20)] = tpow_ht20[10]; |
| 802 | power[ATHN_POWER_HT20(21)] = tpow_ht20[11]; |
| 803 | power[ATHN_POWER_HT20(22)] = tpow_ht20[12]; |
| 804 | power[ATHN_POWER_HT20(23)] = tpow_ht20[13]; |
| 805 | if (extc != NULL) { |
| 806 | /* Next entry covers MCS0, MCS8 and MCS16. */ |
| 807 | power[ATHN_POWER_HT40( 0)] = tpow_ht40[ 0]; |
| 808 | /* Next entry covers MCS1-3, MCS9-11 and MCS17-19. */ |
| 809 | power[ATHN_POWER_HT40( 1)] = tpow_ht40[ 1]; |
| 810 | power[ATHN_POWER_HT40( 4)] = tpow_ht40[ 2]; |
| 811 | power[ATHN_POWER_HT40( 5)] = tpow_ht40[ 3]; |
| 812 | power[ATHN_POWER_HT40( 6)] = tpow_ht40[ 4]; |
| 813 | power[ATHN_POWER_HT40( 7)] = tpow_ht40[ 5]; |
| 814 | power[ATHN_POWER_HT40(12)] = tpow_ht40[ 6]; |
| 815 | power[ATHN_POWER_HT40(13)] = tpow_ht40[ 7]; |
| 816 | power[ATHN_POWER_HT40(14)] = tpow_ht40[ 8]; |
| 817 | power[ATHN_POWER_HT40(15)] = tpow_ht40[ 9]; |
| 818 | power[ATHN_POWER_HT40(20)] = tpow_ht40[10]; |
| 819 | power[ATHN_POWER_HT40(21)] = tpow_ht40[11]; |
| 820 | power[ATHN_POWER_HT40(22)] = tpow_ht40[12]; |
| 821 | power[ATHN_POWER_HT40(23)] = tpow_ht40[13]; |
| 822 | } |
| 823 | |
| 824 | /* Write transmit power values to hardware. */ |
| 825 | ar9003_write_txpower(sc, power); |
| 826 | |
| 827 | /* Apply transmit power correction. */ |
| 828 | ar9380_set_correction(sc, c); |
| 829 | } |
| 830 | |
| 831 | Static void |
| 832 | ar9380_get_correction(struct athn_softc *sc, struct ieee80211_channel *c, |
| 833 | int chain, int *corr, int *temp) |
| 834 | { |
| 835 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 836 | const struct ar9380_cal_data_per_freq_op_loop *pierdata; |
| 837 | const uint8_t *pierfreq; |
| 838 | uint8_t fbin; |
| 839 | int lo, hi, npiers; |
| 840 | |
| 841 | if (IEEE80211_IS_CHAN_2GHZ(c)) { |
| 842 | pierfreq = eep->calFreqPier2G; |
| 843 | pierdata = eep->calPierData2G[chain]; |
| 844 | npiers = AR9380_NUM_2G_CAL_PIERS; |
| 845 | } |
| 846 | else { |
| 847 | pierfreq = eep->calFreqPier5G; |
| 848 | pierdata = eep->calPierData5G[chain]; |
| 849 | npiers = AR9380_NUM_5G_CAL_PIERS; |
| 850 | } |
| 851 | /* Find channel in ROM pier table. */ |
| 852 | fbin = athn_chan2fbin(c); |
| 853 | athn_get_pier_ival(fbin, pierfreq, npiers, &lo, &hi); |
| 854 | |
| 855 | *corr = athn_interpolate(fbin, |
| 856 | pierfreq[lo], pierdata[lo].refPower, |
| 857 | pierfreq[hi], pierdata[hi].refPower); |
| 858 | *temp = athn_interpolate(fbin, |
| 859 | pierfreq[lo], pierdata[lo].tempMeas, |
| 860 | pierfreq[hi], pierdata[hi].tempMeas); |
| 861 | } |
| 862 | |
| 863 | Static void |
| 864 | ar9380_set_correction(struct athn_softc *sc, struct ieee80211_channel *c) |
| 865 | { |
| 866 | const struct ar9380_eeprom *eep = sc->sc_eep; |
| 867 | const struct ar9380_modal_eep_header *modal; |
| 868 | uint32_t reg; |
| 869 | int8_t slope; |
| 870 | int i, corr, temp, temp0; |
| 871 | |
| 872 | if (IEEE80211_IS_CHAN_2GHZ(c)) |
| 873 | modal = &eep->modalHeader2G; |
| 874 | else |
| 875 | modal = &eep->modalHeader5G; |
| 876 | |
| 877 | temp0 = 0; /* XXX: gcc */ |
| 878 | for (i = 0; i < AR9380_MAX_CHAINS; i++) { |
| 879 | ar9380_get_correction(sc, c, i, &corr, &temp); |
| 880 | if (i == 0) |
| 881 | temp0 = temp; |
| 882 | |
| 883 | reg = AR_READ(sc, AR_PHY_TPC_11_B(i)); |
| 884 | reg = RW(reg, AR_PHY_TPC_11_OLPC_GAIN_DELTA, corr); |
| 885 | AR_WRITE(sc, AR_PHY_TPC_11_B(i), reg); |
| 886 | |
| 887 | /* Enable open loop power control. */ |
| 888 | reg = AR_READ(sc, AR_PHY_TPC_6_B(i)); |
| 889 | reg = RW(reg, AR_PHY_TPC_6_ERROR_EST_MODE, 3); |
| 890 | AR_WRITE(sc, AR_PHY_TPC_6_B(i), reg); |
| 891 | } |
| 892 | |
| 893 | /* Enable temperature compensation. */ |
| 894 | if (IEEE80211_IS_CHAN_5GHZ(c) && |
| 895 | eep->base_ext2.tempSlopeLow != 0) { |
| 896 | if (c->ic_freq <= 5500) { |
| 897 | slope = athn_interpolate(c->ic_freq, |
| 898 | 5180, eep->base_ext2.tempSlopeLow, |
| 899 | 5500, modal->tempSlope); |
| 900 | } |
| 901 | else { |
| 902 | slope = athn_interpolate(c->ic_freq, |
| 903 | 5500, modal->tempSlope, |
| 904 | 5785, eep->base_ext2.tempSlopeHigh); |
| 905 | } |
| 906 | } |
| 907 | else |
| 908 | slope = modal->tempSlope; |
| 909 | |
| 910 | reg = AR_READ(sc, AR_PHY_TPC_19); |
| 911 | reg = RW(reg, AR_PHY_TPC_19_ALPHA_THERM, slope); |
| 912 | AR_WRITE(sc, AR_PHY_TPC_19, reg); |
| 913 | |
| 914 | reg = AR_READ(sc, AR_PHY_TPC_18); |
| 915 | reg = RW(reg, AR_PHY_TPC_18_THERM_CAL, temp0); |
| 916 | AR_WRITE(sc, AR_PHY_TPC_18, reg); |
| 917 | AR_WRITE_BARRIER(sc); |
| 918 | } |
| 919 | |