1*4b1a56afSjsg /* $OpenBSD: ar5416.c,v 1.23 2022/01/09 05:42:38 jsg Exp $ */
2498e8a28Sdamien
3498e8a28Sdamien /*-
4498e8a28Sdamien * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr>
5498e8a28Sdamien * Copyright (c) 2008-2009 Atheros Communications Inc.
6498e8a28Sdamien *
7498e8a28Sdamien * Permission to use, copy, modify, and/or distribute this software for any
8498e8a28Sdamien * purpose with or without fee is hereby granted, provided that the above
9498e8a28Sdamien * copyright notice and this permission notice appear in all copies.
10498e8a28Sdamien *
11498e8a28Sdamien * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12498e8a28Sdamien * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13498e8a28Sdamien * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14498e8a28Sdamien * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15498e8a28Sdamien * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16498e8a28Sdamien * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17498e8a28Sdamien * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18498e8a28Sdamien */
19498e8a28Sdamien
20498e8a28Sdamien /*
21498e8a28Sdamien * Driver for Atheros 802.11a/g/n chipsets.
22498e8a28Sdamien * Routines for AR5416, AR5418 and AR9160 chipsets.
23498e8a28Sdamien */
24498e8a28Sdamien
25498e8a28Sdamien #include "bpfilter.h"
26498e8a28Sdamien
27498e8a28Sdamien #include <sys/param.h>
28498e8a28Sdamien #include <sys/sockio.h>
29498e8a28Sdamien #include <sys/mbuf.h>
30498e8a28Sdamien #include <sys/kernel.h>
31498e8a28Sdamien #include <sys/socket.h>
32498e8a28Sdamien #include <sys/systm.h>
33498e8a28Sdamien #include <sys/malloc.h>
34498e8a28Sdamien #include <sys/queue.h>
35498e8a28Sdamien #include <sys/timeout.h>
36498e8a28Sdamien #include <sys/conf.h>
37498e8a28Sdamien #include <sys/device.h>
389b18ffb8Sguenther #include <sys/endian.h>
39498e8a28Sdamien
40498e8a28Sdamien #include <machine/bus.h>
41498e8a28Sdamien #include <machine/intr.h>
42498e8a28Sdamien
43498e8a28Sdamien #if NBPFILTER > 0
44498e8a28Sdamien #include <net/bpf.h>
45498e8a28Sdamien #endif
46498e8a28Sdamien #include <net/if.h>
47498e8a28Sdamien #include <net/if_media.h>
48498e8a28Sdamien
49498e8a28Sdamien #include <netinet/in.h>
50498e8a28Sdamien #include <netinet/if_ether.h>
51498e8a28Sdamien
52498e8a28Sdamien #include <net80211/ieee80211_var.h>
53498e8a28Sdamien #include <net80211/ieee80211_amrr.h>
54d2dd70acSstsp #include <net80211/ieee80211_ra.h>
55498e8a28Sdamien #include <net80211/ieee80211_radiotap.h>
56498e8a28Sdamien
57498e8a28Sdamien #include <dev/ic/athnreg.h>
58498e8a28Sdamien #include <dev/ic/athnvar.h>
59498e8a28Sdamien
60bd6ea91dSdamien #include <dev/ic/ar5008reg.h>
61498e8a28Sdamien #include <dev/ic/ar5416reg.h>
62498e8a28Sdamien
63498e8a28Sdamien int ar5416_attach(struct athn_softc *);
64498e8a28Sdamien void ar5416_setup(struct athn_softc *);
65bd6ea91dSdamien void ar5416_swap_rom(struct athn_softc *);
66bd6ea91dSdamien const struct ar_spur_chan *
67bd6ea91dSdamien ar5416_get_spur_chans(struct athn_softc *, int);
68498e8a28Sdamien int ar5416_set_synth(struct athn_softc *, struct ieee80211_channel *,
69498e8a28Sdamien struct ieee80211_channel *);
70bd6ea91dSdamien uint8_t ar5416_reverse_bits(uint8_t, int);
71bd6ea91dSdamien uint8_t ar5416_get_rf_rev(struct athn_softc *);
72498e8a28Sdamien void ar5416_init_from_rom(struct athn_softc *, struct ieee80211_channel *,
73498e8a28Sdamien struct ieee80211_channel *);
74498e8a28Sdamien int ar5416_init_calib(struct athn_softc *, struct ieee80211_channel *,
75498e8a28Sdamien struct ieee80211_channel *);
76498e8a28Sdamien void ar5416_set_power_calib(struct athn_softc *,
77498e8a28Sdamien struct ieee80211_channel *);
78bd6ea91dSdamien void ar5416_set_txpower(struct athn_softc *, struct ieee80211_channel *,
79bd6ea91dSdamien struct ieee80211_channel *);
80498e8a28Sdamien void ar5416_spur_mitigate(struct athn_softc *, struct ieee80211_channel *,
81498e8a28Sdamien struct ieee80211_channel *);
82498e8a28Sdamien void ar5416_rw_rfbits(uint32_t *, int, int, uint32_t, int);
83498e8a28Sdamien void ar5416_rw_bank6tpc(struct athn_softc *, struct ieee80211_channel *,
84498e8a28Sdamien uint32_t *);
85498e8a28Sdamien void ar5416_rf_reset(struct athn_softc *, struct ieee80211_channel *);
86498e8a28Sdamien void ar5416_reset_bb_gain(struct athn_softc *, struct ieee80211_channel *);
87498e8a28Sdamien void ar5416_force_bias(struct athn_softc *, struct ieee80211_channel *);
88498e8a28Sdamien void ar9160_rw_addac(struct athn_softc *, struct ieee80211_channel *,
89498e8a28Sdamien uint32_t *);
90498e8a28Sdamien void ar5416_reset_addac(struct athn_softc *, struct ieee80211_channel *);
91bd6ea91dSdamien void ar5416_get_pdadcs(struct athn_softc *, struct ieee80211_channel *,
92bd6ea91dSdamien int, int, uint8_t, uint8_t *, uint8_t *);
93bd6ea91dSdamien
94bd6ea91dSdamien /* Extern functions. */
95bd6ea91dSdamien uint8_t athn_chan2fbin(struct ieee80211_channel *);
96bd6ea91dSdamien void athn_get_pier_ival(uint8_t, const uint8_t *, int, int *, int *);
97bd6ea91dSdamien int ar5008_attach(struct athn_softc *);
98bd6ea91dSdamien void ar5008_write_txpower(struct athn_softc *, int16_t power[]);
99bd6ea91dSdamien void ar5008_get_pdadcs(struct athn_softc *, uint8_t, struct athn_pier *,
100bd6ea91dSdamien struct athn_pier *, int, int, uint8_t, uint8_t *, uint8_t *);
101bd6ea91dSdamien void ar5008_set_viterbi_mask(struct athn_softc *, int);
102bd6ea91dSdamien void ar5008_get_lg_tpow(struct athn_softc *, struct ieee80211_channel *,
103bd6ea91dSdamien uint8_t, const struct ar_cal_target_power_leg *, int, uint8_t[]);
104bd6ea91dSdamien void ar5008_get_ht_tpow(struct athn_softc *, struct ieee80211_channel *,
105bd6ea91dSdamien uint8_t, const struct ar_cal_target_power_ht *, int, uint8_t[]);
10691defb09Sdamien void ar9280_olpc_get_pdadcs(struct athn_softc *, struct ieee80211_channel *,
10791defb09Sdamien int, uint8_t *, uint8_t *, uint8_t *);
108bd6ea91dSdamien
109498e8a28Sdamien
110498e8a28Sdamien int
ar5416_attach(struct athn_softc * sc)111498e8a28Sdamien ar5416_attach(struct athn_softc *sc)
112498e8a28Sdamien {
113498e8a28Sdamien sc->eep_base = AR5416_EEP_START_LOC;
114498e8a28Sdamien sc->eep_size = sizeof(struct ar5416_eeprom);
115498e8a28Sdamien sc->ngpiopins = 14;
1169feb3d58Sdamien sc->led_pin = 1;
117498e8a28Sdamien sc->workaround = AR5416_WA_DEFAULT;
118498e8a28Sdamien sc->ops.setup = ar5416_setup;
119498e8a28Sdamien sc->ops.swap_rom = ar5416_swap_rom;
120498e8a28Sdamien sc->ops.init_from_rom = ar5416_init_from_rom;
121498e8a28Sdamien sc->ops.set_txpower = ar5416_set_txpower;
122498e8a28Sdamien sc->ops.set_synth = ar5416_set_synth;
123498e8a28Sdamien sc->ops.spur_mitigate = ar5416_spur_mitigate;
124498e8a28Sdamien sc->ops.get_spur_chans = ar5416_get_spur_chans;
1259d1f2812Sstsp sc->cca_min_2g = AR5416_PHY_CCA_MIN_GOOD_VAL_2GHZ;
1269d1f2812Sstsp sc->cca_max_2g = AR5416_PHY_CCA_MAX_GOOD_VAL_2GHZ;
1279d1f2812Sstsp sc->cca_min_5g = AR5416_PHY_CCA_MIN_GOOD_VAL_5GHZ;
1289d1f2812Sstsp sc->cca_max_5g = AR5416_PHY_CCA_MAX_GOOD_VAL_5GHZ;
129498e8a28Sdamien if (AR_SREV_9160_10_OR_LATER(sc))
130498e8a28Sdamien sc->ini = &ar9160_ini;
131498e8a28Sdamien else
132498e8a28Sdamien sc->ini = &ar5416_ini;
133328b15b2Skettenis sc->serdes = &ar5416_serdes;
134bd6ea91dSdamien
135bd6ea91dSdamien return (ar5008_attach(sc));
136498e8a28Sdamien }
137498e8a28Sdamien
138498e8a28Sdamien void
ar5416_setup(struct athn_softc * sc)139498e8a28Sdamien ar5416_setup(struct athn_softc *sc)
140498e8a28Sdamien {
141498e8a28Sdamien /* Select ADDAC programming. */
142498e8a28Sdamien if (AR_SREV_9160_11(sc))
143498e8a28Sdamien sc->addac = &ar9160_1_1_addac;
144498e8a28Sdamien else if (AR_SREV_9160_10_OR_LATER(sc))
145498e8a28Sdamien sc->addac = &ar9160_1_0_addac;
146498e8a28Sdamien else if (AR_SREV_5416_22_OR_LATER(sc))
147498e8a28Sdamien sc->addac = &ar5416_2_2_addac;
148498e8a28Sdamien else
149498e8a28Sdamien sc->addac = &ar5416_2_1_addac;
150498e8a28Sdamien }
151498e8a28Sdamien
152498e8a28Sdamien void
ar5416_swap_rom(struct athn_softc * sc)153498e8a28Sdamien ar5416_swap_rom(struct athn_softc *sc)
154498e8a28Sdamien {
155498e8a28Sdamien struct ar5416_eeprom *eep = sc->eep;
156498e8a28Sdamien struct ar5416_modal_eep_header *modal;
157498e8a28Sdamien int i, j;
158498e8a28Sdamien
159498e8a28Sdamien for (i = 0; i < 2; i++) { /* Dual-band. */
160498e8a28Sdamien modal = &eep->modalHeader[i];
161498e8a28Sdamien
162498e8a28Sdamien modal->antCtrlCommon = swap32(modal->antCtrlCommon);
163498e8a28Sdamien for (j = 0; j < AR5416_MAX_CHAINS; j++) {
164498e8a28Sdamien modal->antCtrlChain[j] =
165498e8a28Sdamien swap32(modal->antCtrlChain[j]);
166498e8a28Sdamien }
167498e8a28Sdamien for (j = 0; j < AR_EEPROM_MODAL_SPURS; j++) {
168498e8a28Sdamien modal->spurChans[j].spurChan =
169498e8a28Sdamien swap16(modal->spurChans[j].spurChan);
170498e8a28Sdamien }
171498e8a28Sdamien }
172498e8a28Sdamien }
173498e8a28Sdamien
174498e8a28Sdamien const struct ar_spur_chan *
ar5416_get_spur_chans(struct athn_softc * sc,int is2ghz)175498e8a28Sdamien ar5416_get_spur_chans(struct athn_softc *sc, int is2ghz)
176498e8a28Sdamien {
177498e8a28Sdamien const struct ar5416_eeprom *eep = sc->eep;
178498e8a28Sdamien
179bd6ea91dSdamien return (eep->modalHeader[is2ghz].spurChans);
180498e8a28Sdamien }
181498e8a28Sdamien
182498e8a28Sdamien int
ar5416_set_synth(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)183498e8a28Sdamien ar5416_set_synth(struct athn_softc *sc, struct ieee80211_channel *c,
184498e8a28Sdamien struct ieee80211_channel *extc)
185498e8a28Sdamien {
186498e8a28Sdamien uint32_t phy, reg;
187498e8a28Sdamien uint32_t freq = c->ic_freq;
188498e8a28Sdamien uint8_t chansel;
189498e8a28Sdamien
190498e8a28Sdamien phy = 0;
191498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c)) {
192498e8a28Sdamien if (((freq - 2192) % 5) == 0) {
193498e8a28Sdamien chansel = ((freq - 672) * 2 - 3040) / 10;
194498e8a28Sdamien } else if (((freq - 2224) % 5) == 0) {
195498e8a28Sdamien chansel = ((freq - 704) * 2 - 3040) / 10;
196498e8a28Sdamien phy |= AR5416_BMODE_SYNTH;
197498e8a28Sdamien } else
198498e8a28Sdamien return (EINVAL);
199498e8a28Sdamien chansel <<= 2;
200498e8a28Sdamien
201498e8a28Sdamien reg = AR_READ(sc, AR_PHY_CCK_TX_CTRL);
202498e8a28Sdamien if (freq == 2484) /* Channel 14. */
203498e8a28Sdamien reg |= AR_PHY_CCK_TX_CTRL_JAPAN;
204498e8a28Sdamien else
205498e8a28Sdamien reg &= ~AR_PHY_CCK_TX_CTRL_JAPAN;
206498e8a28Sdamien AR_WRITE(sc, AR_PHY_CCK_TX_CTRL, reg);
207498e8a28Sdamien
208498e8a28Sdamien /* Fix for orientation sensitivity issue. */
209498e8a28Sdamien if (AR_SREV_5416(sc))
210498e8a28Sdamien ar5416_force_bias(sc, c);
211498e8a28Sdamien } else {
212498e8a28Sdamien if (freq >= 5120 && (freq % 20) == 0) {
213498e8a28Sdamien chansel = (freq - 4800) / 20;
214498e8a28Sdamien chansel <<= 2;
215498e8a28Sdamien phy |= SM(AR5416_AMODE_REFSEL, 2);
216498e8a28Sdamien } else if ((freq % 10) == 0) {
217498e8a28Sdamien chansel = (freq - 4800) / 10;
218498e8a28Sdamien chansel <<= 1;
219498e8a28Sdamien if (AR_SREV_9160_10_OR_LATER(sc))
220498e8a28Sdamien phy |= SM(AR5416_AMODE_REFSEL, 1);
221498e8a28Sdamien else
222498e8a28Sdamien phy |= SM(AR5416_AMODE_REFSEL, 2);
223498e8a28Sdamien } else if ((freq % 5) == 0) {
224498e8a28Sdamien chansel = (freq - 4800) / 5;
225498e8a28Sdamien phy |= SM(AR5416_AMODE_REFSEL, 2);
226498e8a28Sdamien } else
227498e8a28Sdamien return (EINVAL);
228498e8a28Sdamien }
229bd6ea91dSdamien chansel = ar5416_reverse_bits(chansel, 8);
230498e8a28Sdamien phy |= chansel << 8 | 1 << 5 | 1;
231a67c32bdSdamien DPRINTFN(4, ("AR_PHY(0x37)=0x%08x\n", phy));
232498e8a28Sdamien AR_WRITE(sc, AR_PHY(0x37), phy);
233498e8a28Sdamien return (0);
234498e8a28Sdamien }
235498e8a28Sdamien
236498e8a28Sdamien void
ar5416_init_from_rom(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)237498e8a28Sdamien ar5416_init_from_rom(struct athn_softc *sc, struct ieee80211_channel *c,
238498e8a28Sdamien struct ieee80211_channel *extc)
239498e8a28Sdamien {
240498e8a28Sdamien static const uint32_t chainoffset[] = { 0x0000, 0x2000, 0x1000 };
241498e8a28Sdamien const struct ar5416_eeprom *eep = sc->eep;
242498e8a28Sdamien const struct ar5416_modal_eep_header *modal;
243498e8a28Sdamien uint32_t reg, offset;
244498e8a28Sdamien uint8_t txRxAtten;
245498e8a28Sdamien int i;
246498e8a28Sdamien
247498e8a28Sdamien modal = &eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(c)];
248498e8a28Sdamien
24980a670fbSdamien AR_WRITE(sc, AR_PHY_SWITCH_COM, modal->antCtrlCommon);
250498e8a28Sdamien
251498e8a28Sdamien for (i = 0; i < AR5416_MAX_CHAINS; i++) {
252498e8a28Sdamien if (AR_SREV_5416_20_OR_LATER(sc) &&
253498e8a28Sdamien (sc->rxchainmask == 0x5 || sc->txchainmask == 0x5))
254498e8a28Sdamien offset = chainoffset[i];
255498e8a28Sdamien else
256498e8a28Sdamien offset = i * 0x1000;
257498e8a28Sdamien
258498e8a28Sdamien AR_WRITE(sc, AR_PHY_SWITCH_CHAIN_0 + offset,
259498e8a28Sdamien modal->antCtrlChain[i]);
260498e8a28Sdamien
261498e8a28Sdamien reg = AR_READ(sc, AR_PHY_TIMING_CTRL4_0 + offset);
262498e8a28Sdamien reg = RW(reg, AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF,
263498e8a28Sdamien modal->iqCalICh[i]);
264498e8a28Sdamien reg = RW(reg, AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF,
265498e8a28Sdamien modal->iqCalQCh[i]);
266498e8a28Sdamien AR_WRITE(sc, AR_PHY_TIMING_CTRL4_0 + offset, reg);
267498e8a28Sdamien
268498e8a28Sdamien if (i > 0 && !AR_SREV_5416_20_OR_LATER(sc))
269498e8a28Sdamien continue;
270498e8a28Sdamien
271498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_3) {
272498e8a28Sdamien reg = AR_READ(sc, AR_PHY_GAIN_2GHZ + offset);
273498e8a28Sdamien reg = RW(reg, AR_PHY_GAIN_2GHZ_BSW_MARGIN,
274498e8a28Sdamien modal->bswMargin[i]);
275498e8a28Sdamien reg = RW(reg, AR_PHY_GAIN_2GHZ_BSW_ATTEN,
276498e8a28Sdamien modal->bswAtten[i]);
277498e8a28Sdamien AR_WRITE(sc, AR_PHY_GAIN_2GHZ + offset, reg);
278498e8a28Sdamien }
279498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_3)
280498e8a28Sdamien txRxAtten = modal->txRxAttenCh[i];
281498e8a28Sdamien else /* Workaround for ROM versions < 14.3. */
282498e8a28Sdamien txRxAtten = IEEE80211_IS_CHAN_2GHZ(c) ? 23 : 44;
283498e8a28Sdamien reg = AR_READ(sc, AR_PHY_RXGAIN + offset);
284498e8a28Sdamien reg = RW(reg, AR_PHY_RXGAIN_TXRX_ATTEN, txRxAtten);
285498e8a28Sdamien AR_WRITE(sc, AR_PHY_RXGAIN + offset, reg);
286498e8a28Sdamien
287498e8a28Sdamien reg = AR_READ(sc, AR_PHY_GAIN_2GHZ + offset);
288498e8a28Sdamien reg = RW(reg, AR_PHY_GAIN_2GHZ_RXTX_MARGIN,
289498e8a28Sdamien modal->rxTxMarginCh[i]);
290498e8a28Sdamien AR_WRITE(sc, AR_PHY_GAIN_2GHZ + offset, reg);
291498e8a28Sdamien }
292498e8a28Sdamien reg = AR_READ(sc, AR_PHY_SETTLING);
293498e8a28Sdamien reg = RW(reg, AR_PHY_SETTLING_SWITCH, modal->switchSettling);
294498e8a28Sdamien AR_WRITE(sc, AR_PHY_SETTLING, reg);
295498e8a28Sdamien
296498e8a28Sdamien reg = AR_READ(sc, AR_PHY_DESIRED_SZ);
297498e8a28Sdamien reg = RW(reg, AR_PHY_DESIRED_SZ_ADC, modal->adcDesiredSize);
298498e8a28Sdamien reg = RW(reg, AR_PHY_DESIRED_SZ_PGA, modal->pgaDesiredSize);
299498e8a28Sdamien AR_WRITE(sc, AR_PHY_DESIRED_SZ, reg);
300498e8a28Sdamien
301498e8a28Sdamien reg = SM(AR_PHY_RF_CTL4_TX_END_XPAA_OFF, modal->txEndToXpaOff);
302498e8a28Sdamien reg |= SM(AR_PHY_RF_CTL4_TX_END_XPAB_OFF, modal->txEndToXpaOff);
303498e8a28Sdamien reg |= SM(AR_PHY_RF_CTL4_FRAME_XPAA_ON, modal->txFrameToXpaOn);
304498e8a28Sdamien reg |= SM(AR_PHY_RF_CTL4_FRAME_XPAB_ON, modal->txFrameToXpaOn);
305498e8a28Sdamien AR_WRITE(sc, AR_PHY_RF_CTL4, reg);
306498e8a28Sdamien
307498e8a28Sdamien reg = AR_READ(sc, AR_PHY_RF_CTL3);
308498e8a28Sdamien reg = RW(reg, AR_PHY_TX_END_TO_A2_RX_ON, modal->txEndToRxOn);
309498e8a28Sdamien AR_WRITE(sc, AR_PHY_RF_CTL3, reg);
310498e8a28Sdamien
311498e8a28Sdamien reg = AR_READ(sc, AR_PHY_CCA(0));
312498e8a28Sdamien reg = RW(reg, AR_PHY_CCA_THRESH62, modal->thresh62);
313498e8a28Sdamien AR_WRITE(sc, AR_PHY_CCA(0), reg);
314498e8a28Sdamien
315498e8a28Sdamien reg = AR_READ(sc, AR_PHY_EXT_CCA(0));
316498e8a28Sdamien reg = RW(reg, AR_PHY_EXT_CCA_THRESH62, modal->thresh62);
317498e8a28Sdamien AR_WRITE(sc, AR_PHY_EXT_CCA(0), reg);
318498e8a28Sdamien
319498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_2) {
320498e8a28Sdamien reg = AR_READ(sc, AR_PHY_RF_CTL2);
321498e8a28Sdamien reg = RW(reg, AR_PHY_TX_END_DATA_START,
322498e8a28Sdamien modal->txFrameToDataStart);
323498e8a28Sdamien reg = RW(reg, AR_PHY_TX_END_PA_ON, modal->txFrameToPaOn);
324498e8a28Sdamien AR_WRITE(sc, AR_PHY_RF_CTL2, reg);
325498e8a28Sdamien }
326498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_3 && extc != NULL) {
327498e8a28Sdamien /* Overwrite switch settling with HT-40 value. */
328498e8a28Sdamien reg = AR_READ(sc, AR_PHY_SETTLING);
329498e8a28Sdamien reg = RW(reg, AR_PHY_SETTLING_SWITCH, modal->swSettleHt40);
330498e8a28Sdamien AR_WRITE(sc, AR_PHY_SETTLING, reg);
331498e8a28Sdamien }
332498e8a28Sdamien }
333498e8a28Sdamien
334498e8a28Sdamien int
ar5416_init_calib(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)335498e8a28Sdamien ar5416_init_calib(struct athn_softc *sc, struct ieee80211_channel *c,
336498e8a28Sdamien struct ieee80211_channel *extc)
337498e8a28Sdamien {
338498e8a28Sdamien int ntries;
339498e8a28Sdamien
340498e8a28Sdamien if (AR_SREV_9280_10_OR_LATER(sc)) {
341498e8a28Sdamien /* XXX Linux tests AR9287?! */
342498e8a28Sdamien AR_CLRBITS(sc, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC);
343498e8a28Sdamien AR_SETBITS(sc, AR_PHY_AGC_CONTROL,
344498e8a28Sdamien AR_PHY_AGC_CONTROL_FLTR_CAL);
345498e8a28Sdamien }
346498e8a28Sdamien /* Calibrate the AGC. */
347498e8a28Sdamien AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
348498e8a28Sdamien /* Poll for offset calibration completion. */
349498e8a28Sdamien for (ntries = 0; ntries < 10000; ntries++) {
350498e8a28Sdamien if (!(AR_READ(sc, AR_PHY_AGC_CONTROL) &
351498e8a28Sdamien AR_PHY_AGC_CONTROL_CAL))
352498e8a28Sdamien break;
353498e8a28Sdamien DELAY(10);
354498e8a28Sdamien }
355498e8a28Sdamien if (ntries == 10000)
356498e8a28Sdamien return (ETIMEDOUT);
357498e8a28Sdamien if (AR_SREV_9280_10_OR_LATER(sc)) {
358498e8a28Sdamien AR_SETBITS(sc, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC);
359498e8a28Sdamien AR_CLRBITS(sc, AR_PHY_AGC_CONTROL,
360498e8a28Sdamien AR_PHY_AGC_CONTROL_FLTR_CAL);
361498e8a28Sdamien }
362498e8a28Sdamien return (0);
363498e8a28Sdamien }
364498e8a28Sdamien
365498e8a28Sdamien void
ar5416_get_pdadcs(struct athn_softc * sc,struct ieee80211_channel * c,int chain,int nxpdgains,uint8_t overlap,uint8_t * boundaries,uint8_t * pdadcs)366498e8a28Sdamien ar5416_get_pdadcs(struct athn_softc *sc, struct ieee80211_channel *c,
367498e8a28Sdamien int chain, int nxpdgains, uint8_t overlap, uint8_t *boundaries,
368498e8a28Sdamien uint8_t *pdadcs)
369498e8a28Sdamien {
370498e8a28Sdamien const struct ar5416_eeprom *eep = sc->eep;
371498e8a28Sdamien const struct ar5416_cal_data_per_freq *pierdata;
372498e8a28Sdamien const uint8_t *pierfreq;
373498e8a28Sdamien struct athn_pier lopier, hipier;
374498e8a28Sdamien int16_t delta;
375498e8a28Sdamien uint8_t fbin, pwroff;
376498e8a28Sdamien int i, lo, hi, npiers;
377498e8a28Sdamien
378498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c)) {
379498e8a28Sdamien pierfreq = eep->calFreqPier2G;
380498e8a28Sdamien pierdata = eep->calPierData2G[chain];
381498e8a28Sdamien npiers = AR5416_NUM_2G_CAL_PIERS;
382498e8a28Sdamien } else {
383498e8a28Sdamien pierfreq = eep->calFreqPier5G;
384498e8a28Sdamien pierdata = eep->calPierData5G[chain];
385498e8a28Sdamien npiers = AR5416_NUM_5G_CAL_PIERS;
386498e8a28Sdamien }
387498e8a28Sdamien /* Find channel in ROM pier table. */
388498e8a28Sdamien fbin = athn_chan2fbin(c);
389498e8a28Sdamien athn_get_pier_ival(fbin, pierfreq, npiers, &lo, &hi);
390498e8a28Sdamien
391498e8a28Sdamien lopier.fbin = pierfreq[lo];
392498e8a28Sdamien hipier.fbin = pierfreq[hi];
393498e8a28Sdamien for (i = 0; i < nxpdgains; i++) {
394498e8a28Sdamien lopier.pwr[i] = pierdata[lo].pwrPdg[i];
395498e8a28Sdamien lopier.vpd[i] = pierdata[lo].vpdPdg[i];
396498e8a28Sdamien hipier.pwr[i] = pierdata[lo].pwrPdg[i];
397498e8a28Sdamien hipier.vpd[i] = pierdata[lo].vpdPdg[i];
398498e8a28Sdamien }
399bd6ea91dSdamien ar5008_get_pdadcs(sc, fbin, &lopier, &hipier, nxpdgains,
400498e8a28Sdamien AR5416_PD_GAIN_ICEPTS, overlap, boundaries, pdadcs);
401498e8a28Sdamien
402498e8a28Sdamien if (!AR_SREV_9280_20_OR_LATER(sc))
403498e8a28Sdamien return;
404498e8a28Sdamien
405498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_21)
406498e8a28Sdamien pwroff = eep->baseEepHeader.pwrTableOffset;
407498e8a28Sdamien else
408498e8a28Sdamien pwroff = AR_PWR_TABLE_OFFSET_DB;
409498e8a28Sdamien delta = (pwroff - AR_PWR_TABLE_OFFSET_DB) * 2; /* In half dB. */
410498e8a28Sdamien
411498e8a28Sdamien /* Change the original gain boundaries setting. */
412498e8a28Sdamien for (i = 0; i < nxpdgains; i++) {
413498e8a28Sdamien /* XXX Possible overflows? */
414498e8a28Sdamien boundaries[i] -= delta;
415498e8a28Sdamien if (boundaries[i] > AR_MAX_RATE_POWER - overlap)
416498e8a28Sdamien boundaries[i] = AR_MAX_RATE_POWER - overlap;
417498e8a28Sdamien }
418498e8a28Sdamien if (delta != 0) {
419498e8a28Sdamien /* Shift the PDADC table to start at the new offset. */
420498e8a28Sdamien for (i = 0; i < AR_NUM_PDADC_VALUES; i++)
421498e8a28Sdamien pdadcs[i] = pdadcs[MIN(i + delta,
422498e8a28Sdamien AR_NUM_PDADC_VALUES - 1)];
423498e8a28Sdamien }
424498e8a28Sdamien }
425498e8a28Sdamien
426498e8a28Sdamien void
ar5416_set_power_calib(struct athn_softc * sc,struct ieee80211_channel * c)427498e8a28Sdamien ar5416_set_power_calib(struct athn_softc *sc, struct ieee80211_channel *c)
428498e8a28Sdamien {
429498e8a28Sdamien static const uint32_t chainoffset[] = { 0x0000, 0x2000, 0x1000 };
430498e8a28Sdamien const struct ar5416_eeprom *eep = sc->eep;
431498e8a28Sdamien const struct ar5416_modal_eep_header *modal;
432498e8a28Sdamien uint8_t boundaries[AR_PD_GAINS_IN_MASK];
433498e8a28Sdamien uint8_t pdadcs[AR_NUM_PDADC_VALUES];
434498e8a28Sdamien uint8_t xpdgains[AR5416_NUM_PD_GAINS];
435498e8a28Sdamien uint8_t overlap, txgain;
436498e8a28Sdamien uint32_t reg, offset;
437498e8a28Sdamien int i, j, nxpdgains;
438498e8a28Sdamien
439498e8a28Sdamien modal = &eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(c)];
440498e8a28Sdamien
441498e8a28Sdamien if (sc->eep_rev < AR_EEP_MINOR_VER_2) {
442498e8a28Sdamien overlap = MS(AR_READ(sc, AR_PHY_TPCRG5),
443498e8a28Sdamien AR_PHY_TPCRG5_PD_GAIN_OVERLAP);
444498e8a28Sdamien } else
445498e8a28Sdamien overlap = modal->pdGainOverlap;
446498e8a28Sdamien
447498e8a28Sdamien if ((sc->flags & ATHN_FLAG_OLPC) && IEEE80211_IS_CHAN_2GHZ(c)) {
448498e8a28Sdamien /* XXX not here. */
449498e8a28Sdamien sc->pdadc =
450498e8a28Sdamien ((const struct ar_cal_data_per_freq_olpc *)
451498e8a28Sdamien eep->calPierData2G[0])->vpdPdg[0][0];
452498e8a28Sdamien }
453498e8a28Sdamien
454498e8a28Sdamien nxpdgains = 0;
455bd6ea91dSdamien memset(xpdgains, 0, sizeof(xpdgains));
456498e8a28Sdamien for (i = AR5416_PD_GAINS_IN_MASK - 1; i >= 0; i--) {
457498e8a28Sdamien if (nxpdgains >= AR5416_NUM_PD_GAINS)
458498e8a28Sdamien break; /* Can't happen. */
459498e8a28Sdamien if (modal->xpdGain & (1 << i))
460498e8a28Sdamien xpdgains[nxpdgains++] = i;
461498e8a28Sdamien }
462498e8a28Sdamien reg = AR_READ(sc, AR_PHY_TPCRG1);
463498e8a28Sdamien reg = RW(reg, AR_PHY_TPCRG1_NUM_PD_GAIN, nxpdgains - 1);
464498e8a28Sdamien reg = RW(reg, AR_PHY_TPCRG1_PD_GAIN_1, xpdgains[0]);
465498e8a28Sdamien reg = RW(reg, AR_PHY_TPCRG1_PD_GAIN_2, xpdgains[1]);
466498e8a28Sdamien reg = RW(reg, AR_PHY_TPCRG1_PD_GAIN_3, xpdgains[2]);
467498e8a28Sdamien AR_WRITE(sc, AR_PHY_TPCRG1, reg);
468498e8a28Sdamien
469498e8a28Sdamien for (i = 0; i < AR5416_MAX_CHAINS; i++) {
470498e8a28Sdamien if (!(sc->txchainmask & (1 << i)))
471498e8a28Sdamien continue;
472498e8a28Sdamien
473498e8a28Sdamien if (AR_SREV_5416_20_OR_LATER(sc) &&
474498e8a28Sdamien (sc->rxchainmask == 0x5 || sc->txchainmask == 0x5))
475498e8a28Sdamien offset = chainoffset[i];
476498e8a28Sdamien else
477498e8a28Sdamien offset = i * 0x1000;
478498e8a28Sdamien
479498e8a28Sdamien if (sc->flags & ATHN_FLAG_OLPC) {
48091defb09Sdamien ar9280_olpc_get_pdadcs(sc, c, i, boundaries,
481498e8a28Sdamien pdadcs, &txgain);
482498e8a28Sdamien
483498e8a28Sdamien reg = AR_READ(sc, AR_PHY_TX_PWRCTRL6_0);
484498e8a28Sdamien reg = RW(reg, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
485498e8a28Sdamien AR_WRITE(sc, AR_PHY_TX_PWRCTRL6_0, reg);
486498e8a28Sdamien
487498e8a28Sdamien reg = AR_READ(sc, AR_PHY_TX_PWRCTRL6_1);
488498e8a28Sdamien reg = RW(reg, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
489498e8a28Sdamien AR_WRITE(sc, AR_PHY_TX_PWRCTRL6_1, reg);
490498e8a28Sdamien
491498e8a28Sdamien reg = AR_READ(sc, AR_PHY_TX_PWRCTRL7);
492498e8a28Sdamien reg = RW(reg, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, txgain);
493498e8a28Sdamien AR_WRITE(sc, AR_PHY_TX_PWRCTRL7, reg);
494498e8a28Sdamien
495498e8a28Sdamien overlap = 6;
496498e8a28Sdamien } else {
497498e8a28Sdamien ar5416_get_pdadcs(sc, c, i, nxpdgains, overlap,
498498e8a28Sdamien boundaries, pdadcs);
499498e8a28Sdamien }
500498e8a28Sdamien /* Write boundaries. */
501498e8a28Sdamien if (i == 0 || AR_SREV_5416_20_OR_LATER(sc)) {
502498e8a28Sdamien reg = SM(AR_PHY_TPCRG5_PD_GAIN_OVERLAP,
503498e8a28Sdamien overlap);
504498e8a28Sdamien reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1,
505498e8a28Sdamien boundaries[0]);
506498e8a28Sdamien reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2,
507498e8a28Sdamien boundaries[1]);
508498e8a28Sdamien reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3,
509498e8a28Sdamien boundaries[2]);
510498e8a28Sdamien reg |= SM(AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4,
511498e8a28Sdamien boundaries[3]);
512498e8a28Sdamien AR_WRITE(sc, AR_PHY_TPCRG5 + offset, reg);
513498e8a28Sdamien }
514498e8a28Sdamien /* Write PDADC values. */
515498e8a28Sdamien for (j = 0; j < AR_NUM_PDADC_VALUES; j += 4) {
516498e8a28Sdamien AR_WRITE(sc, AR_PHY_PDADC_TBL_BASE + offset + j,
517498e8a28Sdamien pdadcs[j + 0] << 0 |
518498e8a28Sdamien pdadcs[j + 1] << 8 |
519498e8a28Sdamien pdadcs[j + 2] << 16 |
520498e8a28Sdamien pdadcs[j + 3] << 24);
521498e8a28Sdamien }
522498e8a28Sdamien }
523498e8a28Sdamien }
524498e8a28Sdamien
525498e8a28Sdamien void
ar5416_set_txpower(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)526498e8a28Sdamien ar5416_set_txpower(struct athn_softc *sc, struct ieee80211_channel *c,
527498e8a28Sdamien struct ieee80211_channel *extc)
528498e8a28Sdamien {
529498e8a28Sdamien const struct ar5416_eeprom *eep = sc->eep;
530498e8a28Sdamien const struct ar5416_modal_eep_header *modal;
531498e8a28Sdamien uint8_t tpow_cck[4], tpow_ofdm[4];
532498e8a28Sdamien uint8_t tpow_cck_ext[4], tpow_ofdm_ext[4];
533498e8a28Sdamien uint8_t tpow_ht20[8], tpow_ht40[8];
5342224219cSderaadt uint8_t ht40inc;
53597e63874Skrw int16_t pwr = 0, pwroff, max_ant_gain, power[ATHN_POWER_COUNT];
536bd6ea91dSdamien uint8_t cckinc;
537498e8a28Sdamien int i;
538498e8a28Sdamien
539498e8a28Sdamien ar5416_set_power_calib(sc, c);
540498e8a28Sdamien
541498e8a28Sdamien modal = &eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(c)];
542498e8a28Sdamien
543498e8a28Sdamien /* Compute transmit power reduction due to antenna gain. */
544498e8a28Sdamien max_ant_gain = MAX(modal->antennaGainCh[0], modal->antennaGainCh[1]);
545498e8a28Sdamien max_ant_gain = MAX(modal->antennaGainCh[2], max_ant_gain);
546498e8a28Sdamien /* XXX */
547498e8a28Sdamien
548498e8a28Sdamien /*
549498e8a28Sdamien * Reduce scaled power by number of active chains to get per-chain
550498e8a28Sdamien * transmit power level.
551498e8a28Sdamien */
552498e8a28Sdamien if (sc->ntxchains == 2)
553498e8a28Sdamien pwr -= AR_PWR_DECREASE_FOR_2_CHAIN;
554498e8a28Sdamien else if (sc->ntxchains == 3)
555498e8a28Sdamien pwr -= AR_PWR_DECREASE_FOR_3_CHAIN;
556498e8a28Sdamien if (pwr < 0)
557498e8a28Sdamien pwr = 0;
558498e8a28Sdamien
559498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c)) {
560498e8a28Sdamien /* Get CCK target powers. */
561bd6ea91dSdamien ar5008_get_lg_tpow(sc, c, AR_CTL_11B, eep->calTargetPowerCck,
562498e8a28Sdamien AR5416_NUM_2G_CCK_TARGET_POWERS, tpow_cck);
563498e8a28Sdamien
564498e8a28Sdamien /* Get OFDM target powers. */
565bd6ea91dSdamien ar5008_get_lg_tpow(sc, c, AR_CTL_11G, eep->calTargetPower2G,
566498e8a28Sdamien AR5416_NUM_2G_20_TARGET_POWERS, tpow_ofdm);
567498e8a28Sdamien
568498e8a28Sdamien /* Get HT-20 target powers. */
569bd6ea91dSdamien ar5008_get_ht_tpow(sc, c, AR_CTL_2GHT20,
570498e8a28Sdamien eep->calTargetPower2GHT20, AR5416_NUM_2G_20_TARGET_POWERS,
571498e8a28Sdamien tpow_ht20);
572498e8a28Sdamien
573498e8a28Sdamien if (extc != NULL) {
574498e8a28Sdamien /* Get HT-40 target powers. */
575bd6ea91dSdamien ar5008_get_ht_tpow(sc, c, AR_CTL_2GHT40,
576498e8a28Sdamien eep->calTargetPower2GHT40,
577498e8a28Sdamien AR5416_NUM_2G_40_TARGET_POWERS, tpow_ht40);
578498e8a28Sdamien
579498e8a28Sdamien /* Get secondary channel CCK target powers. */
580bd6ea91dSdamien ar5008_get_lg_tpow(sc, extc, AR_CTL_11B,
581498e8a28Sdamien eep->calTargetPowerCck,
582498e8a28Sdamien AR5416_NUM_2G_CCK_TARGET_POWERS, tpow_cck_ext);
583498e8a28Sdamien
584498e8a28Sdamien /* Get secondary channel OFDM target powers. */
585bd6ea91dSdamien ar5008_get_lg_tpow(sc, extc, AR_CTL_11G,
586498e8a28Sdamien eep->calTargetPower2G,
587498e8a28Sdamien AR5416_NUM_2G_20_TARGET_POWERS, tpow_ofdm_ext);
588498e8a28Sdamien }
589498e8a28Sdamien } else {
590498e8a28Sdamien /* Get OFDM target powers. */
591bd6ea91dSdamien ar5008_get_lg_tpow(sc, c, AR_CTL_11A, eep->calTargetPower5G,
592498e8a28Sdamien AR5416_NUM_5G_20_TARGET_POWERS, tpow_ofdm);
593498e8a28Sdamien
594498e8a28Sdamien /* Get HT-20 target powers. */
595bd6ea91dSdamien ar5008_get_ht_tpow(sc, c, AR_CTL_5GHT20,
596498e8a28Sdamien eep->calTargetPower5GHT20, AR5416_NUM_5G_20_TARGET_POWERS,
597498e8a28Sdamien tpow_ht20);
598498e8a28Sdamien
599498e8a28Sdamien if (extc != NULL) {
600498e8a28Sdamien /* Get HT-40 target powers. */
601bd6ea91dSdamien ar5008_get_ht_tpow(sc, c, AR_CTL_5GHT40,
602498e8a28Sdamien eep->calTargetPower5GHT40,
603498e8a28Sdamien AR5416_NUM_5G_40_TARGET_POWERS, tpow_ht40);
604498e8a28Sdamien
605498e8a28Sdamien /* Get secondary channel OFDM target powers. */
606bd6ea91dSdamien ar5008_get_lg_tpow(sc, extc, AR_CTL_11A,
607498e8a28Sdamien eep->calTargetPower5G,
608498e8a28Sdamien AR5416_NUM_5G_20_TARGET_POWERS, tpow_ofdm_ext);
609498e8a28Sdamien }
610498e8a28Sdamien }
611498e8a28Sdamien
612498e8a28Sdamien /* Compute CCK/OFDM delta. */
613498e8a28Sdamien cckinc = (sc->flags & ATHN_FLAG_OLPC) ? -2 : 0;
614498e8a28Sdamien
615bd6ea91dSdamien memset(power, 0, sizeof(power));
616*4b1a56afSjsg /* Shuffle target powers across transmit rates. */
617498e8a28Sdamien power[ATHN_POWER_OFDM6 ] =
618498e8a28Sdamien power[ATHN_POWER_OFDM9 ] =
619498e8a28Sdamien power[ATHN_POWER_OFDM12] =
620498e8a28Sdamien power[ATHN_POWER_OFDM18] =
621498e8a28Sdamien power[ATHN_POWER_OFDM24] = tpow_ofdm[0];
622498e8a28Sdamien power[ATHN_POWER_OFDM36] = tpow_ofdm[1];
623498e8a28Sdamien power[ATHN_POWER_OFDM48] = tpow_ofdm[2];
624498e8a28Sdamien power[ATHN_POWER_OFDM54] = tpow_ofdm[3];
625498e8a28Sdamien power[ATHN_POWER_XR ] = tpow_ofdm[0];
626498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c)) {
627498e8a28Sdamien power[ATHN_POWER_CCK1_LP ] = tpow_cck[0] + cckinc;
628498e8a28Sdamien power[ATHN_POWER_CCK2_LP ] =
629498e8a28Sdamien power[ATHN_POWER_CCK2_SP ] = tpow_cck[1] + cckinc;
630498e8a28Sdamien power[ATHN_POWER_CCK55_LP] =
631498e8a28Sdamien power[ATHN_POWER_CCK55_SP] = tpow_cck[2] + cckinc;
632498e8a28Sdamien power[ATHN_POWER_CCK11_LP] =
633498e8a28Sdamien power[ATHN_POWER_CCK11_SP] = tpow_cck[3] + cckinc;
634498e8a28Sdamien }
635498e8a28Sdamien for (i = 0; i < nitems(tpow_ht20); i++)
636498e8a28Sdamien power[ATHN_POWER_HT20(i)] = tpow_ht20[i];
637498e8a28Sdamien if (extc != NULL) {
638498e8a28Sdamien /* Correct PAR difference between HT40 and HT20/Legacy. */
639498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_2)
640498e8a28Sdamien ht40inc = modal->ht40PowerIncForPdadc;
641498e8a28Sdamien else
642498e8a28Sdamien ht40inc = AR_HT40_POWER_INC_FOR_PDADC;
643498e8a28Sdamien for (i = 0; i < nitems(tpow_ht40); i++)
644498e8a28Sdamien power[ATHN_POWER_HT40(i)] = tpow_ht40[i] + ht40inc;
645498e8a28Sdamien power[ATHN_POWER_OFDM_DUP] = tpow_ht40[0];
646498e8a28Sdamien power[ATHN_POWER_CCK_DUP ] = tpow_ht40[0] + cckinc;
647498e8a28Sdamien power[ATHN_POWER_OFDM_EXT] = tpow_ofdm_ext[0];
648498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c))
649498e8a28Sdamien power[ATHN_POWER_CCK_EXT] = tpow_cck_ext[0] + cckinc;
650498e8a28Sdamien }
651498e8a28Sdamien
652498e8a28Sdamien if (AR_SREV_9280_10_OR_LATER(sc)) {
653498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_21)
654498e8a28Sdamien pwroff = eep->baseEepHeader.pwrTableOffset;
655498e8a28Sdamien else
656498e8a28Sdamien pwroff = AR_PWR_TABLE_OFFSET_DB;
657498e8a28Sdamien for (i = 0; i < ATHN_POWER_COUNT; i++)
658498e8a28Sdamien power[i] -= pwroff * 2; /* In half dB. */
659498e8a28Sdamien }
660498e8a28Sdamien for (i = 0; i < ATHN_POWER_COUNT; i++) {
661498e8a28Sdamien if (power[i] > AR_MAX_RATE_POWER)
662498e8a28Sdamien power[i] = AR_MAX_RATE_POWER;
663498e8a28Sdamien }
664498e8a28Sdamien
665498e8a28Sdamien /* Write transmit power values to hardware. */
666bd6ea91dSdamien ar5008_write_txpower(sc, power);
667498e8a28Sdamien
668498e8a28Sdamien /*
669*4b1a56afSjsg * Write transmit power subtraction for dynamic chain changing
670498e8a28Sdamien * and per-packet transmit power.
671498e8a28Sdamien */
672498e8a28Sdamien AR_WRITE(sc, AR_PHY_POWER_TX_SUB,
673498e8a28Sdamien (modal->pwrDecreaseFor3Chain & 0x3f) << 6 |
674498e8a28Sdamien (modal->pwrDecreaseFor2Chain & 0x3f));
675498e8a28Sdamien }
676498e8a28Sdamien
677498e8a28Sdamien void
ar5416_spur_mitigate(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)678498e8a28Sdamien ar5416_spur_mitigate(struct athn_softc *sc, struct ieee80211_channel *c,
679498e8a28Sdamien struct ieee80211_channel *extc)
680498e8a28Sdamien {
681498e8a28Sdamien const struct ar_spur_chan *spurchans;
682498e8a28Sdamien int i, spur, bin, spur_delta_phase, spur_freq_sd;
683498e8a28Sdamien
684498e8a28Sdamien spurchans = sc->ops.get_spur_chans(sc, IEEE80211_IS_CHAN_2GHZ(c));
685498e8a28Sdamien for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
686498e8a28Sdamien spur = spurchans[i].spurChan;
687498e8a28Sdamien if (spur == AR_NO_SPUR)
688498e8a28Sdamien return; /* XXX disable if it was enabled! */
689498e8a28Sdamien spur -= c->ic_freq * 10;
690498e8a28Sdamien /* Verify range +/-9.5MHz */
691498e8a28Sdamien if (abs(spur) < 95)
692498e8a28Sdamien break;
693498e8a28Sdamien }
694498e8a28Sdamien if (i == AR_EEPROM_MODAL_SPURS)
695498e8a28Sdamien return; /* XXX disable if it was enabled! */
696a67c32bdSdamien DPRINTFN(2, ("enabling spur mitigation\n"));
697498e8a28Sdamien
698498e8a28Sdamien AR_SETBITS(sc, AR_PHY_TIMING_CTRL4_0,
699498e8a28Sdamien AR_PHY_TIMING_CTRL4_ENABLE_SPUR_RSSI |
700498e8a28Sdamien AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER |
701498e8a28Sdamien AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
702498e8a28Sdamien AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
703498e8a28Sdamien
704498e8a28Sdamien AR_WRITE(sc, AR_PHY_SPUR_REG,
705498e8a28Sdamien AR_PHY_SPUR_REG_MASK_RATE_CNTL |
706498e8a28Sdamien AR_PHY_SPUR_REG_ENABLE_MASK_PPM |
707498e8a28Sdamien AR_PHY_SPUR_REG_MASK_RATE_SELECT |
708498e8a28Sdamien AR_PHY_SPUR_REG_ENABLE_VIT_SPUR_RSSI |
709498e8a28Sdamien SM(AR_PHY_SPUR_REG_SPUR_RSSI_THRESH, AR_SPUR_RSSI_THRESH));
710498e8a28Sdamien
711498e8a28Sdamien spur_delta_phase = (spur * 524288) / 100;
712498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c))
713498e8a28Sdamien spur_freq_sd = (spur * 2048) / 440;
714498e8a28Sdamien else
715498e8a28Sdamien spur_freq_sd = (spur * 2048) / 400;
716498e8a28Sdamien
717498e8a28Sdamien AR_WRITE(sc, AR_PHY_TIMING11,
718498e8a28Sdamien AR_PHY_TIMING11_USE_SPUR_IN_AGC |
719498e8a28Sdamien SM(AR_PHY_TIMING11_SPUR_FREQ_SD, spur_freq_sd) |
720498e8a28Sdamien SM(AR_PHY_TIMING11_SPUR_DELTA_PHASE, spur_delta_phase));
721498e8a28Sdamien
722498e8a28Sdamien bin = spur * 32;
723bd6ea91dSdamien ar5008_set_viterbi_mask(sc, bin);
724bd6ea91dSdamien }
725bd6ea91dSdamien
726bd6ea91dSdamien uint8_t
ar5416_reverse_bits(uint8_t v,int nbits)727bd6ea91dSdamien ar5416_reverse_bits(uint8_t v, int nbits)
728bd6ea91dSdamien {
729bd6ea91dSdamien KASSERT(nbits <= 8);
730bd6ea91dSdamien v = ((v >> 1) & 0x55) | ((v & 0x55) << 1);
731bd6ea91dSdamien v = ((v >> 2) & 0x33) | ((v & 0x33) << 2);
732bd6ea91dSdamien v = ((v >> 4) & 0x0f) | ((v & 0x0f) << 4);
733bd6ea91dSdamien return (v >> (8 - nbits));
734bd6ea91dSdamien }
735bd6ea91dSdamien
736bd6ea91dSdamien uint8_t
ar5416_get_rf_rev(struct athn_softc * sc)737bd6ea91dSdamien ar5416_get_rf_rev(struct athn_softc *sc)
738bd6ea91dSdamien {
739bd6ea91dSdamien uint8_t rev, reg;
740bd6ea91dSdamien int i;
741bd6ea91dSdamien
742bd6ea91dSdamien /* Allow access to analog chips. */
743bd6ea91dSdamien AR_WRITE(sc, AR_PHY(0), 0x00000007);
744bd6ea91dSdamien
745bd6ea91dSdamien AR_WRITE(sc, AR_PHY(0x36), 0x00007058);
746bd6ea91dSdamien for (i = 0; i < 8; i++)
747bd6ea91dSdamien AR_WRITE(sc, AR_PHY(0x20), 0x00010000);
748bd6ea91dSdamien reg = (AR_READ(sc, AR_PHY(256)) >> 24) & 0xff;
749bd6ea91dSdamien reg = (reg & 0xf0) >> 4 | (reg & 0x0f) << 4;
750bd6ea91dSdamien
751bd6ea91dSdamien rev = ar5416_reverse_bits(reg, 8);
752bd6ea91dSdamien if ((rev & AR_RADIO_SREV_MAJOR) == 0)
753bd6ea91dSdamien rev = AR_RAD5133_SREV_MAJOR;
754bd6ea91dSdamien return (rev);
755498e8a28Sdamien }
756498e8a28Sdamien
757498e8a28Sdamien /*
758498e8a28Sdamien * Replace bits "off" to "off+nbits-1" in column "col" with the specified
759498e8a28Sdamien * value.
760498e8a28Sdamien */
761498e8a28Sdamien void
ar5416_rw_rfbits(uint32_t * buf,int col,int off,uint32_t val,int nbits)762498e8a28Sdamien ar5416_rw_rfbits(uint32_t *buf, int col, int off, uint32_t val, int nbits)
763498e8a28Sdamien {
764498e8a28Sdamien int idx, bit;
765498e8a28Sdamien
766498e8a28Sdamien KASSERT(off >= 1 && col < 4 && nbits <= 32);
767498e8a28Sdamien
768498e8a28Sdamien off--; /* Starts at 1. */
769498e8a28Sdamien while (nbits-- > 0) {
770498e8a28Sdamien idx = off / 8;
771498e8a28Sdamien bit = off % 8;
772498e8a28Sdamien buf[idx] &= ~(1 << (bit + col * 8));
773498e8a28Sdamien buf[idx] |= ((val >> nbits) & 1) << (bit + col * 8);
774498e8a28Sdamien off++;
775498e8a28Sdamien }
776498e8a28Sdamien }
777498e8a28Sdamien
778498e8a28Sdamien /*
779498e8a28Sdamien * Overwrite db and ob based on ROM settings.
780498e8a28Sdamien */
781498e8a28Sdamien void
ar5416_rw_bank6tpc(struct athn_softc * sc,struct ieee80211_channel * c,uint32_t * rwbank6tpc)782498e8a28Sdamien ar5416_rw_bank6tpc(struct athn_softc *sc, struct ieee80211_channel *c,
783498e8a28Sdamien uint32_t *rwbank6tpc)
784498e8a28Sdamien {
785498e8a28Sdamien const struct ar5416_eeprom *eep = sc->eep;
786498e8a28Sdamien const struct ar5416_modal_eep_header *modal;
787498e8a28Sdamien
788498e8a28Sdamien if (IEEE80211_IS_CHAN_5GHZ(c)) {
789498e8a28Sdamien modal = &eep->modalHeader[0];
790bd6ea91dSdamien /* 5GHz db in column 0, bits [200-202]. */
791498e8a28Sdamien ar5416_rw_rfbits(rwbank6tpc, 0, 200, modal->db, 3);
792498e8a28Sdamien /* 5GHz ob in column 0, bits [203-205]. */
793498e8a28Sdamien ar5416_rw_rfbits(rwbank6tpc, 0, 203, modal->ob, 3);
794498e8a28Sdamien } else {
795498e8a28Sdamien modal = &eep->modalHeader[1];
796bd6ea91dSdamien /* 2GHz db in column 0, bits [194-196]. */
797498e8a28Sdamien ar5416_rw_rfbits(rwbank6tpc, 0, 194, modal->db, 3);
798498e8a28Sdamien /* 2GHz ob in column 0, bits [197-199]. */
799498e8a28Sdamien ar5416_rw_rfbits(rwbank6tpc, 0, 197, modal->ob, 3);
800498e8a28Sdamien }
801498e8a28Sdamien }
802498e8a28Sdamien
803498e8a28Sdamien /*
804498e8a28Sdamien * Program analog RF.
805498e8a28Sdamien */
806498e8a28Sdamien void
ar5416_rf_reset(struct athn_softc * sc,struct ieee80211_channel * c)807498e8a28Sdamien ar5416_rf_reset(struct athn_softc *sc, struct ieee80211_channel *c)
808498e8a28Sdamien {
809498e8a28Sdamien const uint32_t *bank6tpc;
810498e8a28Sdamien int i;
811498e8a28Sdamien
812498e8a28Sdamien /* Bank 0. */
813498e8a28Sdamien AR_WRITE(sc, 0x98b0, 0x1e5795e5);
814498e8a28Sdamien AR_WRITE(sc, 0x98e0, 0x02008020);
815498e8a28Sdamien
816498e8a28Sdamien /* Bank 1. */
817498e8a28Sdamien AR_WRITE(sc, 0x98b0, 0x02108421);
818498e8a28Sdamien AR_WRITE(sc, 0x98ec, 0x00000008);
819498e8a28Sdamien
820498e8a28Sdamien /* Bank 2. */
821498e8a28Sdamien AR_WRITE(sc, 0x98b0, 0x0e73ff17);
822498e8a28Sdamien AR_WRITE(sc, 0x98e0, 0x00000420);
823498e8a28Sdamien
824498e8a28Sdamien /* Bank 3. */
825498e8a28Sdamien if (IEEE80211_IS_CHAN_5GHZ(c))
826498e8a28Sdamien AR_WRITE(sc, 0x98f0, 0x01400018);
827498e8a28Sdamien else
828498e8a28Sdamien AR_WRITE(sc, 0x98f0, 0x01c00018);
829498e8a28Sdamien
830498e8a28Sdamien /* Select the Bank 6 TPC values to use. */
831498e8a28Sdamien if (AR_SREV_9160_10_OR_LATER(sc))
832498e8a28Sdamien bank6tpc = ar9160_bank6tpc_vals;
833498e8a28Sdamien else
834498e8a28Sdamien bank6tpc = ar5416_bank6tpc_vals;
835498e8a28Sdamien if (sc->eep_rev >= AR_EEP_MINOR_VER_2) {
836498e8a28Sdamien uint32_t *rwbank6tpc = sc->rwbuf;
837498e8a28Sdamien
838498e8a28Sdamien /* Copy values from .rodata to writable buffer. */
839498e8a28Sdamien memcpy(rwbank6tpc, bank6tpc, 32 * sizeof(uint32_t));
840498e8a28Sdamien ar5416_rw_bank6tpc(sc, c, rwbank6tpc);
841498e8a28Sdamien bank6tpc = rwbank6tpc;
842498e8a28Sdamien }
843498e8a28Sdamien /* Bank 6 TPC. */
844498e8a28Sdamien for (i = 0; i < 32; i++)
845498e8a28Sdamien AR_WRITE(sc, 0x989c, bank6tpc[i]);
846498e8a28Sdamien if (IEEE80211_IS_CHAN_5GHZ(c))
847498e8a28Sdamien AR_WRITE(sc, 0x98d0, 0x0000000f);
848498e8a28Sdamien else
849498e8a28Sdamien AR_WRITE(sc, 0x98d0, 0x0010000f);
850498e8a28Sdamien
851498e8a28Sdamien /* Bank 7. */
852498e8a28Sdamien AR_WRITE(sc, 0x989c, 0x00000500);
853498e8a28Sdamien AR_WRITE(sc, 0x989c, 0x00000800);
854498e8a28Sdamien AR_WRITE(sc, 0x98cc, 0x0000000e);
855498e8a28Sdamien }
856498e8a28Sdamien
857498e8a28Sdamien void
ar5416_reset_bb_gain(struct athn_softc * sc,struct ieee80211_channel * c)858498e8a28Sdamien ar5416_reset_bb_gain(struct athn_softc *sc, struct ieee80211_channel *c)
859498e8a28Sdamien {
860498e8a28Sdamien const uint32_t *pvals;
861498e8a28Sdamien int i;
862498e8a28Sdamien
863498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c))
864498e8a28Sdamien pvals = ar5416_bb_rfgain_vals_2g;
865498e8a28Sdamien else
866498e8a28Sdamien pvals = ar5416_bb_rfgain_vals_5g;
867498e8a28Sdamien for (i = 0; i < 64; i++)
868498e8a28Sdamien AR_WRITE(sc, AR_PHY_BB_RFGAIN(i), pvals[i]);
869498e8a28Sdamien }
870498e8a28Sdamien
871498e8a28Sdamien /*
872498e8a28Sdamien * Fix orientation sensitivity issue on AR5416/2GHz by increasing
873498e8a28Sdamien * rf_pwd_icsyndiv.
874498e8a28Sdamien */
875498e8a28Sdamien void
ar5416_force_bias(struct athn_softc * sc,struct ieee80211_channel * c)876498e8a28Sdamien ar5416_force_bias(struct athn_softc *sc, struct ieee80211_channel *c)
877498e8a28Sdamien {
878498e8a28Sdamien uint32_t *rwbank6 = sc->rwbuf;
879498e8a28Sdamien uint8_t bias;
880498e8a28Sdamien int i;
881498e8a28Sdamien
882498e8a28Sdamien KASSERT(IEEE80211_IS_CHAN_2GHZ(c));
883498e8a28Sdamien
884498e8a28Sdamien /* Copy values from .rodata to writable buffer. */
885bd6ea91dSdamien memcpy(rwbank6, ar5416_bank6_vals, sizeof(ar5416_bank6_vals));
886498e8a28Sdamien
887498e8a28Sdamien if (c->ic_freq < 2412)
888498e8a28Sdamien bias = 0;
889498e8a28Sdamien else if (c->ic_freq < 2422)
890498e8a28Sdamien bias = 1;
891498e8a28Sdamien else
892498e8a28Sdamien bias = 2;
893bd6ea91dSdamien ar5416_reverse_bits(bias, 3);
894498e8a28Sdamien
895498e8a28Sdamien /* Overwrite "rf_pwd_icsyndiv" (column 3, bits [181-183].) */
896498e8a28Sdamien ar5416_rw_rfbits(rwbank6, 3, 181, bias, 3);
897498e8a28Sdamien
898498e8a28Sdamien /* Write Bank 6. */
899498e8a28Sdamien for (i = 0; i < 32; i++)
900498e8a28Sdamien AR_WRITE(sc, 0x989c, rwbank6[i]);
901498e8a28Sdamien AR_WRITE(sc, 0x98d0, 0x0010000f);
902498e8a28Sdamien }
903498e8a28Sdamien
904498e8a28Sdamien /*
905498e8a28Sdamien * Overwrite XPA bias level based on ROM setting.
906498e8a28Sdamien */
907498e8a28Sdamien void
ar9160_rw_addac(struct athn_softc * sc,struct ieee80211_channel * c,uint32_t * addac)908498e8a28Sdamien ar9160_rw_addac(struct athn_softc *sc, struct ieee80211_channel *c,
909498e8a28Sdamien uint32_t *addac)
910498e8a28Sdamien {
911498e8a28Sdamien struct ar5416_eeprom *eep = sc->eep;
912498e8a28Sdamien struct ar5416_modal_eep_header *modal;
913498e8a28Sdamien uint8_t fbin, bias;
914498e8a28Sdamien int i;
915498e8a28Sdamien
916498e8a28Sdamien /* XXX xpaBiasLvlFreq values have not been endian-swapped? */
917498e8a28Sdamien
918498e8a28Sdamien /* Get the XPA bias level to use for the specified channel. */
919498e8a28Sdamien modal = &eep->modalHeader[IEEE80211_IS_CHAN_2GHZ(c)];
920498e8a28Sdamien if (modal->xpaBiasLvl == 0xff) {
921498e8a28Sdamien bias = modal->xpaBiasLvlFreq[0] >> 14;
922498e8a28Sdamien fbin = athn_chan2fbin(c);
923498e8a28Sdamien for (i = 1; i < 3; i++) {
924498e8a28Sdamien if (modal->xpaBiasLvlFreq[i] == 0)
925498e8a28Sdamien break;
926498e8a28Sdamien if ((modal->xpaBiasLvlFreq[i] & 0xff) < fbin)
927498e8a28Sdamien break;
928498e8a28Sdamien bias = modal->xpaBiasLvlFreq[i] >> 14;
929498e8a28Sdamien }
930498e8a28Sdamien } else
931498e8a28Sdamien bias = modal->xpaBiasLvl & 0x3;
932498e8a28Sdamien
933bd6ea91dSdamien bias = ar5416_reverse_bits(bias, 2); /* Put in host bit-order. */
934a67c32bdSdamien DPRINTFN(4, ("bias level=%d\n", bias));
935498e8a28Sdamien if (IEEE80211_IS_CHAN_2GHZ(c))
936498e8a28Sdamien ar5416_rw_rfbits(addac, 0, 60, bias, 2);
937498e8a28Sdamien else
938498e8a28Sdamien ar5416_rw_rfbits(addac, 0, 55, bias, 2);
939498e8a28Sdamien }
940498e8a28Sdamien
941498e8a28Sdamien void
ar5416_reset_addac(struct athn_softc * sc,struct ieee80211_channel * c)942498e8a28Sdamien ar5416_reset_addac(struct athn_softc *sc, struct ieee80211_channel *c)
943498e8a28Sdamien {
944498e8a28Sdamien const struct athn_addac *addac = sc->addac;
945498e8a28Sdamien const uint32_t *pvals;
946498e8a28Sdamien int i;
947498e8a28Sdamien
948498e8a28Sdamien if (AR_SREV_9160(sc) && sc->eep_rev >= AR_EEP_MINOR_VER_7) {
949498e8a28Sdamien uint32_t *rwaddac = sc->rwbuf;
950498e8a28Sdamien
951498e8a28Sdamien /* Copy values from .rodata to writable buffer. */
952498e8a28Sdamien memcpy(rwaddac, addac->vals, addac->nvals * sizeof(uint32_t));
953498e8a28Sdamien ar9160_rw_addac(sc, c, rwaddac);
954498e8a28Sdamien pvals = rwaddac;
955498e8a28Sdamien } else
956498e8a28Sdamien pvals = addac->vals;
957498e8a28Sdamien for (i = 0; i < addac->nvals; i++)
958498e8a28Sdamien AR_WRITE(sc, 0x989c, pvals[i]);
959498e8a28Sdamien AR_WRITE(sc, 0x98cc, 0); /* Finalize. */
960498e8a28Sdamien }
961