xref: /openbsd-src/sys/dev/ic/ar5416.c (revision 4b1a56afb1a28c97103da3911d326d1216798a6e)
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