| 1 | /* $NetBSD: arn9285.c,v 1.3 2013/10/17 21:24:24 christos Exp $ */ |
| 2 | /* $OpenBSD: ar9285.c,v 1.19 2012/06/10 21:23:36 kettenis Exp $ */ |
| 3 | |
| 4 | /*- |
| 5 | * Copyright (c) 2009-2010 Damien Bergamini <damien.bergamini@free.fr> |
| 6 | * Copyright (c) 2008-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 AR9285 and AR9271 chipsets. |
| 24 | */ |
| 25 | |
| 26 | #include <sys/cdefs.h> |
| 27 | __KERNEL_RCSID(0, "$NetBSD: arn9285.c,v 1.3 2013/10/17 21:24:24 christos Exp $" ); |
| 28 | |
| 29 | #ifndef _MODULE |
| 30 | #include "athn_usb.h" |
| 31 | #endif |
| 32 | |
| 33 | #include <sys/param.h> |
| 34 | #include <sys/sockio.h> |
| 35 | #include <sys/mbuf.h> |
| 36 | #include <sys/kernel.h> |
| 37 | #include <sys/socket.h> |
| 38 | #include <sys/systm.h> |
| 39 | #include <sys/malloc.h> |
| 40 | #include <sys/queue.h> |
| 41 | #include <sys/callout.h> |
| 42 | #include <sys/conf.h> |
| 43 | #include <sys/device.h> |
| 44 | |
| 45 | #include <sys/bus.h> |
| 46 | #include <sys/endian.h> |
| 47 | #include <sys/intr.h> |
| 48 | |
| 49 | #include <net/bpf.h> |
| 50 | #include <net/if.h> |
| 51 | #include <net/if_arp.h> |
| 52 | #include <net/if_dl.h> |
| 53 | #include <net/if_ether.h> |
| 54 | #include <net/if_media.h> |
| 55 | #include <net/if_types.h> |
| 56 | |
| 57 | #include <netinet/in.h> |
| 58 | #include <netinet/in_systm.h> |
| 59 | #include <netinet/in_var.h> |
| 60 | #include <netinet/ip.h> |
| 61 | |
| 62 | #include <net80211/ieee80211_var.h> |
| 63 | #include <net80211/ieee80211_amrr.h> |
| 64 | #include <net80211/ieee80211_radiotap.h> |
| 65 | |
| 66 | #include <dev/ic/athnreg.h> |
| 67 | #include <dev/ic/athnvar.h> |
| 68 | #include <dev/ic/arn9285.h> |
| 69 | |
| 70 | #include <dev/ic/arn5008reg.h> |
| 71 | #include <dev/ic/arn9280reg.h> |
| 72 | #include <dev/ic/arn9285reg.h> |
| 73 | |
| 74 | #include <dev/ic/arn5008.h> |
| 75 | #include <dev/ic/arn9280.h> |
| 76 | #include <dev/ic/arn9285.h> |
| 77 | |
| 78 | #define Static static |
| 79 | |
| 80 | Static int ar9285_cl_cal(struct athn_softc *, struct ieee80211_channel *, |
| 81 | struct ieee80211_channel *); |
| 82 | Static void ar9285_get_pdadcs(struct athn_softc *, |
| 83 | struct ieee80211_channel *, int, uint8_t, uint8_t *, |
| 84 | uint8_t *); |
| 85 | Static const struct ar_spur_chan * |
| 86 | ar9285_get_spur_chans(struct athn_softc *, int); |
| 87 | Static void ar9285_init_from_rom(struct athn_softc *, |
| 88 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 89 | Static void ar9285_set_power_calib(struct athn_softc *, |
| 90 | struct ieee80211_channel *); |
| 91 | Static void ar9285_set_txpower(struct athn_softc *, |
| 92 | struct ieee80211_channel *, struct ieee80211_channel *); |
| 93 | Static void ar9285_setup(struct athn_softc *); |
| 94 | Static void ar9285_swap_rom(struct athn_softc *); |
| 95 | |
| 96 | PUBLIC int |
| 97 | ar9285_attach(struct athn_softc *sc) |
| 98 | { |
| 99 | |
| 100 | sc->sc_eep_base = AR9285_EEP_START_LOC; |
| 101 | sc->sc_eep_size = sizeof(struct ar9285_eeprom); |
| 102 | sc->sc_def_nf = AR9285_PHY_CCA_MAX_GOOD_VALUE; |
| 103 | sc->sc_ngpiopins = (sc->sc_flags & ATHN_FLAG_USB) ? 16 : 12; |
| 104 | sc->sc_led_pin = (sc->sc_flags & ATHN_FLAG_USB) ? 15 : 1; |
| 105 | sc->sc_workaround = AR9285_WA_DEFAULT; |
| 106 | sc->sc_ops.setup = ar9285_setup; |
| 107 | sc->sc_ops.swap_rom = ar9285_swap_rom; |
| 108 | sc->sc_ops.init_from_rom = ar9285_init_from_rom; |
| 109 | sc->sc_ops.set_txpower = ar9285_set_txpower; |
| 110 | sc->sc_ops.set_synth = ar9280_set_synth; |
| 111 | sc->sc_ops.spur_mitigate = ar9280_spur_mitigate; |
| 112 | sc->sc_ops.get_spur_chans = ar9285_get_spur_chans; |
| 113 | #if NATHN_USB > 0 |
| 114 | if (AR_SREV_9271(sc)) |
| 115 | sc->sc_ini = &ar9271_ini; |
| 116 | else |
| 117 | #endif |
| 118 | sc->sc_ini = &ar9285_1_2_ini; |
| 119 | sc->sc_serdes = &ar9280_2_0_serdes; |
| 120 | |
| 121 | return ar5008_attach(sc); |
| 122 | } |
| 123 | |
| 124 | Static void |
| 125 | ar9285_setup(struct athn_softc *sc) |
| 126 | { |
| 127 | const struct ar9285_eeprom *eep = sc->sc_eep; |
| 128 | uint8_t type; |
| 129 | |
| 130 | /* Select initialization values based on ROM. */ |
| 131 | type = eep->baseEepHeader.txGainType; |
| 132 | DPRINTFN(DBG_TX, sc, "Tx gain type=0x%x\n" , type); |
| 133 | #if NATHN_USB > 0 |
| 134 | if (AR_SREV_9271(sc)) { |
| 135 | if (type == AR_EEP_TXGAIN_HIGH_POWER) |
| 136 | sc->sc_tx_gain = &ar9271_tx_gain_high_power; |
| 137 | else |
| 138 | sc->sc_tx_gain = &ar9271_tx_gain; |
| 139 | } |
| 140 | else |
| 141 | #endif /* NATHN_USB */ |
| 142 | if ((AR_READ(sc, AR_AN_SYNTH9) & 0x7) == 0x1) { /* XE rev. */ |
| 143 | if (type == AR_EEP_TXGAIN_HIGH_POWER) |
| 144 | sc->sc_tx_gain = &ar9285_2_0_tx_gain_high_power; |
| 145 | else |
| 146 | sc->sc_tx_gain = &ar9285_2_0_tx_gain; |
| 147 | } |
| 148 | else { |
| 149 | if (type == AR_EEP_TXGAIN_HIGH_POWER) |
| 150 | sc->sc_tx_gain = &ar9285_1_2_tx_gain_high_power; |
| 151 | else |
| 152 | sc->sc_tx_gain = &ar9285_1_2_tx_gain; |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | Static void |
| 157 | ar9285_swap_rom(struct athn_softc *sc) |
| 158 | { |
| 159 | struct ar9285_eeprom *eep = sc->sc_eep; |
| 160 | int i; |
| 161 | |
| 162 | eep->modalHeader.antCtrlCommon = |
| 163 | bswap32(eep->modalHeader.antCtrlCommon); |
| 164 | eep->modalHeader.antCtrlChain = |
| 165 | bswap32(eep->modalHeader.antCtrlChain); |
| 166 | |
| 167 | for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { |
| 168 | eep->modalHeader.spurChans[i].spurChan = |
| 169 | bswap16(eep->modalHeader.spurChans[i].spurChan); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | Static const struct ar_spur_chan * |
| 174 | ar9285_get_spur_chans(struct athn_softc *sc, int is2ghz) |
| 175 | { |
| 176 | const struct ar9285_eeprom *eep = sc->sc_eep; |
| 177 | |
| 178 | KASSERT(is2ghz); |
| 179 | return eep->modalHeader.spurChans; |
| 180 | } |
| 181 | |
| 182 | Static void |
| 183 | ar9285_init_from_rom(struct athn_softc *sc, struct ieee80211_channel *c, |
| 184 | struct ieee80211_channel *extc) |
| 185 | { |
| 186 | const struct ar9285_eeprom *eep = sc->sc_eep; |
| 187 | const struct ar9285_modal_eep_header *modal = &eep->modalHeader; |
| 188 | uint32_t reg, offset = 0x1000; |
| 189 | uint8_t ob[5], db1[5], db2[5]; |
| 190 | uint8_t txRxAtten; |
| 191 | |
| 192 | AR_WRITE(sc, AR_PHY_SWITCH_COM, modal->antCtrlCommon); |
| 193 | AR_WRITE(sc, AR_PHY_SWITCH_CHAIN_0, modal->antCtrlChain); |
| 194 | |
| 195 | reg = AR_READ(sc, AR_PHY_TIMING_CTRL4_0); |
| 196 | reg = RW(reg, AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, modal->iqCalI); |
| 197 | reg = RW(reg, AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, modal->iqCalQ); |
| 198 | AR_WRITE(sc, AR_PHY_TIMING_CTRL4_0, reg); |
| 199 | |
| 200 | if (sc->sc_eep_rev >= AR_EEP_MINOR_VER_3) { |
| 201 | reg = AR_READ(sc, AR_PHY_GAIN_2GHZ); |
| 202 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, |
| 203 | modal->bswMargin); |
| 204 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN1_DB, |
| 205 | modal->bswAtten); |
| 206 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, |
| 207 | modal->xatten2Margin); |
| 208 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN2_DB, |
| 209 | modal->xatten2Db); |
| 210 | AR_WRITE(sc, AR_PHY_GAIN_2GHZ, reg); |
| 211 | |
| 212 | /* Duplicate values of chain 0 for chain 1. */ |
| 213 | reg = AR_READ(sc, AR_PHY_GAIN_2GHZ + offset); |
| 214 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, |
| 215 | modal->bswMargin); |
| 216 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN1_DB, |
| 217 | modal->bswAtten); |
| 218 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, |
| 219 | modal->xatten2Margin); |
| 220 | reg = RW(reg, AR_PHY_GAIN_2GHZ_XATTEN2_DB, |
| 221 | modal->xatten2Db); |
| 222 | AR_WRITE(sc, AR_PHY_GAIN_2GHZ + offset, reg); |
| 223 | } |
| 224 | if (sc->sc_eep_rev >= AR_EEP_MINOR_VER_3) |
| 225 | txRxAtten = modal->txRxAtten; |
| 226 | else /* Workaround for ROM versions < 14.3. */ |
| 227 | txRxAtten = 23; |
| 228 | reg = AR_READ(sc, AR_PHY_RXGAIN); |
| 229 | reg = RW(reg, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAtten); |
| 230 | reg = RW(reg, AR9280_PHY_RXGAIN_TXRX_MARGIN, modal->rxTxMargin); |
| 231 | AR_WRITE(sc, AR_PHY_RXGAIN, reg); |
| 232 | |
| 233 | /* Duplicate values of chain 0 for chain 1. */ |
| 234 | reg = AR_READ(sc, AR_PHY_RXGAIN + offset); |
| 235 | reg = RW(reg, AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAtten); |
| 236 | reg = RW(reg, AR9280_PHY_RXGAIN_TXRX_MARGIN, modal->rxTxMargin); |
| 237 | AR_WRITE(sc, AR_PHY_RXGAIN + offset, reg); |
| 238 | |
| 239 | if (modal->version >= 3) { |
| 240 | /* Setup antenna diversity from ROM. */ |
| 241 | reg = AR_READ(sc, AR_PHY_MULTICHAIN_GAIN_CTL); |
| 242 | reg = RW(reg, AR9285_PHY_ANT_DIV_CTL_ALL, 0); |
| 243 | reg = RW(reg, AR9285_PHY_ANT_DIV_CTL, |
| 244 | (modal->ob_234 >> 12) & 0x1); |
| 245 | reg = RW(reg, AR9285_PHY_ANT_DIV_ALT_LNACONF, |
| 246 | (modal->db1_234 >> 12) & 0x3); |
| 247 | reg = RW(reg, AR9285_PHY_ANT_DIV_MAIN_LNACONF, |
| 248 | (modal->db1_234 >> 14) & 0x3); |
| 249 | reg = RW(reg, AR9285_PHY_ANT_DIV_ALT_GAINTB, |
| 250 | (modal->ob_234 >> 13) & 0x1); |
| 251 | reg = RW(reg, AR9285_PHY_ANT_DIV_MAIN_GAINTB, |
| 252 | (modal->ob_234 >> 14) & 0x1); |
| 253 | AR_WRITE(sc, AR_PHY_MULTICHAIN_GAIN_CTL, reg); |
| 254 | reg = AR_READ(sc, AR_PHY_MULTICHAIN_GAIN_CTL); /* Flush. */ |
| 255 | |
| 256 | reg = AR_READ(sc, AR_PHY_CCK_DETECT); |
| 257 | if (modal->ob_234 & (1 << 15)) |
| 258 | reg |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; |
| 259 | else |
| 260 | reg &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; |
| 261 | AR_WRITE(sc, AR_PHY_CCK_DETECT, reg); |
| 262 | reg = AR_READ(sc, AR_PHY_CCK_DETECT); /* Flush. */ |
| 263 | } |
| 264 | if (modal->version >= 2) { |
| 265 | ob [0] = (modal->ob_01 >> 0) & 0xf; |
| 266 | ob [1] = (modal->ob_01 >> 4) & 0xf; |
| 267 | ob [2] = (modal->ob_234 >> 0) & 0xf; |
| 268 | ob [3] = (modal->ob_234 >> 4) & 0xf; |
| 269 | ob [4] = (modal->ob_234 >> 8) & 0xf; |
| 270 | |
| 271 | db1[0] = (modal->db1_01 >> 0) & 0xf; |
| 272 | db1[1] = (modal->db1_01 >> 4) & 0xf; |
| 273 | db1[2] = (modal->db1_234 >> 0) & 0xf; |
| 274 | db1[3] = (modal->db1_234 >> 4) & 0xf; |
| 275 | db1[4] = (modal->db1_234 >> 8) & 0xf; |
| 276 | |
| 277 | db2[0] = (modal->db2_01 >> 0) & 0xf; |
| 278 | db2[1] = (modal->db2_01 >> 4) & 0xf; |
| 279 | db2[2] = (modal->db2_234 >> 0) & 0xf; |
| 280 | db2[3] = (modal->db2_234 >> 4) & 0xf; |
| 281 | db2[4] = (modal->db2_234 >> 8) & 0xf; |
| 282 | |
| 283 | } |
| 284 | else if (modal->version == 1) { |
| 285 | ob [0] = (modal->ob_01 >> 0) & 0xf; |
| 286 | ob [1] = (modal->ob_01 >> 4) & 0xf; |
| 287 | /* Field ob_234 does not exist, use ob_01. */ |
| 288 | ob [2] = ob [3] = ob [4] = ob [1]; |
| 289 | |
| 290 | db1[0] = (modal->db1_01 >> 0) & 0xf; |
| 291 | db1[1] = (modal->db1_01 >> 4) & 0xf; |
| 292 | /* Field db1_234 does not exist, use db1_01. */ |
| 293 | db1[2] = db1[3] = db1[4] = db1[1]; |
| 294 | |
| 295 | db2[0] = (modal->db2_01 >> 0) & 0xf; |
| 296 | db2[1] = (modal->db2_01 >> 4) & 0xf; |
| 297 | /* Field db2_234 does not exist, use db2_01. */ |
| 298 | db2[2] = db2[3] = db2[4] = db2[1]; |
| 299 | |
| 300 | } |
| 301 | else { |
| 302 | ob [0] = modal->ob_01; |
| 303 | ob [1] = ob [2] = ob [3] = ob [4] = ob [0]; |
| 304 | |
| 305 | db1[0] = modal->db1_01; |
| 306 | db1[1] = db1[2] = db1[3] = db1[4] = db1[0]; |
| 307 | |
| 308 | /* Field db2_01 does not exist, use db1_01. */ |
| 309 | db2[0] = modal->db1_01; |
| 310 | db2[1] = db2[2] = db2[3] = db2[4] = db2[0]; |
| 311 | } |
| 312 | #if NATHN_USB > 0 |
| 313 | if (AR_SREV_9271(sc)) { |
| 314 | reg = AR_READ(sc, AR9285_AN_RF2G3); |
| 315 | reg = RW(reg, AR9271_AN_RF2G3_OB_CCK, ob [0]); |
| 316 | reg = RW(reg, AR9271_AN_RF2G3_OB_PSK, ob [1]); |
| 317 | reg = RW(reg, AR9271_AN_RF2G3_OB_QAM, ob [2]); |
| 318 | reg = RW(reg, AR9271_AN_RF2G3_DB1, db1[0]); |
| 319 | AR_WRITE(sc, AR9285_AN_RF2G3, reg); |
| 320 | AR_WRITE_BARRIER(sc); |
| 321 | DELAY(100); |
| 322 | reg = AR_READ(sc, AR9285_AN_RF2G4); |
| 323 | reg = RW(reg, AR9271_AN_RF2G4_DB2, db2[0]); |
| 324 | AR_WRITE(sc, AR9285_AN_RF2G4, reg); |
| 325 | AR_WRITE_BARRIER(sc); |
| 326 | DELAY(100); |
| 327 | } |
| 328 | else |
| 329 | #endif /* ATHN_USB */ |
| 330 | { |
| 331 | reg = AR_READ(sc, AR9285_AN_RF2G3); |
| 332 | reg = RW(reg, AR9285_AN_RF2G3_OB_0, ob [0]); |
| 333 | reg = RW(reg, AR9285_AN_RF2G3_OB_1, ob [1]); |
| 334 | reg = RW(reg, AR9285_AN_RF2G3_OB_2, ob [2]); |
| 335 | reg = RW(reg, AR9285_AN_RF2G3_OB_3, ob [3]); |
| 336 | reg = RW(reg, AR9285_AN_RF2G3_OB_4, ob [4]); |
| 337 | reg = RW(reg, AR9285_AN_RF2G3_DB1_0, db1[0]); |
| 338 | reg = RW(reg, AR9285_AN_RF2G3_DB1_1, db1[1]); |
| 339 | reg = RW(reg, AR9285_AN_RF2G3_DB1_2, db1[2]); |
| 340 | AR_WRITE(sc, AR9285_AN_RF2G3, reg); |
| 341 | AR_WRITE_BARRIER(sc); |
| 342 | DELAY(100); |
| 343 | reg = AR_READ(sc, AR9285_AN_RF2G4); |
| 344 | reg = RW(reg, AR9285_AN_RF2G4_DB1_3, db1[3]); |
| 345 | reg = RW(reg, AR9285_AN_RF2G4_DB1_4, db1[4]); |
| 346 | reg = RW(reg, AR9285_AN_RF2G4_DB2_0, db2[0]); |
| 347 | reg = RW(reg, AR9285_AN_RF2G4_DB2_1, db2[1]); |
| 348 | reg = RW(reg, AR9285_AN_RF2G4_DB2_2, db2[2]); |
| 349 | reg = RW(reg, AR9285_AN_RF2G4_DB2_3, db2[3]); |
| 350 | reg = RW(reg, AR9285_AN_RF2G4_DB2_4, db2[4]); |
| 351 | AR_WRITE(sc, AR9285_AN_RF2G4, reg); |
| 352 | AR_WRITE_BARRIER(sc); |
| 353 | DELAY(100); |
| 354 | } |
| 355 | |
| 356 | reg = AR_READ(sc, AR_PHY_SETTLING); |
| 357 | reg = RW(reg, AR_PHY_SETTLING_SWITCH, modal->switchSettling); |
| 358 | AR_WRITE(sc, AR_PHY_SETTLING, reg); |
| 359 | |
| 360 | reg = AR_READ(sc, AR_PHY_DESIRED_SZ); |
| 361 | reg = RW(reg, AR_PHY_DESIRED_SZ_ADC, modal->adcDesiredSize); |
| 362 | AR_WRITE(sc, AR_PHY_DESIRED_SZ, reg); |
| 363 | |
| 364 | reg = SM(AR_PHY_RF_CTL4_TX_END_XPAA_OFF, modal->txEndToXpaOff); |
| 365 | reg |= SM(AR_PHY_RF_CTL4_TX_END_XPAB_OFF, modal->txEndToXpaOff); |
| 366 | reg |= SM(AR_PHY_RF_CTL4_FRAME_XPAA_ON, modal->txFrameToXpaOn); |
| 367 | reg |= SM(AR_PHY_RF_CTL4_FRAME_XPAB_ON, modal->txFrameToXpaOn); |
| 368 | AR_WRITE(sc, AR_PHY_RF_CTL4, reg); |
| 369 | |
| 370 | reg = AR_READ(sc, AR_PHY_RF_CTL3); |
| 371 | reg = RW(reg, AR_PHY_TX_END_TO_A2_RX_ON, modal->txEndToRxOn); |
| 372 | AR_WRITE(sc, AR_PHY_RF_CTL3, reg); |
| 373 | |
| 374 | reg = AR_READ(sc, AR_PHY_CCA(0)); |
| 375 | reg = RW(reg, AR9280_PHY_CCA_THRESH62, modal->thresh62); |
| 376 | AR_WRITE(sc, AR_PHY_CCA(0), reg); |
| 377 | |
| 378 | reg = AR_READ(sc, AR_PHY_EXT_CCA0); |
| 379 | reg = RW(reg, AR_PHY_EXT_CCA0_THRESH62, modal->thresh62); |
| 380 | AR_WRITE(sc, AR_PHY_EXT_CCA0, reg); |
| 381 | |
| 382 | if (sc->sc_eep_rev >= AR_EEP_MINOR_VER_2) { |
| 383 | reg = AR_READ(sc, AR_PHY_RF_CTL2); |
| 384 | reg = RW(reg, AR_PHY_TX_END_PA_ON, |
| 385 | modal->txFrameToPaOn); |
| 386 | reg = RW(reg, AR_PHY_TX_END_DATA_START, |
| 387 | modal->txFrameToDataStart); |
| 388 | AR_WRITE(sc, AR_PHY_RF_CTL2, reg); |
| 389 | } |
| 390 | #ifndef IEEE80211_NO_HT |
| 391 | if (sc->sc_eep_rev >= AR_EEP_MINOR_VER_3 && extc != NULL) { |
| 392 | reg = AR_READ(sc, AR_PHY_SETTLING); |
| 393 | reg = RW(reg, AR_PHY_SETTLING_SWITCH, modal->swSettleHt40); |
| 394 | AR_WRITE(sc, AR_PHY_SETTLING, reg); |
| 395 | } |
| 396 | #endif |
| 397 | AR_WRITE_BARRIER(sc); |
| 398 | } |
| 399 | |
| 400 | PUBLIC void |
| 401 | ar9285_pa_calib(struct athn_softc *sc) |
| 402 | { |
| 403 | /* List of registers that need to be saved/restored. */ |
| 404 | static const uint16_t regs[] = { |
| 405 | AR9285_AN_TOP3, |
| 406 | AR9285_AN_RXTXBB1, |
| 407 | AR9285_AN_RF2G1, |
| 408 | AR9285_AN_RF2G2, |
| 409 | AR9285_AN_TOP2, |
| 410 | AR9285_AN_RF2G8, |
| 411 | AR9285_AN_RF2G7 |
| 412 | }; |
| 413 | uint32_t svg[7], reg, ccomp_svg; |
| 414 | size_t i; |
| 415 | |
| 416 | /* No PA calibration needed for high power solutions. */ |
| 417 | if (AR_SREV_9285(sc) && |
| 418 | ((struct ar9285_base_eep_header *)sc->sc_eep)->txGainType == |
| 419 | AR_EEP_TXGAIN_HIGH_POWER) /* XXX AR9287? */ |
| 420 | return; |
| 421 | |
| 422 | /* Save registers. */ |
| 423 | for (i = 0; i < __arraycount(regs); i++) |
| 424 | svg[i] = AR_READ(sc, regs[i]); |
| 425 | |
| 426 | AR_CLRBITS(sc, AR9285_AN_RF2G6, 1); |
| 427 | AR_SETBITS(sc, AR_PHY(2), 1 << 27); |
| 428 | |
| 429 | AR_SETBITS(sc, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC); |
| 430 | AR_SETBITS(sc, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1); |
| 431 | AR_SETBITS(sc, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I); |
| 432 | AR_SETBITS(sc, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF); |
| 433 | AR_CLRBITS(sc, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL); |
| 434 | AR_CLRBITS(sc, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB); |
| 435 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL); |
| 436 | /* Power down PA drivers. */ |
| 437 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1); |
| 438 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2); |
| 439 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT); |
| 440 | |
| 441 | reg = AR_READ(sc, AR9285_AN_RF2G8); |
| 442 | reg = RW(reg, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); |
| 443 | AR_WRITE(sc, AR9285_AN_RF2G8, reg); |
| 444 | |
| 445 | reg = AR_READ(sc, AR9285_AN_RF2G7); |
| 446 | reg = RW(reg, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); |
| 447 | AR_WRITE(sc, AR9285_AN_RF2G7, reg); |
| 448 | |
| 449 | reg = AR_READ(sc, AR9285_AN_RF2G6); |
| 450 | /* Save compensation capacitor value. */ |
| 451 | ccomp_svg = MS(reg, AR9285_AN_RF2G6_CCOMP); |
| 452 | /* Program compensation capacitor for dynamic PA. */ |
| 453 | reg = RW(reg, AR9285_AN_RF2G6_CCOMP, 0xf); |
| 454 | AR_WRITE(sc, AR9285_AN_RF2G6, reg); |
| 455 | |
| 456 | AR_WRITE(sc, AR9285_AN_TOP2, AR9285_AN_TOP2_DEFAULT); |
| 457 | AR_WRITE_BARRIER(sc); |
| 458 | DELAY(30); |
| 459 | |
| 460 | /* Clear offsets 6-1. */ |
| 461 | AR_CLRBITS(sc, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS_6_1); |
| 462 | /* Clear offset 0. */ |
| 463 | AR_CLRBITS(sc, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP); |
| 464 | /* Set offsets 6-1. */ |
| 465 | for (i = 6; i >= 1; i--) { |
| 466 | AR_SETBITS(sc, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS(i)); |
| 467 | AR_WRITE_BARRIER(sc); |
| 468 | DELAY(1); |
| 469 | if (AR_READ(sc, AR9285_AN_RF2G9) & AR9285_AN_RXTXBB1_SPARE9) { |
| 470 | AR_SETBITS(sc, AR9285_AN_RF2G6, |
| 471 | AR9285_AN_RF2G6_OFFS(i)); |
| 472 | } |
| 473 | else { |
| 474 | AR_CLRBITS(sc, AR9285_AN_RF2G6, |
| 475 | AR9285_AN_RF2G6_OFFS(i)); |
| 476 | } |
| 477 | } |
| 478 | /* Set offset 0. */ |
| 479 | AR_SETBITS(sc, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP); |
| 480 | AR_WRITE_BARRIER(sc); |
| 481 | DELAY(1); |
| 482 | if (AR_READ(sc, AR9285_AN_RF2G9) & AR9285_AN_RXTXBB1_SPARE9) |
| 483 | AR_SETBITS(sc, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP); |
| 484 | else |
| 485 | AR_CLRBITS(sc, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP); |
| 486 | |
| 487 | AR_WRITE_BARRIER(sc); |
| 488 | |
| 489 | AR_SETBITS(sc, AR9285_AN_RF2G6, 1); |
| 490 | AR_CLRBITS(sc, AR_PHY(2), 1 << 27); |
| 491 | |
| 492 | /* Restore registers. */ |
| 493 | for (i = 0; i < __arraycount(regs); i++) |
| 494 | AR_WRITE(sc, regs[i], svg[i]); |
| 495 | |
| 496 | /* Restore compensation capacitor value. */ |
| 497 | reg = AR_READ(sc, AR9285_AN_RF2G6); |
| 498 | reg = RW(reg, AR9285_AN_RF2G6_CCOMP, ccomp_svg); |
| 499 | AR_WRITE(sc, AR9285_AN_RF2G6, reg); |
| 500 | AR_WRITE_BARRIER(sc); |
| 501 | } |
| 502 | |
| 503 | PUBLIC void |
| 504 | ar9271_pa_calib(struct athn_softc *sc) |
| 505 | { |
| 506 | #if NATHN_USB > 0 |
| 507 | /* List of registers that need to be saved/restored. */ |
| 508 | static const uint16_t regs[] = { |
| 509 | AR9285_AN_TOP3, |
| 510 | AR9285_AN_RXTXBB1, |
| 511 | AR9285_AN_RF2G1, |
| 512 | AR9285_AN_RF2G2, |
| 513 | AR9285_AN_TOP2, |
| 514 | AR9285_AN_RF2G8, |
| 515 | AR9285_AN_RF2G7 |
| 516 | }; |
| 517 | uint32_t svg[7], reg, rf2g3_svg; |
| 518 | size_t i; |
| 519 | |
| 520 | /* Save registers. */ |
| 521 | for (i = 0; i < __arraycount(regs); i++) |
| 522 | svg[i] = AR_READ(sc, regs[i]); |
| 523 | |
| 524 | AR_CLRBITS(sc, AR9285_AN_RF2G6, 1); |
| 525 | AR_SETBITS(sc, AR_PHY(2), 1 << 27); |
| 526 | |
| 527 | AR_SETBITS(sc, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC); |
| 528 | AR_SETBITS(sc, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1); |
| 529 | AR_SETBITS(sc, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I); |
| 530 | AR_SETBITS(sc, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF); |
| 531 | AR_CLRBITS(sc, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL); |
| 532 | AR_CLRBITS(sc, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB); |
| 533 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL); |
| 534 | /* Power down PA drivers. */ |
| 535 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1); |
| 536 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2); |
| 537 | AR_CLRBITS(sc, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT); |
| 538 | |
| 539 | reg = AR_READ(sc, AR9285_AN_RF2G8); |
| 540 | reg = RW(reg, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); |
| 541 | AR_WRITE(sc, AR9285_AN_RF2G8, reg); |
| 542 | |
| 543 | reg = AR_READ(sc, AR9285_AN_RF2G7); |
| 544 | reg = RW(reg, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); |
| 545 | AR_WRITE(sc, AR9285_AN_RF2G7, reg); |
| 546 | |
| 547 | /* Save compensation capacitor value. */ |
| 548 | reg = rf2g3_svg = AR_READ(sc, AR9285_AN_RF2G3); |
| 549 | /* Program compensation capacitor for dynamic PA. */ |
| 550 | reg = RW(reg, AR9271_AN_RF2G3_CCOMP, 0xfff); |
| 551 | AR_WRITE(sc, AR9285_AN_RF2G3, reg); |
| 552 | |
| 553 | AR_WRITE(sc, AR9285_AN_TOP2, AR9285_AN_TOP2_DEFAULT); |
| 554 | AR_WRITE_BARRIER(sc); |
| 555 | DELAY(30); |
| 556 | |
| 557 | /* Clear offsets 6-0. */ |
| 558 | AR_CLRBITS(sc, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS_6_0); |
| 559 | /* Set offsets 6-1. */ |
| 560 | for (i = 6; i >= 1; i--) { |
| 561 | reg = AR_READ(sc, AR9285_AN_RF2G6); |
| 562 | reg |= AR9271_AN_RF2G6_OFFS(i); |
| 563 | AR_WRITE(sc, AR9285_AN_RF2G6, reg); |
| 564 | AR_WRITE_BARRIER(sc); |
| 565 | DELAY(1); |
| 566 | if (!(AR_READ(sc, AR9285_AN_RF2G9) & AR9285_AN_RXTXBB1_SPARE9)) |
| 567 | reg &= ~AR9271_AN_RF2G6_OFFS(i); |
| 568 | AR_WRITE(sc, AR9285_AN_RF2G6, reg); |
| 569 | } |
| 570 | AR_WRITE_BARRIER(sc); |
| 571 | |
| 572 | AR_SETBITS(sc, AR9285_AN_RF2G6, 1); |
| 573 | AR_CLRBITS(sc, AR_PHY(2), 1 << 27); |
| 574 | |
| 575 | /* Restore registers. */ |
| 576 | for (i = 0; i < __arraycount(regs); i++) |
| 577 | AR_WRITE(sc, regs[i], svg[i]); |
| 578 | |
| 579 | /* Restore compensation capacitor value. */ |
| 580 | AR_WRITE(sc, AR9285_AN_RF2G3, rf2g3_svg); |
| 581 | AR_WRITE_BARRIER(sc); |
| 582 | #endif /* NATHN_USB */ |
| 583 | } |
| 584 | |
| 585 | /* |
| 586 | * Carrier Leakage Calibration. |
| 587 | */ |
| 588 | int |
| 589 | ar9285_cl_cal(struct athn_softc *sc, struct ieee80211_channel *c, |
| 590 | struct ieee80211_channel *extc) |
| 591 | { |
| 592 | int ntries; |
| 593 | |
| 594 | AR_SETBITS(sc, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); |
| 595 | #ifndef IEEE80211_NO_HT |
| 596 | if (0 && extc == NULL) { /* XXX IS_CHAN_HT20!! */ |
| 597 | AR_SETBITS(sc, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); |
| 598 | AR_SETBITS(sc, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); |
| 599 | AR_CLRBITS(sc, AR_PHY_AGC_CONTROL, |
| 600 | AR_PHY_AGC_CONTROL_FLTR_CAL); |
| 601 | AR_CLRBITS(sc, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); |
| 602 | AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); |
| 603 | for (ntries = 0; ntries < 10000; ntries++) { |
| 604 | if (!(AR_READ(sc, AR_PHY_AGC_CONTROL) & |
| 605 | AR_PHY_AGC_CONTROL_CAL)) |
| 606 | break; |
| 607 | DELAY(10); |
| 608 | } |
| 609 | if (ntries == 10000) |
| 610 | return ETIMEDOUT; |
| 611 | AR_CLRBITS(sc, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); |
| 612 | AR_CLRBITS(sc, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); |
| 613 | AR_CLRBITS(sc, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); |
| 614 | } |
| 615 | #endif |
| 616 | AR_CLRBITS(sc, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); |
| 617 | AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); |
| 618 | AR_SETBITS(sc, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); |
| 619 | AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); |
| 620 | for (ntries = 0; ntries < 10000; ntries++) { |
| 621 | if (!(AR_READ(sc, AR_PHY_AGC_CONTROL) & |
| 622 | AR_PHY_AGC_CONTROL_CAL)) |
| 623 | break; |
| 624 | DELAY(10); |
| 625 | } |
| 626 | if (ntries == 10000) |
| 627 | return ETIMEDOUT; |
| 628 | AR_SETBITS(sc, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); |
| 629 | AR_CLRBITS(sc, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); |
| 630 | AR_CLRBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); |
| 631 | AR_WRITE_BARRIER(sc); |
| 632 | return 0; |
| 633 | } |
| 634 | |
| 635 | PUBLIC void |
| 636 | ar9271_load_ani(struct athn_softc *sc) |
| 637 | { |
| 638 | |
| 639 | #if NATHN_USB > 0 |
| 640 | /* Write ANI registers. */ |
| 641 | AR_WRITE(sc, AR_PHY_DESIRED_SZ, 0x6d4000e2); |
| 642 | AR_WRITE(sc, AR_PHY_AGC_CTL1, 0x3139605e); |
| 643 | AR_WRITE(sc, AR_PHY_FIND_SIG, 0x7ec84d2e); |
| 644 | AR_WRITE(sc, AR_PHY_SFCORR_LOW, 0x06903881); |
| 645 | AR_WRITE(sc, AR_PHY_SFCORR, 0x5ac640d0); |
| 646 | AR_WRITE(sc, AR_PHY_CCK_DETECT, 0x803e68c8); |
| 647 | AR_WRITE(sc, AR_PHY_TIMING5, 0xd00a8007); |
| 648 | AR_WRITE(sc, AR_PHY_SFCORR_EXT, 0x05eea6d4); |
| 649 | AR_WRITE_BARRIER(sc); |
| 650 | #endif /* NATHN_USB */ |
| 651 | } |
| 652 | |
| 653 | int |
| 654 | ar9285_init_calib(struct athn_softc *sc, struct ieee80211_channel *c, |
| 655 | struct ieee80211_channel *extc) |
| 656 | { |
| 657 | uint32_t reg, mask, clcgain, rf2g5_svg; |
| 658 | int i, maxgain, nclcs, thresh, error; |
| 659 | |
| 660 | /* Do carrier leakage calibration. */ |
| 661 | if ((error = ar9285_cl_cal(sc, c, extc)) != 0) |
| 662 | return error; |
| 663 | |
| 664 | /* Workaround for high temperature is not applicable on AR9271. */ |
| 665 | if (AR_SREV_9271(sc)) |
| 666 | return 0; |
| 667 | |
| 668 | mask = 0; |
| 669 | nclcs = 0; |
| 670 | reg = AR_READ(sc, AR_PHY_TX_PWRCTRL7); |
| 671 | maxgain = MS(reg, AR_PHY_TX_PWRCTRL_TX_GAIN_TAB_MAX); |
| 672 | for (i = 0; i <= maxgain; i++) { |
| 673 | reg = AR_READ(sc, AR_PHY_TX_GAIN_TBL(i)); |
| 674 | clcgain = MS(reg, AR_PHY_TX_GAIN_CLC); |
| 675 | /* NB: clcgain <= 0xf. */ |
| 676 | if (!(mask & (1 << clcgain))) { |
| 677 | mask |= 1 << clcgain; |
| 678 | nclcs++; |
| 679 | } |
| 680 | } |
| 681 | thresh = 0; |
| 682 | for (i = 0; i < nclcs; i++) { |
| 683 | reg = AR_READ(sc, AR_PHY_CLC_TBL(i)); |
| 684 | if (MS(reg, AR_PHY_CLC_I0) == 0) |
| 685 | thresh++; |
| 686 | if (MS(reg, AR_PHY_CLC_Q0) == 0) |
| 687 | thresh++; |
| 688 | } |
| 689 | if (thresh <= AR9285_CL_CAL_REDO_THRESH) |
| 690 | return 0; /* No need to redo. */ |
| 691 | |
| 692 | /* Threshold reached, redo carrier leakage calibration. */ |
| 693 | DPRINTFN(DBG_INIT, sc, "CLC threshold=%d\n" , thresh); |
| 694 | rf2g5_svg = reg = AR_READ(sc, AR9285_AN_RF2G5); |
| 695 | if ((AR_READ(sc, AR_AN_SYNTH9) & 0x7) == 0x1) /* XE rev. */ |
| 696 | reg = RW(reg, AR9285_AN_RF2G5_IC50TX, 0x5); |
| 697 | else |
| 698 | reg = RW(reg, AR9285_AN_RF2G5_IC50TX, 0x4); |
| 699 | AR_WRITE(sc, AR9285_AN_RF2G5, reg); |
| 700 | AR_WRITE_BARRIER(sc); |
| 701 | error = ar9285_cl_cal(sc, c, extc); |
| 702 | AR_WRITE(sc, AR9285_AN_RF2G5, rf2g5_svg); |
| 703 | AR_WRITE_BARRIER(sc); |
| 704 | return error; |
| 705 | } |
| 706 | |
| 707 | Static void |
| 708 | ar9285_get_pdadcs(struct athn_softc *sc, struct ieee80211_channel *c, |
| 709 | int nxpdgains, uint8_t overlap, uint8_t *boundaries, uint8_t *pdadcs) |
| 710 | { |
| 711 | const struct ar9285_eeprom *eep = sc->sc_eep; |
| 712 | const struct ar9285_cal_data_per_freq *pierdata; |
| 713 | const uint8_t *pierfreq; |
| 714 | struct athn_pier lopier, hipier; |
| 715 | uint8_t fbin; |
| 716 | int i, lo, hi, npiers; |
| 717 | |
| 718 | pierfreq = eep->calFreqPier2G; |
| 719 | pierdata = eep->calPierData2G; |
| 720 | npiers = AR9285_NUM_2G_CAL_PIERS; |
| 721 | |
| 722 | /* Find channel in ROM pier table. */ |
| 723 | fbin = athn_chan2fbin(c); |
| 724 | athn_get_pier_ival(fbin, pierfreq, npiers, &lo, &hi); |
| 725 | |
| 726 | lopier.fbin = pierfreq[lo]; |
| 727 | hipier.fbin = pierfreq[hi]; |
| 728 | for (i = 0; i < nxpdgains; i++) { |
| 729 | lopier.pwr[i] = pierdata[lo].pwrPdg[i]; |
| 730 | lopier.vpd[i] = pierdata[lo].vpdPdg[i]; |
| 731 | hipier.pwr[i] = pierdata[lo].pwrPdg[i]; |
| 732 | hipier.vpd[i] = pierdata[lo].vpdPdg[i]; |
| 733 | } |
| 734 | ar5008_get_pdadcs(sc, fbin, &lopier, &hipier, nxpdgains, |
| 735 | AR9285_PD_GAIN_ICEPTS, overlap, boundaries, pdadcs); |
| 736 | } |
| 737 | |
| 738 | Static void |
| 739 | ar9285_set_power_calib(struct athn_softc *sc, struct ieee80211_channel *c) |
| 740 | { |
| 741 | const struct ar9285_eeprom *eep = sc->sc_eep; |
| 742 | uint8_t boundaries[AR_PD_GAINS_IN_MASK]; |
| 743 | uint8_t pdadcs[AR_NUM_PDADC_VALUES]; |
| 744 | uint8_t xpdgains[AR9285_NUM_PD_GAINS]; |
| 745 | uint8_t overlap; |
| 746 | uint32_t reg; |
| 747 | int i, nxpdgains; |
| 748 | |
| 749 | if (sc->sc_eep_rev < AR_EEP_MINOR_VER_2) { |
| 750 | overlap = MS(AR_READ(sc, AR_PHY_TPCRG5), |
| 751 | AR_PHY_TPCRG5_PD_GAIN_OVERLAP); |
| 752 | } |
| 753 | else |
| 754 | overlap = eep->modalHeader.pdGainOverlap; |
| 755 | |
| 756 | nxpdgains = 0; |
| 757 | memset(xpdgains, 0, sizeof(xpdgains)); |
| 758 | for (i = AR9285_PD_GAINS_IN_MASK - 1; i >= 0; i--) { |
| 759 | if (nxpdgains >= AR9285_NUM_PD_GAINS) |
| 760 | break; |
| 761 | if (eep->modalHeader.xpdGain & (1 << i)) |
| 762 | xpdgains[nxpdgains++] = i; |
| 763 | } |
| 764 | reg = AR_READ(sc, AR_PHY_TPCRG1); |
| 765 | reg = RW(reg, AR_PHY_TPCRG1_NUM_PD_GAIN, nxpdgains - 1); |
| 766 | reg = RW(reg, AR_PHY_TPCRG1_PD_GAIN_1, xpdgains[0]); |
| 767 | reg = RW(reg, AR_PHY_TPCRG1_PD_GAIN_2, xpdgains[1]); |
| 768 | AR_WRITE(sc, AR_PHY_TPCRG1, reg); |
| 769 | |
| 770 | /* NB: No open loop power control for AR9285. */ |
| 771 | ar9285_get_pdadcs(sc, c, nxpdgains, overlap, boundaries, pdadcs); |
| 772 | |
| 773 | /* Write boundaries. */ |
| 774 | reg = SM(AR_PHY_TPCRG5_PD_GAIN_OVERLAP, overlap); |
| 775 | reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1, boundaries[0]); |
| 776 | reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2, boundaries[1]); |
| 777 | reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3, boundaries[2]); |
| 778 | reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4, boundaries[3]); |
| 779 | AR_WRITE(sc, AR_PHY_TPCRG5, reg); |
| 780 | |
| 781 | /* Write PDADC values. */ |
| 782 | for (i = 0; i < AR_NUM_PDADC_VALUES; i += 4) { |
| 783 | AR_WRITE(sc, AR_PHY_PDADC_TBL_BASE + i, |
| 784 | pdadcs[i + 0] << 0 | |
| 785 | pdadcs[i + 1] << 8 | |
| 786 | pdadcs[i + 2] << 16 | |
| 787 | pdadcs[i + 3] << 24); |
| 788 | } |
| 789 | AR_WRITE_BARRIER(sc); |
| 790 | } |
| 791 | |
| 792 | Static void |
| 793 | ar9285_set_txpower(struct athn_softc *sc, struct ieee80211_channel *c, |
| 794 | struct ieee80211_channel *extc) |
| 795 | { |
| 796 | const struct ar9285_eeprom *eep = sc->sc_eep; |
| 797 | #ifdef notyet |
| 798 | const struct ar9285_modal_eep_header *modal = &eep->modalHeader; |
| 799 | #endif |
| 800 | uint8_t tpow_cck[4], tpow_ofdm[4]; |
| 801 | #ifndef IEEE80211_NO_HT |
| 802 | uint8_t tpow_cck_ext[4], tpow_ofdm_ext[4]; |
| 803 | uint8_t tpow_ht20[8], tpow_ht40[8]; |
| 804 | uint8_t ht40inc; |
| 805 | #endif |
| 806 | int16_t power[ATHN_POWER_COUNT]; |
| 807 | int i; |
| 808 | |
| 809 | ar9285_set_power_calib(sc, c); |
| 810 | |
| 811 | #ifdef notyet |
| 812 | /* Compute transmit power reduction due to antenna gain. */ |
| 813 | uint16_t max_ant_gain = modal->antennaGain; |
| 814 | /* XXX */ |
| 815 | #endif |
| 816 | |
| 817 | /* Get CCK target powers. */ |
| 818 | ar5008_get_lg_tpow(sc, c, AR_CTL_11B, eep->calTargetPowerCck, |
| 819 | AR9285_NUM_2G_CCK_TARGET_POWERS, tpow_cck); |
| 820 | |
| 821 | /* Get OFDM target powers. */ |
| 822 | ar5008_get_lg_tpow(sc, c, AR_CTL_11G, eep->calTargetPower2G, |
| 823 | AR9285_NUM_2G_20_TARGET_POWERS, tpow_ofdm); |
| 824 | |
| 825 | #ifndef IEEE80211_NO_HT |
| 826 | /* Get HT-20 target powers. */ |
| 827 | ar5008_get_ht_tpow(sc, c, AR_CTL_2GHT20, eep->calTargetPower2GHT20, |
| 828 | AR9285_NUM_2G_20_TARGET_POWERS, tpow_ht20); |
| 829 | |
| 830 | if (extc != NULL) { |
| 831 | /* Get HT-40 target powers. */ |
| 832 | ar5008_get_ht_tpow(sc, c, AR_CTL_2GHT40, |
| 833 | eep->calTargetPower2GHT40, AR9285_NUM_2G_40_TARGET_POWERS, |
| 834 | tpow_ht40); |
| 835 | |
| 836 | /* Get secondary channel CCK target powers. */ |
| 837 | ar5008_get_lg_tpow(sc, extc, AR_CTL_11B, |
| 838 | eep->calTargetPowerCck, AR9285_NUM_2G_CCK_TARGET_POWERS, |
| 839 | tpow_cck_ext); |
| 840 | |
| 841 | /* Get secondary channel OFDM target powers. */ |
| 842 | ar5008_get_lg_tpow(sc, extc, AR_CTL_11G, |
| 843 | eep->calTargetPower2G, AR9285_NUM_2G_20_TARGET_POWERS, |
| 844 | tpow_ofdm_ext); |
| 845 | } |
| 846 | #endif |
| 847 | |
| 848 | memset(power, 0, sizeof(power)); |
| 849 | /* Shuffle target powers accross transmit rates. */ |
| 850 | power[ATHN_POWER_OFDM6 ] = |
| 851 | power[ATHN_POWER_OFDM9 ] = |
| 852 | power[ATHN_POWER_OFDM12 ] = |
| 853 | power[ATHN_POWER_OFDM18 ] = |
| 854 | power[ATHN_POWER_OFDM24 ] = tpow_ofdm[0]; |
| 855 | power[ATHN_POWER_OFDM36 ] = tpow_ofdm[1]; |
| 856 | power[ATHN_POWER_OFDM48 ] = tpow_ofdm[2]; |
| 857 | power[ATHN_POWER_OFDM54 ] = tpow_ofdm[3]; |
| 858 | power[ATHN_POWER_XR ] = tpow_ofdm[0]; |
| 859 | power[ATHN_POWER_CCK1_LP ] = tpow_cck[0]; |
| 860 | power[ATHN_POWER_CCK2_LP ] = |
| 861 | power[ATHN_POWER_CCK2_SP ] = tpow_cck[1]; |
| 862 | power[ATHN_POWER_CCK55_LP] = |
| 863 | power[ATHN_POWER_CCK55_SP] = tpow_cck[2]; |
| 864 | power[ATHN_POWER_CCK11_LP] = |
| 865 | power[ATHN_POWER_CCK11_SP] = tpow_cck[3]; |
| 866 | #ifndef IEEE80211_NO_HT |
| 867 | for (i = 0; i < __arraycount(tpow_ht20); i++) |
| 868 | power[ATHN_POWER_HT20(i)] = tpow_ht20[i]; |
| 869 | if (extc != NULL) { |
| 870 | /* Correct PAR difference between HT40 and HT20/Legacy. */ |
| 871 | if (sc->sc_eep_rev >= AR_EEP_MINOR_VER_2) |
| 872 | ht40inc = modal->ht40PowerIncForPdadc; |
| 873 | else |
| 874 | ht40inc = AR_HT40_POWER_INC_FOR_PDADC; |
| 875 | for (i = 0; i < __arraycount(tpow_ht40); i++) |
| 876 | power[ATHN_POWER_HT40(i)] = tpow_ht40[i] + ht40inc; |
| 877 | power[ATHN_POWER_OFDM_DUP] = tpow_ht40[0]; |
| 878 | power[ATHN_POWER_CCK_DUP ] = tpow_ht40[0]; |
| 879 | power[ATHN_POWER_OFDM_EXT] = tpow_ofdm_ext[0]; |
| 880 | power[ATHN_POWER_CCK_EXT ] = tpow_cck_ext[0]; |
| 881 | } |
| 882 | #endif |
| 883 | |
| 884 | for (i = 0; i < ATHN_POWER_COUNT; i++) { |
| 885 | power[i] -= AR_PWR_TABLE_OFFSET_DB * 2; /* In half dB. */ |
| 886 | if (power[i] > AR_MAX_RATE_POWER) |
| 887 | power[i] = AR_MAX_RATE_POWER; |
| 888 | } |
| 889 | |
| 890 | /* Commit transmit power values to hardware. */ |
| 891 | ar5008_write_txpower(sc, power); |
| 892 | } |
| 893 | |