xref: /openbsd-src/sys/dev/ic/ar5008.c (revision 04cdc2f221d0a137bc3fe350017128664d5a8b58)
1*04cdc2f2Spatrick /*	$OpenBSD: ar5008.c,v 1.71 2022/12/27 20:13:03 patrick Exp $	*/
2bd6ea91dSdamien 
3bd6ea91dSdamien /*-
4bd6ea91dSdamien  * Copyright (c) 2009 Damien Bergamini <damien.bergamini@free.fr>
5bd6ea91dSdamien  * Copyright (c) 2008-2009 Atheros Communications Inc.
6bd6ea91dSdamien  *
7bd6ea91dSdamien  * Permission to use, copy, modify, and/or distribute this software for any
8bd6ea91dSdamien  * purpose with or without fee is hereby granted, provided that the above
9bd6ea91dSdamien  * copyright notice and this permission notice appear in all copies.
10bd6ea91dSdamien  *
11bd6ea91dSdamien  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12bd6ea91dSdamien  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13bd6ea91dSdamien  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14bd6ea91dSdamien  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15bd6ea91dSdamien  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16bd6ea91dSdamien  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17bd6ea91dSdamien  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18bd6ea91dSdamien  */
19bd6ea91dSdamien 
20bd6ea91dSdamien /*
21bd6ea91dSdamien  * Driver for Atheros 802.11a/g/n chipsets.
22bd6ea91dSdamien  * Routines common to AR5008, AR9001 and AR9002 families.
23bd6ea91dSdamien  */
24bd6ea91dSdamien 
25bd6ea91dSdamien #include "bpfilter.h"
26bd6ea91dSdamien 
27bd6ea91dSdamien #include <sys/param.h>
28bd6ea91dSdamien #include <sys/sockio.h>
29bd6ea91dSdamien #include <sys/mbuf.h>
30bd6ea91dSdamien #include <sys/kernel.h>
31bd6ea91dSdamien #include <sys/socket.h>
32bd6ea91dSdamien #include <sys/systm.h>
33bd6ea91dSdamien #include <sys/malloc.h>
34bd6ea91dSdamien #include <sys/queue.h>
35bd6ea91dSdamien #include <sys/timeout.h>
36bd6ea91dSdamien #include <sys/conf.h>
37bd6ea91dSdamien #include <sys/device.h>
38bd6ea91dSdamien #include <sys/stdint.h>	/* uintptr_t */
399b18ffb8Sguenther #include <sys/endian.h>
40bd6ea91dSdamien 
41bd6ea91dSdamien #include <machine/bus.h>
42bd6ea91dSdamien 
43bd6ea91dSdamien #if NBPFILTER > 0
44bd6ea91dSdamien #include <net/bpf.h>
45bd6ea91dSdamien #endif
46bd6ea91dSdamien #include <net/if.h>
47bd6ea91dSdamien #include <net/if_media.h>
48bd6ea91dSdamien 
49bd6ea91dSdamien #include <netinet/in.h>
50bd6ea91dSdamien #include <netinet/if_ether.h>
51bd6ea91dSdamien 
52bd6ea91dSdamien #include <net80211/ieee80211_var.h>
53bd6ea91dSdamien #include <net80211/ieee80211_amrr.h>
54d2dd70acSstsp #include <net80211/ieee80211_ra.h>
55bd6ea91dSdamien #include <net80211/ieee80211_radiotap.h>
56bd6ea91dSdamien 
57bd6ea91dSdamien #include <dev/ic/athnreg.h>
58bd6ea91dSdamien #include <dev/ic/athnvar.h>
59bd6ea91dSdamien 
60bd6ea91dSdamien #include <dev/ic/ar5008reg.h>
61bd6ea91dSdamien 
62bd6ea91dSdamien int	ar5008_attach(struct athn_softc *);
633a686414Sdamien int	ar5008_read_eep_word(struct athn_softc *, uint32_t, uint16_t *);
64bd6ea91dSdamien int	ar5008_read_rom(struct athn_softc *);
65bd6ea91dSdamien void	ar5008_swap_rom(struct athn_softc *);
66bd6ea91dSdamien int	ar5008_gpio_read(struct athn_softc *, int);
67bd6ea91dSdamien void	ar5008_gpio_write(struct athn_softc *, int, int);
68bd6ea91dSdamien void	ar5008_gpio_config_input(struct athn_softc *, int);
69bd6ea91dSdamien void	ar5008_gpio_config_output(struct athn_softc *, int, int);
70bd6ea91dSdamien void	ar5008_rfsilent_init(struct athn_softc *);
71bd6ea91dSdamien int	ar5008_dma_alloc(struct athn_softc *);
72bd6ea91dSdamien void	ar5008_dma_free(struct athn_softc *);
73bd6ea91dSdamien int	ar5008_tx_alloc(struct athn_softc *);
74bd6ea91dSdamien void	ar5008_tx_free(struct athn_softc *);
75bd6ea91dSdamien int	ar5008_rx_alloc(struct athn_softc *);
76bd6ea91dSdamien void	ar5008_rx_free(struct athn_softc *);
77bd6ea91dSdamien void	ar5008_rx_enable(struct athn_softc *);
78bd6ea91dSdamien void	ar5008_rx_radiotap(struct athn_softc *, struct mbuf *,
79bd6ea91dSdamien 	    struct ar_rx_desc *);
80ff1dd4b7Sstsp int	ar5008_ccmp_decap(struct athn_softc *, struct mbuf *,
81ff1dd4b7Sstsp 	    struct ieee80211_node *);
82bd6ea91dSdamien void	ar5008_rx_intr(struct athn_softc *);
83bd6ea91dSdamien int	ar5008_tx_process(struct athn_softc *, int);
84bd6ea91dSdamien void	ar5008_tx_intr(struct athn_softc *);
85eff5798eSdamien int	ar5008_swba_intr(struct athn_softc *);
86bd6ea91dSdamien int	ar5008_intr(struct athn_softc *);
87ff1dd4b7Sstsp int	ar5008_ccmp_encap(struct mbuf *, u_int, struct ieee80211_key *);
88436170c5Sdamien int	ar5008_tx(struct athn_softc *, struct mbuf *, struct ieee80211_node *,
89436170c5Sdamien 	    int);
90bd6ea91dSdamien void	ar5008_set_rf_mode(struct athn_softc *, struct ieee80211_channel *);
91bd6ea91dSdamien int	ar5008_rf_bus_request(struct athn_softc *);
92bd6ea91dSdamien void	ar5008_rf_bus_release(struct athn_softc *);
93bd6ea91dSdamien void	ar5008_set_phy(struct athn_softc *, struct ieee80211_channel *,
94bd6ea91dSdamien 	    struct ieee80211_channel *);
95bd6ea91dSdamien void	ar5008_set_delta_slope(struct athn_softc *, struct ieee80211_channel *,
96bd6ea91dSdamien 	    struct ieee80211_channel *);
97bd6ea91dSdamien void	ar5008_enable_antenna_diversity(struct athn_softc *);
98bd6ea91dSdamien void	ar5008_init_baseband(struct athn_softc *);
99bd6ea91dSdamien void	ar5008_disable_phy(struct athn_softc *);
100bd6ea91dSdamien void	ar5008_init_chains(struct athn_softc *);
101bd6ea91dSdamien void	ar5008_set_rxchains(struct athn_softc *);
102bd6ea91dSdamien void	ar5008_read_noisefloor(struct athn_softc *, int16_t *, int16_t *);
103bd6ea91dSdamien void	ar5008_write_noisefloor(struct athn_softc *, int16_t *, int16_t *);
1049d1f2812Sstsp int	ar5008_get_noisefloor(struct athn_softc *);
1059d1f2812Sstsp void	ar5008_apply_noisefloor(struct athn_softc *);
106bd6ea91dSdamien void	ar5008_bb_load_noisefloor(struct athn_softc *);
107bd6ea91dSdamien void	ar5008_do_noisefloor_calib(struct athn_softc *);
1089d1f2812Sstsp void	ar5008_init_noisefloor_calib(struct athn_softc *);
109bd6ea91dSdamien void	ar5008_do_calib(struct athn_softc *);
110bd6ea91dSdamien void	ar5008_next_calib(struct athn_softc *);
111bd6ea91dSdamien void	ar5008_calib_iq(struct athn_softc *);
112bd6ea91dSdamien void	ar5008_calib_adc_gain(struct athn_softc *);
113bd6ea91dSdamien void	ar5008_calib_adc_dc_off(struct athn_softc *);
114*04cdc2f2Spatrick void	ar5008_write_txpower(struct athn_softc *, int16_t *);
115bd6ea91dSdamien void	ar5008_set_viterbi_mask(struct athn_softc *, int);
116bd6ea91dSdamien void	ar5008_hw_init(struct athn_softc *, struct ieee80211_channel *,
117bd6ea91dSdamien 	    struct ieee80211_channel *);
118bd6ea91dSdamien uint8_t	ar5008_get_vpd(uint8_t, const uint8_t *, const uint8_t *, int);
119bd6ea91dSdamien void	ar5008_get_pdadcs(struct athn_softc *, uint8_t, struct athn_pier *,
120bd6ea91dSdamien 	    struct athn_pier *, int, int, uint8_t, uint8_t *, uint8_t *);
121bd6ea91dSdamien void	ar5008_get_lg_tpow(struct athn_softc *, struct ieee80211_channel *,
122*04cdc2f2Spatrick 	    uint8_t, const struct ar_cal_target_power_leg *, int, uint8_t *);
123bd6ea91dSdamien void	ar5008_get_ht_tpow(struct athn_softc *, struct ieee80211_channel *,
124*04cdc2f2Spatrick 	    uint8_t, const struct ar_cal_target_power_ht *, int, uint8_t *);
125bd6ea91dSdamien void	ar5008_set_noise_immunity_level(struct athn_softc *, int);
126bd6ea91dSdamien void	ar5008_enable_ofdm_weak_signal(struct athn_softc *);
127bd6ea91dSdamien void	ar5008_disable_ofdm_weak_signal(struct athn_softc *);
128bd6ea91dSdamien void	ar5008_set_cck_weak_signal(struct athn_softc *, int);
129bd6ea91dSdamien void	ar5008_set_firstep_level(struct athn_softc *, int);
130bd6ea91dSdamien void	ar5008_set_spur_immunity_level(struct athn_softc *, int);
131bd6ea91dSdamien 
132bd6ea91dSdamien /* Extern functions. */
133bd6ea91dSdamien void	athn_stop(struct ifnet *, int);
134bd6ea91dSdamien int	athn_interpolate(int, int, int, int, int);
135bd6ea91dSdamien int	athn_txtime(struct athn_softc *, int, int, u_int);
136bd6ea91dSdamien void	athn_inc_tx_trigger_level(struct athn_softc *);
137eff5798eSdamien int	athn_tx_pending(struct athn_softc *, int);
138eff5798eSdamien void	athn_stop_tx_dma(struct athn_softc *, int);
139bd6ea91dSdamien void	athn_get_delta_slope(uint32_t, uint32_t *, uint32_t *);
140bd6ea91dSdamien void	athn_config_pcie(struct athn_softc *);
141bd6ea91dSdamien void	athn_config_nonpcie(struct athn_softc *);
142bd6ea91dSdamien uint8_t	athn_chan2fbin(struct ieee80211_channel *);
143bd6ea91dSdamien uint8_t	ar5416_get_rf_rev(struct athn_softc *);
144bd6ea91dSdamien void	ar5416_reset_addac(struct athn_softc *, struct ieee80211_channel *);
145bd6ea91dSdamien void	ar5416_rf_reset(struct athn_softc *, struct ieee80211_channel *);
146bd6ea91dSdamien void	ar5416_reset_bb_gain(struct athn_softc *, struct ieee80211_channel *);
147bd6ea91dSdamien void	ar9280_reset_rx_gain(struct athn_softc *, struct ieee80211_channel *);
148bd6ea91dSdamien void	ar9280_reset_tx_gain(struct athn_softc *, struct ieee80211_channel *);
149bd6ea91dSdamien 
150bd6ea91dSdamien 
151bd6ea91dSdamien int
ar5008_attach(struct athn_softc * sc)152bd6ea91dSdamien ar5008_attach(struct athn_softc *sc)
153bd6ea91dSdamien {
154bd6ea91dSdamien 	struct athn_ops *ops = &sc->ops;
155bd6ea91dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
156bd6ea91dSdamien 	struct ar_base_eep_header *base;
157bd6ea91dSdamien 	uint8_t eep_ver, kc_entries_log;
158bd6ea91dSdamien 	int error;
159bd6ea91dSdamien 
160bd6ea91dSdamien 	/* Set callbacks for AR5008, AR9001 and AR9002 families. */
161bd6ea91dSdamien 	ops->gpio_read = ar5008_gpio_read;
162bd6ea91dSdamien 	ops->gpio_write = ar5008_gpio_write;
163bd6ea91dSdamien 	ops->gpio_config_input = ar5008_gpio_config_input;
164bd6ea91dSdamien 	ops->gpio_config_output = ar5008_gpio_config_output;
165bd6ea91dSdamien 	ops->rfsilent_init = ar5008_rfsilent_init;
166bd6ea91dSdamien 
167bd6ea91dSdamien 	ops->dma_alloc = ar5008_dma_alloc;
168bd6ea91dSdamien 	ops->dma_free = ar5008_dma_free;
169bd6ea91dSdamien 	ops->rx_enable = ar5008_rx_enable;
170bd6ea91dSdamien 	ops->intr = ar5008_intr;
171bd6ea91dSdamien 	ops->tx = ar5008_tx;
172bd6ea91dSdamien 
173bd6ea91dSdamien 	ops->set_rf_mode = ar5008_set_rf_mode;
174bd6ea91dSdamien 	ops->rf_bus_request = ar5008_rf_bus_request;
175bd6ea91dSdamien 	ops->rf_bus_release = ar5008_rf_bus_release;
176bd6ea91dSdamien 	ops->set_phy = ar5008_set_phy;
177bd6ea91dSdamien 	ops->set_delta_slope = ar5008_set_delta_slope;
178bd6ea91dSdamien 	ops->enable_antenna_diversity = ar5008_enable_antenna_diversity;
179bd6ea91dSdamien 	ops->init_baseband = ar5008_init_baseband;
180bd6ea91dSdamien 	ops->disable_phy = ar5008_disable_phy;
181bd6ea91dSdamien 	ops->set_rxchains = ar5008_set_rxchains;
182bd6ea91dSdamien 	ops->noisefloor_calib = ar5008_do_noisefloor_calib;
1839d1f2812Sstsp 	ops->init_noisefloor_calib = ar5008_init_noisefloor_calib;
1849d1f2812Sstsp 	ops->get_noisefloor = ar5008_get_noisefloor;
1859d1f2812Sstsp 	ops->apply_noisefloor = ar5008_apply_noisefloor;
186bd6ea91dSdamien 	ops->do_calib = ar5008_do_calib;
187bd6ea91dSdamien 	ops->next_calib = ar5008_next_calib;
188bd6ea91dSdamien 	ops->hw_init = ar5008_hw_init;
189bd6ea91dSdamien 
190bd6ea91dSdamien 	ops->set_noise_immunity_level = ar5008_set_noise_immunity_level;
191bd6ea91dSdamien 	ops->enable_ofdm_weak_signal = ar5008_enable_ofdm_weak_signal;
192bd6ea91dSdamien 	ops->disable_ofdm_weak_signal = ar5008_disable_ofdm_weak_signal;
193bd6ea91dSdamien 	ops->set_cck_weak_signal = ar5008_set_cck_weak_signal;
194bd6ea91dSdamien 	ops->set_firstep_level = ar5008_set_firstep_level;
195bd6ea91dSdamien 	ops->set_spur_immunity_level = ar5008_set_spur_immunity_level;
196bd6ea91dSdamien 
197bd6ea91dSdamien 	/* Set MAC registers offsets. */
198bd6ea91dSdamien 	sc->obs_off = AR_OBS;
199bd6ea91dSdamien 	sc->gpio_input_en_off = AR_GPIO_INPUT_EN_VAL;
200bd6ea91dSdamien 
201bd6ea91dSdamien 	if (!(sc->flags & ATHN_FLAG_PCIE))
202bd6ea91dSdamien 		athn_config_nonpcie(sc);
203bd6ea91dSdamien 	else
204bd6ea91dSdamien 		athn_config_pcie(sc);
205bd6ea91dSdamien 
206bd6ea91dSdamien 	/* Read entire ROM content in memory. */
207bd6ea91dSdamien 	if ((error = ar5008_read_rom(sc)) != 0) {
2083a686414Sdamien 		printf("%s: could not read ROM\n", sc->sc_dev.dv_xname);
209bd6ea91dSdamien 		return (error);
210bd6ea91dSdamien 	}
211bd6ea91dSdamien 
212bd6ea91dSdamien 	/* Get RF revision. */
213bd6ea91dSdamien 	sc->rf_rev = ar5416_get_rf_rev(sc);
214bd6ea91dSdamien 
215bd6ea91dSdamien 	base = sc->eep;
216bd6ea91dSdamien 	eep_ver = (base->version >> 12) & 0xf;
217bd6ea91dSdamien 	sc->eep_rev = (base->version & 0xfff);
218bd6ea91dSdamien 	if (eep_ver != AR_EEP_VER || sc->eep_rev == 0) {
2193a686414Sdamien 		printf("%s: unsupported ROM version %d.%d\n",
2203a686414Sdamien 		    sc->sc_dev.dv_xname, eep_ver, sc->eep_rev);
221bd6ea91dSdamien 		return (EINVAL);
222bd6ea91dSdamien 	}
223bd6ea91dSdamien 
2247363c99eSstsp 	if (base->opCapFlags & AR_OPFLAGS_11A) {
225bd6ea91dSdamien 		sc->flags |= ATHN_FLAG_11A;
2267363c99eSstsp 		if ((base->opCapFlags & AR_OPFLAGS_11N_5G20) == 0)
227bd6ea91dSdamien 			sc->flags |= ATHN_FLAG_11N;
2287363c99eSstsp #ifdef notyet
2297363c99eSstsp 		if ((base->opCapFlags & AR_OPFLAGS_11N_5G40) == 0)
2307363c99eSstsp 			sc->flags |= ATHN_FLAG_11N;
2317363c99eSstsp #endif
2327363c99eSstsp 	}
2337363c99eSstsp 	if (base->opCapFlags & AR_OPFLAGS_11G) {
2347363c99eSstsp 		sc->flags |= ATHN_FLAG_11G;
2357363c99eSstsp 		if ((base->opCapFlags & AR_OPFLAGS_11N_2G20) == 0)
2367363c99eSstsp 			sc->flags |= ATHN_FLAG_11N;
2377363c99eSstsp #ifdef notyet
2387363c99eSstsp 		if ((base->opCapFlags & AR_OPFLAGS_11N_2G40) == 0)
2397363c99eSstsp 			sc->flags |= ATHN_FLAG_11N;
2407363c99eSstsp #endif
2417363c99eSstsp 	}
242bd6ea91dSdamien 
243bd6ea91dSdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, base->macAddr);
244bd6ea91dSdamien 
245bd6ea91dSdamien 	/* Check if we have a hardware radio switch. */
246bd6ea91dSdamien 	if (base->rfSilent & AR_EEP_RFSILENT_ENABLED) {
247bd6ea91dSdamien 		sc->flags |= ATHN_FLAG_RFSILENT;
248bd6ea91dSdamien 		/* Get GPIO pin used by hardware radio switch. */
249bd6ea91dSdamien 		sc->rfsilent_pin = MS(base->rfSilent,
250bd6ea91dSdamien 		    AR_EEP_RFSILENT_GPIO_SEL);
251bd6ea91dSdamien 		/* Get polarity of hardware radio switch. */
252bd6ea91dSdamien 		if (base->rfSilent & AR_EEP_RFSILENT_POLARITY)
253bd6ea91dSdamien 			sc->flags |= ATHN_FLAG_RFSILENT_REVERSED;
254bd6ea91dSdamien 	}
255bd6ea91dSdamien 
256bd6ea91dSdamien 	/* Get the number of HW key cache entries. */
257bd6ea91dSdamien 	kc_entries_log = MS(base->deviceCap, AR_EEP_DEVCAP_KC_ENTRIES);
258bd6ea91dSdamien 	sc->kc_entries = (kc_entries_log != 0) ?
259bd6ea91dSdamien 	    1 << kc_entries_log : AR_KEYTABLE_SIZE;
260ff1dd4b7Sstsp 	if (sc->kc_entries > AR_KEYTABLE_SIZE)
261ff1dd4b7Sstsp 		sc->kc_entries = AR_KEYTABLE_SIZE;
262bd6ea91dSdamien 
263bd6ea91dSdamien 	sc->txchainmask = base->txMask;
264bd6ea91dSdamien 	if (sc->mac_ver == AR_SREV_VERSION_5416_PCI &&
265bd6ea91dSdamien 	    !(base->opCapFlags & AR_OPFLAGS_11A)) {
266bd6ea91dSdamien 		/* For single-band AR5416 PCI, use GPIO pin 0. */
267bd6ea91dSdamien 		sc->rxchainmask = ar5008_gpio_read(sc, 0) ? 0x5 : 0x7;
268bd6ea91dSdamien 	} else
269bd6ea91dSdamien 		sc->rxchainmask = base->rxMask;
270bd6ea91dSdamien 
271bd6ea91dSdamien 	ops->setup(sc);
272bd6ea91dSdamien 	return (0);
273bd6ea91dSdamien }
274bd6ea91dSdamien 
275bd6ea91dSdamien /*
2763a686414Sdamien  * Read 16-bit word from ROM.
277bd6ea91dSdamien  */
278bd6ea91dSdamien int
ar5008_read_eep_word(struct athn_softc * sc,uint32_t addr,uint16_t * val)2793a686414Sdamien ar5008_read_eep_word(struct athn_softc *sc, uint32_t addr, uint16_t *val)
280bd6ea91dSdamien {
281bd6ea91dSdamien 	uint32_t reg;
282bd6ea91dSdamien 	int ntries;
283bd6ea91dSdamien 
284bd6ea91dSdamien 	reg = AR_READ(sc, AR_EEPROM_OFFSET(addr));
285bd6ea91dSdamien 	for (ntries = 0; ntries < 1000; ntries++) {
286bd6ea91dSdamien 		reg = AR_READ(sc, AR_EEPROM_STATUS_DATA);
287bd6ea91dSdamien 		if (!(reg & (AR_EEPROM_STATUS_DATA_BUSY |
288bd6ea91dSdamien 		    AR_EEPROM_STATUS_DATA_PROT_ACCESS))) {
289bd6ea91dSdamien 			*val = MS(reg, AR_EEPROM_STATUS_DATA_VAL);
290bd6ea91dSdamien 			return (0);
291bd6ea91dSdamien 		}
292bd6ea91dSdamien 		DELAY(10);
293bd6ea91dSdamien 	}
294bd6ea91dSdamien 	*val = 0xffff;
295bd6ea91dSdamien 	return (ETIMEDOUT);
296bd6ea91dSdamien }
297bd6ea91dSdamien 
298bd6ea91dSdamien int
ar5008_read_rom(struct athn_softc * sc)299bd6ea91dSdamien ar5008_read_rom(struct athn_softc *sc)
300bd6ea91dSdamien {
301bd6ea91dSdamien 	uint32_t addr, end;
302bd6ea91dSdamien 	uint16_t magic, sum, *eep;
303bd6ea91dSdamien 	int need_swap = 0;
304bd6ea91dSdamien 	int error;
305bd6ea91dSdamien 
306bd6ea91dSdamien 	/* Determine ROM endianness. */
3073a686414Sdamien 	error = ar5008_read_eep_word(sc, AR_EEPROM_MAGIC_OFFSET, &magic);
308bd6ea91dSdamien 	if (error != 0)
309bd6ea91dSdamien 		return (error);
310bd6ea91dSdamien 	if (magic != AR_EEPROM_MAGIC) {
311bd6ea91dSdamien 		if (magic != swap16(AR_EEPROM_MAGIC)) {
312bd6ea91dSdamien 			DPRINTF(("invalid ROM magic 0x%x != 0x%x\n",
313bd6ea91dSdamien 			    magic, AR_EEPROM_MAGIC));
314bd6ea91dSdamien 			return (EIO);
315bd6ea91dSdamien 		}
316bd6ea91dSdamien 		DPRINTF(("non-native ROM endianness\n"));
317bd6ea91dSdamien 		need_swap = 1;
318bd6ea91dSdamien 	}
319bd6ea91dSdamien 
320bd6ea91dSdamien 	/* Allocate space to store ROM in host memory. */
321bd6ea91dSdamien 	sc->eep = malloc(sc->eep_size, M_DEVBUF, M_NOWAIT);
322bd6ea91dSdamien 	if (sc->eep == NULL)
323bd6ea91dSdamien 		return (ENOMEM);
324bd6ea91dSdamien 
325bd6ea91dSdamien 	/* Read entire ROM and compute checksum. */
326bd6ea91dSdamien 	sum = 0;
327bd6ea91dSdamien 	eep = sc->eep;
328bd6ea91dSdamien 	end = sc->eep_base + sc->eep_size / sizeof(uint16_t);
329bd6ea91dSdamien 	for (addr = sc->eep_base; addr < end; addr++, eep++) {
3303a686414Sdamien 		if ((error = ar5008_read_eep_word(sc, addr, eep)) != 0) {
331bd6ea91dSdamien 			DPRINTF(("could not read ROM at 0x%x\n", addr));
332bd6ea91dSdamien 			return (error);
333bd6ea91dSdamien 		}
334bd6ea91dSdamien 		if (need_swap)
335bd6ea91dSdamien 			*eep = swap16(*eep);
336bd6ea91dSdamien 		sum ^= *eep;
337bd6ea91dSdamien 	}
338bd6ea91dSdamien 	if (sum != 0xffff) {
339bd6ea91dSdamien 		printf("%s: bad ROM checksum 0x%04x\n",
340bd6ea91dSdamien 		    sc->sc_dev.dv_xname, sum);
341bd6ea91dSdamien 		return (EIO);
342bd6ea91dSdamien 	}
343bd6ea91dSdamien 	if (need_swap)
344bd6ea91dSdamien 		ar5008_swap_rom(sc);
345bd6ea91dSdamien 
346bd6ea91dSdamien 	return (0);
347bd6ea91dSdamien }
348bd6ea91dSdamien 
349bd6ea91dSdamien void
ar5008_swap_rom(struct athn_softc * sc)350bd6ea91dSdamien ar5008_swap_rom(struct athn_softc *sc)
351bd6ea91dSdamien {
352bd6ea91dSdamien 	struct ar_base_eep_header *base = sc->eep;
353bd6ea91dSdamien 
354bd6ea91dSdamien 	/* Swap common fields first. */
355bd6ea91dSdamien 	base->length = swap16(base->length);
356bd6ea91dSdamien 	base->version = swap16(base->version);
357bd6ea91dSdamien 	base->regDmn[0] = swap16(base->regDmn[0]);
358bd6ea91dSdamien 	base->regDmn[1] = swap16(base->regDmn[1]);
359bd6ea91dSdamien 	base->rfSilent = swap16(base->rfSilent);
360bd6ea91dSdamien 	base->blueToothOptions = swap16(base->blueToothOptions);
361bd6ea91dSdamien 	base->deviceCap = swap16(base->deviceCap);
362bd6ea91dSdamien 
363bd6ea91dSdamien 	/* Swap device-dependent fields. */
364bd6ea91dSdamien 	sc->ops.swap_rom(sc);
365bd6ea91dSdamien }
366bd6ea91dSdamien 
367bd6ea91dSdamien /*
368bd6ea91dSdamien  * Access to General Purpose Input/Output ports.
369bd6ea91dSdamien  */
370bd6ea91dSdamien int
ar5008_gpio_read(struct athn_softc * sc,int pin)371bd6ea91dSdamien ar5008_gpio_read(struct athn_softc *sc, int pin)
372bd6ea91dSdamien {
373bd6ea91dSdamien 	KASSERT(pin < sc->ngpiopins);
37411a1e31dSdamien 	if ((sc->flags & ATHN_FLAG_USB) && !AR_SREV_9271(sc))
375b0b75032Smatthew 		return (!((AR_READ(sc, AR7010_GPIO_IN) >> pin) & 1));
376bd6ea91dSdamien 	return ((AR_READ(sc, AR_GPIO_IN_OUT) >> (sc->ngpiopins + pin)) & 1);
377bd6ea91dSdamien }
378bd6ea91dSdamien 
379bd6ea91dSdamien void
ar5008_gpio_write(struct athn_softc * sc,int pin,int set)380bd6ea91dSdamien ar5008_gpio_write(struct athn_softc *sc, int pin, int set)
381bd6ea91dSdamien {
382bd6ea91dSdamien 	uint32_t reg;
383bd6ea91dSdamien 
384bd6ea91dSdamien 	KASSERT(pin < sc->ngpiopins);
38511a1e31dSdamien 
38611a1e31dSdamien 	if (sc->flags & ATHN_FLAG_USB)
38711a1e31dSdamien 		set = !set;	/* AR9271/AR7010 is reversed. */
38811a1e31dSdamien 
38911a1e31dSdamien 	if ((sc->flags & ATHN_FLAG_USB) && !AR_SREV_9271(sc)) {
39011a1e31dSdamien 		/* Special case for AR7010. */
39111a1e31dSdamien 		reg = AR_READ(sc, AR7010_GPIO_OUT);
39211a1e31dSdamien 		if (set)
39311a1e31dSdamien 			reg |= 1 << pin;
39411a1e31dSdamien 		else
39511a1e31dSdamien 			reg &= ~(1 << pin);
39611a1e31dSdamien 		AR_WRITE(sc, AR7010_GPIO_OUT, reg);
39711a1e31dSdamien 	} else {
398bd6ea91dSdamien 		reg = AR_READ(sc, AR_GPIO_IN_OUT);
399bd6ea91dSdamien 		if (set)
400bd6ea91dSdamien 			reg |= 1 << pin;
401bd6ea91dSdamien 		else
402bd6ea91dSdamien 			reg &= ~(1 << pin);
403bd6ea91dSdamien 		AR_WRITE(sc, AR_GPIO_IN_OUT, reg);
40411a1e31dSdamien 	}
405c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
406bd6ea91dSdamien }
407bd6ea91dSdamien 
408bd6ea91dSdamien void
ar5008_gpio_config_input(struct athn_softc * sc,int pin)409bd6ea91dSdamien ar5008_gpio_config_input(struct athn_softc *sc, int pin)
410bd6ea91dSdamien {
411bd6ea91dSdamien 	uint32_t reg;
412bd6ea91dSdamien 
41311a1e31dSdamien 	if ((sc->flags & ATHN_FLAG_USB) && !AR_SREV_9271(sc)) {
41411a1e31dSdamien 		/* Special case for AR7010. */
41511a1e31dSdamien 		AR_SETBITS(sc, AR7010_GPIO_OE, 1 << pin);
41611a1e31dSdamien 	} else {
417bd6ea91dSdamien 		reg = AR_READ(sc, AR_GPIO_OE_OUT);
418bd6ea91dSdamien 		reg &= ~(AR_GPIO_OE_OUT_DRV_M << (pin * 2));
419bd6ea91dSdamien 		reg |= AR_GPIO_OE_OUT_DRV_NO << (pin * 2);
420bd6ea91dSdamien 		AR_WRITE(sc, AR_GPIO_OE_OUT, reg);
42111a1e31dSdamien 	}
422c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
423bd6ea91dSdamien }
424bd6ea91dSdamien 
425bd6ea91dSdamien void
ar5008_gpio_config_output(struct athn_softc * sc,int pin,int type)426bd6ea91dSdamien ar5008_gpio_config_output(struct athn_softc *sc, int pin, int type)
427bd6ea91dSdamien {
428bd6ea91dSdamien 	uint32_t reg;
429bd6ea91dSdamien 	int mux, off;
430bd6ea91dSdamien 
43111a1e31dSdamien 	if ((sc->flags & ATHN_FLAG_USB) && !AR_SREV_9271(sc)) {
43211a1e31dSdamien 		/* Special case for AR7010. */
43311a1e31dSdamien 		AR_CLRBITS(sc, AR7010_GPIO_OE, 1 << pin);
43411a1e31dSdamien 		AR_WRITE_BARRIER(sc);
43511a1e31dSdamien 		return;
43611a1e31dSdamien 	}
437bd6ea91dSdamien 	mux = pin / 6;
438bd6ea91dSdamien 	off = pin % 6;
439bd6ea91dSdamien 
440bd6ea91dSdamien 	reg = AR_READ(sc, AR_GPIO_OUTPUT_MUX(mux));
441bd6ea91dSdamien 	if (!AR_SREV_9280_20_OR_LATER(sc) && mux == 0)
442bd6ea91dSdamien 		reg = (reg & ~0x1f0) | (reg & 0x1f0) << 1;
443bd6ea91dSdamien 	reg &= ~(0x1f << (off * 5));
444bd6ea91dSdamien 	reg |= (type & 0x1f) << (off * 5);
445bd6ea91dSdamien 	AR_WRITE(sc, AR_GPIO_OUTPUT_MUX(mux), reg);
446bd6ea91dSdamien 
447bd6ea91dSdamien 	reg = AR_READ(sc, AR_GPIO_OE_OUT);
448bd6ea91dSdamien 	reg &= ~(AR_GPIO_OE_OUT_DRV_M << (pin * 2));
449bd6ea91dSdamien 	reg |= AR_GPIO_OE_OUT_DRV_ALL << (pin * 2);
450bd6ea91dSdamien 	AR_WRITE(sc, AR_GPIO_OE_OUT, reg);
451c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
452bd6ea91dSdamien }
453bd6ea91dSdamien 
454bd6ea91dSdamien void
ar5008_rfsilent_init(struct athn_softc * sc)455bd6ea91dSdamien ar5008_rfsilent_init(struct athn_softc *sc)
456bd6ea91dSdamien {
457bd6ea91dSdamien 	uint32_t reg;
458bd6ea91dSdamien 
459bd6ea91dSdamien 	/* Configure hardware radio switch. */
460bd6ea91dSdamien 	AR_SETBITS(sc, AR_GPIO_INPUT_EN_VAL, AR_GPIO_INPUT_EN_VAL_RFSILENT_BB);
461bd6ea91dSdamien 	reg = AR_READ(sc, AR_GPIO_INPUT_MUX2);
462bd6ea91dSdamien 	reg = RW(reg, AR_GPIO_INPUT_MUX2_RFSILENT, 0);
463bd6ea91dSdamien 	AR_WRITE(sc, AR_GPIO_INPUT_MUX2, reg);
464bd6ea91dSdamien 	ar5008_gpio_config_input(sc, sc->rfsilent_pin);
465bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_TEST, AR_PHY_TEST_RFSILENT_BB);
466bd6ea91dSdamien 	if (!(sc->flags & ATHN_FLAG_RFSILENT_REVERSED)) {
467bd6ea91dSdamien 		AR_SETBITS(sc, AR_GPIO_INTR_POL,
468bd6ea91dSdamien 		    AR_GPIO_INTR_POL_PIN(sc->rfsilent_pin));
469bd6ea91dSdamien 	}
470c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
471bd6ea91dSdamien }
472bd6ea91dSdamien 
473bd6ea91dSdamien int
ar5008_dma_alloc(struct athn_softc * sc)474bd6ea91dSdamien ar5008_dma_alloc(struct athn_softc *sc)
475bd6ea91dSdamien {
476bd6ea91dSdamien 	int error;
477bd6ea91dSdamien 
478bd6ea91dSdamien 	error = ar5008_tx_alloc(sc);
479bd6ea91dSdamien 	if (error != 0)
480bd6ea91dSdamien 		return (error);
481bd6ea91dSdamien 
482bd6ea91dSdamien 	error = ar5008_rx_alloc(sc);
483bd6ea91dSdamien 	if (error != 0)
484bd6ea91dSdamien 		return (error);
485bd6ea91dSdamien 
486bd6ea91dSdamien 	return (0);
487bd6ea91dSdamien }
488bd6ea91dSdamien 
489bd6ea91dSdamien void
ar5008_dma_free(struct athn_softc * sc)490bd6ea91dSdamien ar5008_dma_free(struct athn_softc *sc)
491bd6ea91dSdamien {
492bd6ea91dSdamien 	ar5008_tx_free(sc);
493bd6ea91dSdamien 	ar5008_rx_free(sc);
494bd6ea91dSdamien }
495bd6ea91dSdamien 
496bd6ea91dSdamien int
ar5008_tx_alloc(struct athn_softc * sc)497bd6ea91dSdamien ar5008_tx_alloc(struct athn_softc *sc)
498bd6ea91dSdamien {
499bd6ea91dSdamien 	struct athn_tx_buf *bf;
500bd6ea91dSdamien 	bus_size_t size;
501bd6ea91dSdamien 	int error, nsegs, i;
502bd6ea91dSdamien 
503bd6ea91dSdamien 	/*
504bd6ea91dSdamien 	 * Allocate a pool of Tx descriptors shared between all Tx queues.
505bd6ea91dSdamien 	 */
506bd6ea91dSdamien 	size = ATHN_NTXBUFS * AR5008_MAX_SCATTER * sizeof(struct ar_tx_desc);
507bd6ea91dSdamien 
508bd6ea91dSdamien 	error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
509bd6ea91dSdamien 	    BUS_DMA_NOWAIT, &sc->map);
510bd6ea91dSdamien 	if (error != 0)
511bd6ea91dSdamien 		goto fail;
512bd6ea91dSdamien 
513bd6ea91dSdamien 	error = bus_dmamem_alloc(sc->sc_dmat, size, 4, 0, &sc->seg, 1,
514bd6ea91dSdamien 	    &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
515bd6ea91dSdamien 	if (error != 0)
516bd6ea91dSdamien 		goto fail;
517bd6ea91dSdamien 
518bd6ea91dSdamien 	error = bus_dmamem_map(sc->sc_dmat, &sc->seg, 1, size,
519bd6ea91dSdamien 	    (caddr_t *)&sc->descs, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
520bd6ea91dSdamien 	if (error != 0)
521bd6ea91dSdamien 		goto fail;
522bd6ea91dSdamien 
523bd6ea91dSdamien 	error = bus_dmamap_load_raw(sc->sc_dmat, sc->map, &sc->seg, 1, size,
524bd6ea91dSdamien 	    BUS_DMA_NOWAIT);
525bd6ea91dSdamien 	if (error != 0)
526bd6ea91dSdamien 		goto fail;
527bd6ea91dSdamien 
528bd6ea91dSdamien 	SIMPLEQ_INIT(&sc->txbufs);
529bd6ea91dSdamien 	for (i = 0; i < ATHN_NTXBUFS; i++) {
530bd6ea91dSdamien 		bf = &sc->txpool[i];
531bd6ea91dSdamien 
532bd6ea91dSdamien 		error = bus_dmamap_create(sc->sc_dmat, ATHN_TXBUFSZ,
533bd6ea91dSdamien 		    AR5008_MAX_SCATTER, ATHN_TXBUFSZ, 0, BUS_DMA_NOWAIT,
534bd6ea91dSdamien 		    &bf->bf_map);
535bd6ea91dSdamien 		if (error != 0) {
536bd6ea91dSdamien 			printf("%s: could not create Tx buf DMA map\n",
537bd6ea91dSdamien 			    sc->sc_dev.dv_xname);
538bd6ea91dSdamien 			goto fail;
539bd6ea91dSdamien 		}
540bd6ea91dSdamien 
541bd6ea91dSdamien 		bf->bf_descs =
542bd6ea91dSdamien 		    &((struct ar_tx_desc *)sc->descs)[i * AR5008_MAX_SCATTER];
543bd6ea91dSdamien 		bf->bf_daddr = sc->map->dm_segs[0].ds_addr +
544bd6ea91dSdamien 		    i * AR5008_MAX_SCATTER * sizeof(struct ar_tx_desc);
545bd6ea91dSdamien 
546bd6ea91dSdamien 		SIMPLEQ_INSERT_TAIL(&sc->txbufs, bf, bf_list);
547bd6ea91dSdamien 	}
548bd6ea91dSdamien 	return (0);
549bd6ea91dSdamien  fail:
550bd6ea91dSdamien 	ar5008_tx_free(sc);
551bd6ea91dSdamien 	return (error);
552bd6ea91dSdamien }
553bd6ea91dSdamien 
554bd6ea91dSdamien void
ar5008_tx_free(struct athn_softc * sc)555bd6ea91dSdamien ar5008_tx_free(struct athn_softc *sc)
556bd6ea91dSdamien {
557bd6ea91dSdamien 	struct athn_tx_buf *bf;
558bd6ea91dSdamien 	int i;
559bd6ea91dSdamien 
560bd6ea91dSdamien 	for (i = 0; i < ATHN_NTXBUFS; i++) {
561bd6ea91dSdamien 		bf = &sc->txpool[i];
562bd6ea91dSdamien 
563bd6ea91dSdamien 		if (bf->bf_map != NULL)
564bd6ea91dSdamien 			bus_dmamap_destroy(sc->sc_dmat, bf->bf_map);
565bd6ea91dSdamien 	}
566bd6ea91dSdamien 	/* Free Tx descriptors. */
567bd6ea91dSdamien 	if (sc->map != NULL) {
568bd6ea91dSdamien 		if (sc->descs != NULL) {
569bd6ea91dSdamien 			bus_dmamap_unload(sc->sc_dmat, sc->map);
570bd6ea91dSdamien 			bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->descs,
571bd6ea91dSdamien 			    ATHN_NTXBUFS * AR5008_MAX_SCATTER *
572bd6ea91dSdamien 			    sizeof(struct ar_tx_desc));
573bd6ea91dSdamien 			bus_dmamem_free(sc->sc_dmat, &sc->seg, 1);
574bd6ea91dSdamien 		}
575bd6ea91dSdamien 		bus_dmamap_destroy(sc->sc_dmat, sc->map);
576bd6ea91dSdamien 	}
577bd6ea91dSdamien }
578bd6ea91dSdamien 
579bd6ea91dSdamien int
ar5008_rx_alloc(struct athn_softc * sc)580bd6ea91dSdamien ar5008_rx_alloc(struct athn_softc *sc)
581bd6ea91dSdamien {
582bd6ea91dSdamien 	struct athn_rxq *rxq = &sc->rxq[0];
583bd6ea91dSdamien 	struct athn_rx_buf *bf;
584bd6ea91dSdamien 	struct ar_rx_desc *ds;
585bd6ea91dSdamien 	bus_size_t size;
586bd6ea91dSdamien 	int error, nsegs, i;
587bd6ea91dSdamien 
5889f6fb5c7Sderaadt 	rxq->bf = mallocarray(ATHN_NRXBUFS, sizeof(*bf), M_DEVBUF,
589bd6ea91dSdamien 	    M_NOWAIT | M_ZERO);
590bd6ea91dSdamien 	if (rxq->bf == NULL)
591bd6ea91dSdamien 		return (ENOMEM);
592bd6ea91dSdamien 
593bd6ea91dSdamien 	size = ATHN_NRXBUFS * sizeof(struct ar_rx_desc);
594bd6ea91dSdamien 
595bd6ea91dSdamien 	error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
596bd6ea91dSdamien 	    BUS_DMA_NOWAIT, &rxq->map);
597bd6ea91dSdamien 	if (error != 0)
598bd6ea91dSdamien 		goto fail;
599bd6ea91dSdamien 
600bd6ea91dSdamien 	error = bus_dmamem_alloc(sc->sc_dmat, size, 0, 0, &rxq->seg, 1,
601bd6ea91dSdamien 	    &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO);
602bd6ea91dSdamien 	if (error != 0)
603bd6ea91dSdamien 		goto fail;
604bd6ea91dSdamien 
605bd6ea91dSdamien 	error = bus_dmamem_map(sc->sc_dmat, &rxq->seg, 1, size,
606bd6ea91dSdamien 	    (caddr_t *)&rxq->descs, BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
607bd6ea91dSdamien 	if (error != 0)
608bd6ea91dSdamien 		goto fail;
609bd6ea91dSdamien 
610bd6ea91dSdamien 	error = bus_dmamap_load_raw(sc->sc_dmat, rxq->map, &rxq->seg, 1,
611bd6ea91dSdamien 	    size, BUS_DMA_NOWAIT);
612bd6ea91dSdamien 	if (error != 0)
613bd6ea91dSdamien 		goto fail;
614bd6ea91dSdamien 
615bd6ea91dSdamien 	for (i = 0; i < ATHN_NRXBUFS; i++) {
616bd6ea91dSdamien 		bf = &rxq->bf[i];
617bd6ea91dSdamien 		ds = &((struct ar_rx_desc *)rxq->descs)[i];
618bd6ea91dSdamien 
619bd6ea91dSdamien 		error = bus_dmamap_create(sc->sc_dmat, ATHN_RXBUFSZ, 1,
620bd6ea91dSdamien 		    ATHN_RXBUFSZ, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
621bd6ea91dSdamien 		    &bf->bf_map);
622bd6ea91dSdamien 		if (error != 0) {
623bd6ea91dSdamien 			printf("%s: could not create Rx buf DMA map\n",
624bd6ea91dSdamien 			    sc->sc_dev.dv_xname);
625bd6ea91dSdamien 			goto fail;
626bd6ea91dSdamien 		}
627bd6ea91dSdamien 		/*
628471f2571Sjan 		 * Assumes MCLGETL returns cache-line-size aligned buffers.
629bd6ea91dSdamien 		 */
630471f2571Sjan 		bf->bf_m = MCLGETL(NULL, M_DONTWAIT, ATHN_RXBUFSZ);
631bd6ea91dSdamien 		if (bf->bf_m == NULL) {
632bd6ea91dSdamien 			printf("%s: could not allocate Rx mbuf\n",
633bd6ea91dSdamien 			    sc->sc_dev.dv_xname);
634bd6ea91dSdamien 			error = ENOBUFS;
635bd6ea91dSdamien 			goto fail;
636bd6ea91dSdamien 		}
637bd6ea91dSdamien 
638bd6ea91dSdamien 		error = bus_dmamap_load(sc->sc_dmat, bf->bf_map,
639bd6ea91dSdamien 		    mtod(bf->bf_m, void *), ATHN_RXBUFSZ, NULL,
640bd6ea91dSdamien 		    BUS_DMA_NOWAIT | BUS_DMA_READ);
641bd6ea91dSdamien 		if (error != 0) {
642bd6ea91dSdamien 			printf("%s: could not DMA map Rx buffer\n",
643bd6ea91dSdamien 			    sc->sc_dev.dv_xname);
644bd6ea91dSdamien 			goto fail;
645bd6ea91dSdamien 		}
646bd6ea91dSdamien 
6476c0255d5Sdamien 		bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, ATHN_RXBUFSZ,
6486c0255d5Sdamien 		    BUS_DMASYNC_PREREAD);
649bd6ea91dSdamien 
650bd6ea91dSdamien 		bf->bf_desc = ds;
651bd6ea91dSdamien 		bf->bf_daddr = rxq->map->dm_segs[0].ds_addr +
652bd6ea91dSdamien 		    i * sizeof(struct ar_rx_desc);
653bd6ea91dSdamien 	}
654bd6ea91dSdamien 	return (0);
655bd6ea91dSdamien  fail:
656bd6ea91dSdamien 	ar5008_rx_free(sc);
657bd6ea91dSdamien 	return (error);
658bd6ea91dSdamien }
659bd6ea91dSdamien 
660bd6ea91dSdamien void
ar5008_rx_free(struct athn_softc * sc)661bd6ea91dSdamien ar5008_rx_free(struct athn_softc *sc)
662bd6ea91dSdamien {
663bd6ea91dSdamien 	struct athn_rxq *rxq = &sc->rxq[0];
664bd6ea91dSdamien 	struct athn_rx_buf *bf;
665bd6ea91dSdamien 	int i;
666bd6ea91dSdamien 
66785aea44dSdamien 	if (rxq->bf == NULL)
66885aea44dSdamien 		return;
669bd6ea91dSdamien 	for (i = 0; i < ATHN_NRXBUFS; i++) {
670bd6ea91dSdamien 		bf = &rxq->bf[i];
671bd6ea91dSdamien 
672bd6ea91dSdamien 		if (bf->bf_map != NULL)
673bd6ea91dSdamien 			bus_dmamap_destroy(sc->sc_dmat, bf->bf_map);
674bd6ea91dSdamien 		m_freem(bf->bf_m);
675bd6ea91dSdamien 	}
676aa3cabd0Stedu 	free(rxq->bf, M_DEVBUF, 0);
67785aea44dSdamien 
678bd6ea91dSdamien 	/* Free Rx descriptors. */
6792f9e70a3Sdamien 	if (rxq->map != NULL) {
680bd6ea91dSdamien 		if (rxq->descs != NULL) {
681bd6ea91dSdamien 			bus_dmamap_unload(sc->sc_dmat, rxq->map);
682bd6ea91dSdamien 			bus_dmamem_unmap(sc->sc_dmat, (caddr_t)rxq->descs,
683bd6ea91dSdamien 			    ATHN_NRXBUFS * sizeof(struct ar_rx_desc));
684bd6ea91dSdamien 			bus_dmamem_free(sc->sc_dmat, &rxq->seg, 1);
685bd6ea91dSdamien 		}
686bd6ea91dSdamien 		bus_dmamap_destroy(sc->sc_dmat, rxq->map);
687bd6ea91dSdamien 	}
688bd6ea91dSdamien }
689bd6ea91dSdamien 
690bd6ea91dSdamien void
ar5008_rx_enable(struct athn_softc * sc)691bd6ea91dSdamien ar5008_rx_enable(struct athn_softc *sc)
692bd6ea91dSdamien {
693bd6ea91dSdamien 	struct athn_rxq *rxq = &sc->rxq[0];
694bd6ea91dSdamien 	struct athn_rx_buf *bf;
695bd6ea91dSdamien 	struct ar_rx_desc *ds;
696bd6ea91dSdamien 	int i;
697bd6ea91dSdamien 
698bd6ea91dSdamien 	/* Setup and link Rx descriptors. */
699bd6ea91dSdamien 	SIMPLEQ_INIT(&rxq->head);
700bd6ea91dSdamien 	rxq->lastds = NULL;
701bd6ea91dSdamien 	for (i = 0; i < ATHN_NRXBUFS; i++) {
702bd6ea91dSdamien 		bf = &rxq->bf[i];
703bd6ea91dSdamien 		ds = bf->bf_desc;
704bd6ea91dSdamien 
705bd6ea91dSdamien 		memset(ds, 0, sizeof(*ds));
706bd6ea91dSdamien 		ds->ds_data = bf->bf_map->dm_segs[0].ds_addr;
707bd6ea91dSdamien 		ds->ds_ctl1 = SM(AR_RXC1_BUF_LEN, ATHN_RXBUFSZ);
708bd6ea91dSdamien 
709bd6ea91dSdamien 		if (rxq->lastds != NULL) {
710bd6ea91dSdamien 			((struct ar_rx_desc *)rxq->lastds)->ds_link =
711bd6ea91dSdamien 			    bf->bf_daddr;
712bd6ea91dSdamien 		}
713bd6ea91dSdamien 		SIMPLEQ_INSERT_TAIL(&rxq->head, bf, bf_list);
714bd6ea91dSdamien 		rxq->lastds = ds;
715bd6ea91dSdamien 	}
716bd6ea91dSdamien 	bus_dmamap_sync(sc->sc_dmat, rxq->map, 0, rxq->map->dm_mapsize,
717bd6ea91dSdamien 	    BUS_DMASYNC_PREREAD);
718bd6ea91dSdamien 
719bd6ea91dSdamien 	/* Enable Rx. */
720bd6ea91dSdamien 	AR_WRITE(sc, AR_RXDP, SIMPLEQ_FIRST(&rxq->head)->bf_daddr);
721bd6ea91dSdamien 	AR_WRITE(sc, AR_CR, AR_CR_RXE);
722c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
723bd6ea91dSdamien }
724bd6ea91dSdamien 
725bd6ea91dSdamien #if NBPFILTER > 0
726bd6ea91dSdamien void
ar5008_rx_radiotap(struct athn_softc * sc,struct mbuf * m,struct ar_rx_desc * ds)727bd6ea91dSdamien ar5008_rx_radiotap(struct athn_softc *sc, struct mbuf *m,
728bd6ea91dSdamien     struct ar_rx_desc *ds)
729bd6ea91dSdamien {
730bd6ea91dSdamien #define IEEE80211_RADIOTAP_F_SHORTGI	0x80	/* XXX from FBSD */
731bd6ea91dSdamien 
732bd6ea91dSdamien 	struct athn_rx_radiotap_header *tap = &sc->sc_rxtap;
733bd6ea91dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
734bd6ea91dSdamien 	uint64_t tsf;
735bd6ea91dSdamien 	uint32_t tstamp;
736bd6ea91dSdamien 	uint8_t rate;
737bd6ea91dSdamien 
738bd6ea91dSdamien 	/* Extend the 15-bit timestamp from Rx descriptor to 64-bit TSF. */
739bd6ea91dSdamien 	tstamp = ds->ds_status2;
740bd6ea91dSdamien 	tsf = AR_READ(sc, AR_TSF_U32);
741bd6ea91dSdamien 	tsf = tsf << 32 | AR_READ(sc, AR_TSF_L32);
742bd6ea91dSdamien 	if ((tsf & 0x7fff) < tstamp)
743bd6ea91dSdamien 		tsf -= 0x8000;
744bd6ea91dSdamien 	tsf = (tsf & ~0x7fff) | tstamp;
745bd6ea91dSdamien 
746bd6ea91dSdamien 	tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
747bd6ea91dSdamien 	tap->wr_tsft = htole64(tsf);
748bd6ea91dSdamien 	tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
749bd6ea91dSdamien 	tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
750bd6ea91dSdamien 	tap->wr_dbm_antsignal = MS(ds->ds_status4, AR_RXS4_RSSI_COMBINED);
751bd6ea91dSdamien 	/* XXX noise. */
752bd6ea91dSdamien 	tap->wr_antenna = MS(ds->ds_status3, AR_RXS3_ANTENNA);
753bd6ea91dSdamien 	tap->wr_rate = 0;	/* In case it can't be found below. */
754bd6ea91dSdamien 	if (AR_SREV_5416_20_OR_LATER(sc))
755bd6ea91dSdamien 		rate = MS(ds->ds_status0, AR_RXS0_RATE);
756bd6ea91dSdamien 	else
757bd6ea91dSdamien 		rate = MS(ds->ds_status3, AR_RXS3_RATE);
758bd6ea91dSdamien 	if (rate & 0x80) {		/* HT. */
759bd6ea91dSdamien 		/* Bit 7 set means HT MCS instead of rate. */
760bd6ea91dSdamien 		tap->wr_rate = rate;
761bd6ea91dSdamien 		if (!(ds->ds_status3 & AR_RXS3_GI))
762bd6ea91dSdamien 			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
763bd6ea91dSdamien 
764bd6ea91dSdamien 	} else if (rate & 0x10) {	/* CCK. */
765bd6ea91dSdamien 		if (rate & 0x04)
766bd6ea91dSdamien 			tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
767bd6ea91dSdamien 		switch (rate & ~0x14) {
768bd6ea91dSdamien 		case 0xb: tap->wr_rate =   2; break;
769bd6ea91dSdamien 		case 0xa: tap->wr_rate =   4; break;
770bd6ea91dSdamien 		case 0x9: tap->wr_rate =  11; break;
771bd6ea91dSdamien 		case 0x8: tap->wr_rate =  22; break;
772bd6ea91dSdamien 		}
773bd6ea91dSdamien 	} else {			/* OFDM. */
774bd6ea91dSdamien 		switch (rate) {
775bd6ea91dSdamien 		case 0xb: tap->wr_rate =  12; break;
776bd6ea91dSdamien 		case 0xf: tap->wr_rate =  18; break;
777bd6ea91dSdamien 		case 0xa: tap->wr_rate =  24; break;
778bd6ea91dSdamien 		case 0xe: tap->wr_rate =  36; break;
779bd6ea91dSdamien 		case 0x9: tap->wr_rate =  48; break;
780bd6ea91dSdamien 		case 0xd: tap->wr_rate =  72; break;
781bd6ea91dSdamien 		case 0x8: tap->wr_rate =  96; break;
782bd6ea91dSdamien 		case 0xc: tap->wr_rate = 108; break;
783bd6ea91dSdamien 		}
784bd6ea91dSdamien 	}
785526f68fbSclaudio 	bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m, BPF_DIRECTION_IN);
786bd6ea91dSdamien }
787bd6ea91dSdamien #endif
788bd6ea91dSdamien 
789ff1dd4b7Sstsp int
ar5008_ccmp_decap(struct athn_softc * sc,struct mbuf * m,struct ieee80211_node * ni)790ff1dd4b7Sstsp ar5008_ccmp_decap(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
791ff1dd4b7Sstsp {
792ff1dd4b7Sstsp 	struct ieee80211com *ic = &sc->sc_ic;
793ff1dd4b7Sstsp 	struct ieee80211_key *k;
794ff1dd4b7Sstsp 	struct ieee80211_frame *wh;
795ff1dd4b7Sstsp 	struct ieee80211_rx_ba *ba;
796ff1dd4b7Sstsp 	uint64_t pn, *prsc;
797e995d523Sstsp 	u_int8_t *ivp;
798ff1dd4b7Sstsp 	uint8_t tid;
799ff1dd4b7Sstsp 	int hdrlen, hasqos;
800ff1dd4b7Sstsp 	uintptr_t entry;
801ff1dd4b7Sstsp 
802ff1dd4b7Sstsp 	wh = mtod(m, struct ieee80211_frame *);
803ff1dd4b7Sstsp 	hdrlen = ieee80211_get_hdrlen(wh);
804ff1dd4b7Sstsp 	ivp = mtod(m, u_int8_t *) + hdrlen;
805ff1dd4b7Sstsp 
806ff1dd4b7Sstsp 	/* find key for decryption */
807e995d523Sstsp 	k = ieee80211_get_rxkey(ic, m, ni);
808e995d523Sstsp 	if (k == NULL || k->k_cipher != IEEE80211_CIPHER_CCMP)
809ff1dd4b7Sstsp 		return 1;
810ff1dd4b7Sstsp 
811ff1dd4b7Sstsp 	/* Sanity checks to ensure this is really a key we installed. */
812ff1dd4b7Sstsp 	entry = (uintptr_t)k->k_priv;
813ff1dd4b7Sstsp 	if (k->k_flags & IEEE80211_KEY_GROUP) {
814797d2c3eSstsp 		if (k->k_id >= IEEE80211_WEP_NKID ||
815ff1dd4b7Sstsp 		    entry != k->k_id)
816ff1dd4b7Sstsp 			return 1;
817797d2c3eSstsp 	} else {
818797d2c3eSstsp #ifndef IEEE80211_STA_ONLY
819797d2c3eSstsp 		if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
820797d2c3eSstsp 			if (entry != IEEE80211_WEP_NKID +
821ff1dd4b7Sstsp 			    IEEE80211_AID(ni->ni_associd))
822ff1dd4b7Sstsp 				return 1;
823797d2c3eSstsp 		} else
824797d2c3eSstsp #endif
825797d2c3eSstsp 			if (entry != IEEE80211_WEP_NKID)
826797d2c3eSstsp 				return 1;
827797d2c3eSstsp 	}
828ff1dd4b7Sstsp 
829f3701207Sstsp 	/* Check that ExtIV bit is set. */
830ff1dd4b7Sstsp 	if (!(ivp[3] & IEEE80211_WEP_EXTIV))
831ff1dd4b7Sstsp 		return 1;
832ff1dd4b7Sstsp 
833ff1dd4b7Sstsp 	hasqos = ieee80211_has_qos(wh);
834ff1dd4b7Sstsp 	tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0;
835ff1dd4b7Sstsp 	ba = hasqos ? &ni->ni_rx_ba[tid] : NULL;
836e995d523Sstsp 	prsc = &k->k_rsc[tid];
837ff1dd4b7Sstsp 
838ff1dd4b7Sstsp 	/* Extract the 48-bit PN from the CCMP header. */
839ff1dd4b7Sstsp 	pn = (uint64_t)ivp[0]       |
840ff1dd4b7Sstsp 	     (uint64_t)ivp[1] <<  8 |
841ff1dd4b7Sstsp 	     (uint64_t)ivp[4] << 16 |
842ff1dd4b7Sstsp 	     (uint64_t)ivp[5] << 24 |
843ff1dd4b7Sstsp 	     (uint64_t)ivp[6] << 32 |
844ff1dd4b7Sstsp 	     (uint64_t)ivp[7] << 40;
845ff1dd4b7Sstsp 	if (pn <= *prsc) {
846ff1dd4b7Sstsp 		ic->ic_stats.is_ccmp_replays++;
847ff1dd4b7Sstsp 		return 1;
848ff1dd4b7Sstsp 	}
849e995d523Sstsp 	/* Last seen packet number is updated in ieee80211_inputm(). */
850ff1dd4b7Sstsp 
851e995d523Sstsp 	/* Strip MIC. IV will be stripped by ieee80211_inputm(). */
852ff1dd4b7Sstsp 	m_adj(m, -IEEE80211_CCMP_MICLEN);
853ff1dd4b7Sstsp 	return 0;
854ff1dd4b7Sstsp }
855ff1dd4b7Sstsp 
856bd6ea91dSdamien static __inline int
ar5008_rx_process(struct athn_softc * sc,struct mbuf_list * ml)8578fbaf8a2Sstsp ar5008_rx_process(struct athn_softc *sc, struct mbuf_list *ml)
858bd6ea91dSdamien {
859bd6ea91dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
860bd6ea91dSdamien 	struct ifnet *ifp = &ic->ic_if;
861bd6ea91dSdamien 	struct athn_rxq *rxq = &sc->rxq[0];
862bd6ea91dSdamien 	struct athn_rx_buf *bf, *nbf;
863bd6ea91dSdamien 	struct ar_rx_desc *ds;
864bd6ea91dSdamien 	struct ieee80211_frame *wh;
865bd6ea91dSdamien 	struct ieee80211_rxinfo rxi;
866bd6ea91dSdamien 	struct ieee80211_node *ni;
867bd6ea91dSdamien 	struct mbuf *m, *m1;
8680c4e73bbSstsp 	int error, len, michael_mic_failure = 0;
869bd6ea91dSdamien 
870bd6ea91dSdamien 	bf = SIMPLEQ_FIRST(&rxq->head);
871bd6ea91dSdamien 	if (__predict_false(bf == NULL)) {	/* Should not happen. */
872bd6ea91dSdamien 		printf("%s: Rx queue is empty!\n", sc->sc_dev.dv_xname);
873bd6ea91dSdamien 		return (ENOENT);
874bd6ea91dSdamien 	}
875bd6ea91dSdamien 	ds = bf->bf_desc;
876bd6ea91dSdamien 
877bd6ea91dSdamien 	if (!(ds->ds_status8 & AR_RXS8_DONE)) {
878bd6ea91dSdamien 		/*
879bd6ea91dSdamien 		 * On some parts, the status words can get corrupted
880bd6ea91dSdamien 		 * (including the "done" bit), so we check the next
881bd6ea91dSdamien 		 * descriptor "done" bit.  If it is set, it is a good
882bd6ea91dSdamien 		 * indication that the status words are corrupted, so
883bd6ea91dSdamien 		 * we skip this descriptor and drop the frame.
884bd6ea91dSdamien 		 */
885bd6ea91dSdamien 		nbf = SIMPLEQ_NEXT(bf, bf_list);
886bd6ea91dSdamien 		if (nbf != NULL &&
887bd6ea91dSdamien 		    (((struct ar_rx_desc *)nbf->bf_desc)->ds_status8 &
888bd6ea91dSdamien 		     AR_RXS8_DONE)) {
889bd6ea91dSdamien 			DPRINTF(("corrupted descriptor status=0x%x\n",
890bd6ea91dSdamien 			    ds->ds_status8));
891bd6ea91dSdamien 			/* HW will not "move" RXDP in this case, so do it. */
892bd6ea91dSdamien 			AR_WRITE(sc, AR_RXDP, nbf->bf_daddr);
893c0a11cf8Sdamien 			AR_WRITE_BARRIER(sc);
894bd6ea91dSdamien 			ifp->if_ierrors++;
895bd6ea91dSdamien 			goto skip;
896bd6ea91dSdamien 		}
897bd6ea91dSdamien 		return (EBUSY);
898bd6ea91dSdamien 	}
899bd6ea91dSdamien 
900bd6ea91dSdamien 	if (__predict_false(ds->ds_status1 & AR_RXS1_MORE)) {
901bd6ea91dSdamien 		/* Drop frames that span multiple Rx descriptors. */
902bd6ea91dSdamien 		DPRINTF(("dropping split frame\n"));
903bd6ea91dSdamien 		ifp->if_ierrors++;
904bd6ea91dSdamien 		goto skip;
905bd6ea91dSdamien 	}
906bd6ea91dSdamien 	if (!(ds->ds_status8 & AR_RXS8_FRAME_OK)) {
907bd6ea91dSdamien 		if (ds->ds_status8 & AR_RXS8_CRC_ERR)
908bd6ea91dSdamien 			DPRINTFN(6, ("CRC error\n"));
909bd6ea91dSdamien 		else if (ds->ds_status8 & AR_RXS8_PHY_ERR)
910bd6ea91dSdamien 			DPRINTFN(6, ("PHY error=0x%x\n",
911bd6ea91dSdamien 			    MS(ds->ds_status8, AR_RXS8_PHY_ERR_CODE)));
912b2d0c33fSstsp 		else if (ds->ds_status8 & (AR_RXS8_DECRYPT_CRC_ERR |
913b2d0c33fSstsp 		    AR_RXS8_KEY_MISS | AR_RXS8_DECRYPT_BUSY_ERR)) {
914bd6ea91dSdamien 			DPRINTFN(6, ("Decryption CRC error\n"));
915ff1dd4b7Sstsp 			ic->ic_stats.is_ccmp_dec_errs++;
916ff1dd4b7Sstsp 		} else if (ds->ds_status8 & AR_RXS8_MICHAEL_ERR) {
917bd6ea91dSdamien 			DPRINTFN(2, ("Michael MIC failure\n"));
9180c4e73bbSstsp 			michael_mic_failure = 1;
919b2d0c33fSstsp 		}
9200c4e73bbSstsp 		if (!michael_mic_failure) {
921bd6ea91dSdamien 			ifp->if_ierrors++;
922bd6ea91dSdamien 			goto skip;
923bd6ea91dSdamien 		}
9246dc21066Skettenis 	} else {
9256dc21066Skettenis 		if (ds->ds_status8 & (AR_RXS8_CRC_ERR | AR_RXS8_PHY_ERR |
9266dc21066Skettenis 		    AR_RXS8_DECRYPT_CRC_ERR | AR_RXS8_MICHAEL_ERR)) {
9276dc21066Skettenis 			ifp->if_ierrors++;
9286dc21066Skettenis 			goto skip;
9296dc21066Skettenis 		}
9300c4e73bbSstsp 	}
931bd6ea91dSdamien 
932bd6ea91dSdamien 	len = MS(ds->ds_status1, AR_RXS1_DATA_LEN);
9336c0255d5Sdamien 	if (__predict_false(len < IEEE80211_MIN_LEN || len > ATHN_RXBUFSZ)) {
934bd6ea91dSdamien 		DPRINTF(("corrupted descriptor length=%d\n", len));
935bd6ea91dSdamien 		ifp->if_ierrors++;
936bd6ea91dSdamien 		goto skip;
937bd6ea91dSdamien 	}
938bd6ea91dSdamien 
939bd6ea91dSdamien 	/* Allocate a new Rx buffer. */
940471f2571Sjan 	m1 = MCLGETL(NULL, M_DONTWAIT, ATHN_RXBUFSZ);
941bd6ea91dSdamien 	if (__predict_false(m1 == NULL)) {
942bd6ea91dSdamien 		ic->ic_stats.is_rx_nombuf++;
943bd6ea91dSdamien 		ifp->if_ierrors++;
944bd6ea91dSdamien 		goto skip;
945bd6ea91dSdamien 	}
946bd6ea91dSdamien 
947bd6ea91dSdamien 	/* Sync and unmap the old Rx buffer. */
948bd6ea91dSdamien 	bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, ATHN_RXBUFSZ,
949bd6ea91dSdamien 	    BUS_DMASYNC_POSTREAD);
950bd6ea91dSdamien 	bus_dmamap_unload(sc->sc_dmat, bf->bf_map);
951bd6ea91dSdamien 
952bd6ea91dSdamien 	/* Map the new Rx buffer. */
953bd6ea91dSdamien 	error = bus_dmamap_load(sc->sc_dmat, bf->bf_map, mtod(m1, void *),
954bd6ea91dSdamien 	    ATHN_RXBUFSZ, NULL, BUS_DMA_NOWAIT | BUS_DMA_READ);
955bd6ea91dSdamien 	if (__predict_false(error != 0)) {
956bd6ea91dSdamien 		m_freem(m1);
957bd6ea91dSdamien 
958bd6ea91dSdamien 		/* Remap the old Rx buffer or panic. */
959bd6ea91dSdamien 		error = bus_dmamap_load(sc->sc_dmat, bf->bf_map,
960bd6ea91dSdamien 		    mtod(bf->bf_m, void *), ATHN_RXBUFSZ, NULL,
961bd6ea91dSdamien 		    BUS_DMA_NOWAIT | BUS_DMA_READ);
962bd6ea91dSdamien 		KASSERT(error != 0);
963bd6ea91dSdamien 		ifp->if_ierrors++;
964bd6ea91dSdamien 		goto skip;
965bd6ea91dSdamien 	}
966bd6ea91dSdamien 
96794261de9Sdamien 	bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, ATHN_RXBUFSZ,
968bd6ea91dSdamien 	    BUS_DMASYNC_PREREAD);
969bd6ea91dSdamien 
970bd6ea91dSdamien 	/* Write physical address of new Rx buffer. */
971bd6ea91dSdamien 	ds->ds_data = bf->bf_map->dm_segs[0].ds_addr;
972bd6ea91dSdamien 
973bd6ea91dSdamien 	m = bf->bf_m;
974bd6ea91dSdamien 	bf->bf_m = m1;
975bd6ea91dSdamien 
976bd6ea91dSdamien 	/* Finalize mbuf. */
977bd6ea91dSdamien 	m->m_pkthdr.len = m->m_len = len;
978bd6ea91dSdamien 
979bd6ea91dSdamien 	wh = mtod(m, struct ieee80211_frame *);
980bd6ea91dSdamien 
9810c4e73bbSstsp 	if (michael_mic_failure) {
9820c4e73bbSstsp 		/*
9830c4e73bbSstsp 		 * Check that it is not a control frame
9840c4e73bbSstsp 		 * (invalid MIC failures on valid ctl frames).
985c253dea3Sstsp 		 * Validate the transmitter's address to avoid passing
986c253dea3Sstsp 		 * corrupt frames with bogus addresses to net80211.
9870c4e73bbSstsp 		 */
988c253dea3Sstsp 		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) {
989c253dea3Sstsp 			switch (ic->ic_opmode) {
990c253dea3Sstsp #ifndef IEEE80211_STA_ONLY
991c253dea3Sstsp 			case IEEE80211_M_HOSTAP:
992c253dea3Sstsp 				if (ieee80211_find_node(ic, wh->i_addr2))
993c253dea3Sstsp 					michael_mic_failure = 0;
994c253dea3Sstsp 				break;
995c253dea3Sstsp #endif
996c253dea3Sstsp 			case IEEE80211_M_STA:
997c253dea3Sstsp 				if (IEEE80211_ADDR_EQ(wh->i_addr2,
998c253dea3Sstsp 				    ic->ic_bss->ni_macaddr))
999c253dea3Sstsp 					michael_mic_failure = 0;
1000c253dea3Sstsp 				break;
1001c253dea3Sstsp 			case IEEE80211_M_MONITOR:
1002c253dea3Sstsp 				michael_mic_failure = 0;
1003c253dea3Sstsp 				break;
1004c253dea3Sstsp 			default:
1005c253dea3Sstsp 				break;
1006c253dea3Sstsp 			}
1007c253dea3Sstsp 		}
1008c253dea3Sstsp 
1009c253dea3Sstsp 		if (michael_mic_failure) {
10100c4e73bbSstsp  			/* Report Michael MIC failures to net80211. */
1011c253dea3Sstsp 			if ((ic->ic_rsnciphers & IEEE80211_CIPHER_TKIP) ||
1012c253dea3Sstsp 			    ic->ic_rsngroupcipher == IEEE80211_CIPHER_TKIP) {
10130c4e73bbSstsp 				ic->ic_stats.is_rx_locmicfail++;
10140c4e73bbSstsp 				ieee80211_michael_mic_failure(ic, 0);
1015c253dea3Sstsp 			}
10160c4e73bbSstsp 			ifp->if_ierrors++;
10170c4e73bbSstsp 			m_freem(m);
10180c4e73bbSstsp 			goto skip;
10190c4e73bbSstsp 		}
10200c4e73bbSstsp 	}
10210c4e73bbSstsp 
1022c253dea3Sstsp 	/* Grab a reference to the source node. */
1023c253dea3Sstsp 	ni = ieee80211_find_rxnode(ic, wh);
1024c253dea3Sstsp 
1025bd6ea91dSdamien 	/* Remove any HW padding after the 802.11 header. */
1026bd6ea91dSdamien 	if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) {
1027bd6ea91dSdamien 		u_int hdrlen = ieee80211_get_hdrlen(wh);
1028bd6ea91dSdamien 		if (hdrlen & 3) {
10298f51fbe3Sderaadt 			memmove((caddr_t)wh + 2, wh, hdrlen);
10306c0255d5Sdamien 			m_adj(m, 2);
1031bd6ea91dSdamien 		}
1032ff1dd4b7Sstsp 		wh = mtod(m, struct ieee80211_frame *);
1033bd6ea91dSdamien 	}
1034bd6ea91dSdamien #if NBPFILTER > 0
1035bd6ea91dSdamien 	if (__predict_false(sc->sc_drvbpf != NULL))
1036bd6ea91dSdamien 		ar5008_rx_radiotap(sc, m, ds);
1037bd6ea91dSdamien #endif
1038bd6ea91dSdamien 	/* Trim 802.11 FCS after radiotap. */
1039bd6ea91dSdamien 	m_adj(m, -IEEE80211_CRC_LEN);
1040bd6ea91dSdamien 
1041bd6ea91dSdamien 	/* Send the frame to the 802.11 layer. */
104252a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
1043bd6ea91dSdamien 	rxi.rxi_rssi = MS(ds->ds_status4, AR_RXS4_RSSI_COMBINED);
104404586493Sstsp 	rxi.rxi_rssi += AR_DEFAULT_NOISE_FLOOR;
1045bd6ea91dSdamien 	rxi.rxi_tstamp = ds->ds_status2;
1046ff1dd4b7Sstsp 	if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL) &&
1047ff1dd4b7Sstsp 	    (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
1048ff1dd4b7Sstsp 	    (ic->ic_flags & IEEE80211_F_RSNON) &&
1049ff1dd4b7Sstsp 	    (ni->ni_flags & IEEE80211_NODE_RXPROT) &&
1050c45f4c72Sstsp 	    ((!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
1051c45f4c72Sstsp 	    ni->ni_rsncipher == IEEE80211_CIPHER_CCMP) ||
1052ff1dd4b7Sstsp 	    (IEEE80211_IS_MULTICAST(wh->i_addr1) &&
105386e3bb8aSstsp 	    ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP))) {
1054ff1dd4b7Sstsp 		if (ar5008_ccmp_decap(sc, m, ni) != 0) {
1055ff1dd4b7Sstsp 			ifp->if_ierrors++;
1056ff1dd4b7Sstsp 			ieee80211_release_node(ic, ni);
1057ff1dd4b7Sstsp 			m_freem(m);
1058ff1dd4b7Sstsp 			goto skip;
1059ff1dd4b7Sstsp 		}
1060ff1dd4b7Sstsp 		rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
1061ff1dd4b7Sstsp 	}
10628fbaf8a2Sstsp 	ieee80211_inputm(ifp, m, ni, &rxi, ml);
1063bd6ea91dSdamien 
1064bd6ea91dSdamien 	/* Node is no longer needed. */
1065bd6ea91dSdamien 	ieee80211_release_node(ic, ni);
1066bd6ea91dSdamien 
1067bd6ea91dSdamien  skip:
1068bd6ea91dSdamien 	/* Unlink this descriptor from head. */
1069bd6ea91dSdamien 	SIMPLEQ_REMOVE_HEAD(&rxq->head, bf_list);
1070bd6ea91dSdamien 	memset(&ds->ds_status0, 0, 36);	/* XXX Really needed? */
1071bd6ea91dSdamien 	ds->ds_status8 &= ~AR_RXS8_DONE;
1072bd6ea91dSdamien 	ds->ds_link = 0;
1073bd6ea91dSdamien 
1074bd6ea91dSdamien 	/* Re-use this descriptor and link it to tail. */
1075bd6ea91dSdamien 	if (__predict_true(!SIMPLEQ_EMPTY(&rxq->head)))
1076bd6ea91dSdamien 		((struct ar_rx_desc *)rxq->lastds)->ds_link = bf->bf_daddr;
1077bd6ea91dSdamien 	else
1078bd6ea91dSdamien 		AR_WRITE(sc, AR_RXDP, bf->bf_daddr);
1079bd6ea91dSdamien 	SIMPLEQ_INSERT_TAIL(&rxq->head, bf, bf_list);
1080bd6ea91dSdamien 	rxq->lastds = ds;
1081bd6ea91dSdamien 
1082bd6ea91dSdamien 	/* Re-enable Rx. */
1083bd6ea91dSdamien 	AR_WRITE(sc, AR_CR, AR_CR_RXE);
1084c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1085bd6ea91dSdamien 	return (0);
1086bd6ea91dSdamien }
1087bd6ea91dSdamien 
1088bd6ea91dSdamien void
ar5008_rx_intr(struct athn_softc * sc)1089bd6ea91dSdamien ar5008_rx_intr(struct athn_softc *sc)
1090bd6ea91dSdamien {
10918fbaf8a2Sstsp 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
10928fbaf8a2Sstsp 	struct ieee80211com *ic = &sc->sc_ic;
10938fbaf8a2Sstsp 	struct ifnet *ifp = &ic->ic_if;
10948fbaf8a2Sstsp 
10958fbaf8a2Sstsp 	while (ar5008_rx_process(sc, &ml) == 0);
10968fbaf8a2Sstsp 
10978fbaf8a2Sstsp 	if_input(ifp, &ml);
1098bd6ea91dSdamien }
1099bd6ea91dSdamien 
1100bd6ea91dSdamien int
ar5008_tx_process(struct athn_softc * sc,int qid)1101bd6ea91dSdamien ar5008_tx_process(struct athn_softc *sc, int qid)
1102bd6ea91dSdamien {
1103bd6ea91dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
1104bd6ea91dSdamien 	struct ifnet *ifp = &ic->ic_if;
1105bd6ea91dSdamien 	struct athn_txq *txq = &sc->txq[qid];
1106bd6ea91dSdamien 	struct athn_node *an;
11077363c99eSstsp 	struct ieee80211_node *ni;
1108bd6ea91dSdamien 	struct athn_tx_buf *bf;
1109bd6ea91dSdamien 	struct ar_tx_desc *ds;
1110bd6ea91dSdamien 	uint8_t failcnt;
1111d2dd70acSstsp 	int txfail = 0, rtscts;
1112bd6ea91dSdamien 
1113bd6ea91dSdamien 	bf = SIMPLEQ_FIRST(&txq->head);
11146c0255d5Sdamien 	if (bf == NULL)
1115bd6ea91dSdamien 		return (ENOENT);
1116bd6ea91dSdamien 	/* Get descriptor of last DMA segment. */
1117bd6ea91dSdamien 	ds = &((struct ar_tx_desc *)bf->bf_descs)[bf->bf_map->dm_nsegs - 1];
1118bd6ea91dSdamien 
1119bd6ea91dSdamien 	if (!(ds->ds_status9 & AR_TXS9_DONE))
1120bd6ea91dSdamien 		return (EBUSY);
1121bd6ea91dSdamien 
1122bd6ea91dSdamien 	SIMPLEQ_REMOVE_HEAD(&txq->head, bf_list);
1123bd6ea91dSdamien 
1124bd6ea91dSdamien 	sc->sc_tx_timer = 0;
1125bd6ea91dSdamien 
1126d2dd70acSstsp 	/* These status bits are valid if “FRM_XMIT_OK” is clear. */
1127d2dd70acSstsp 	if ((ds->ds_status1 & AR_TXS1_FRM_XMIT_OK) == 0) {
11287363c99eSstsp 		txfail = (ds->ds_status1 & AR_TXS1_EXCESSIVE_RETRIES);
11297363c99eSstsp 		if (txfail)
1130bd6ea91dSdamien 			ifp->if_oerrors++;
1131bd6ea91dSdamien 		if (ds->ds_status1 & AR_TXS1_UNDERRUN)
1132bd6ea91dSdamien 			athn_inc_tx_trigger_level(sc);
1133d2dd70acSstsp 	}
1134bd6ea91dSdamien 
1135bd6ea91dSdamien 	an = (struct athn_node *)bf->bf_ni;
11367363c99eSstsp 	ni = (struct ieee80211_node *)bf->bf_ni;
11377363c99eSstsp 
1138bd6ea91dSdamien 	/*
1139bd6ea91dSdamien 	 * NB: the data fail count contains the number of un-acked tries
1140bd6ea91dSdamien 	 * for the final series used.  We must add the number of tries for
11414b798cb4Sstsp 	 * each series that was fully processed to punish transmit rates in
11424b798cb4Sstsp 	 * the earlier series which did not perform well.
1143bd6ea91dSdamien 	 */
1144bd6ea91dSdamien 	failcnt  = MS(ds->ds_status1, AR_TXS1_DATA_FAIL_CNT);
1145d2dd70acSstsp 	/* Assume two tries per series, as per AR_TXC2_XMIT_DATA_TRIESx. */
1146bd6ea91dSdamien 	failcnt += MS(ds->ds_status9, AR_TXS9_FINAL_IDX) * 2;
1147d2dd70acSstsp 
1148d2dd70acSstsp 	rtscts = (ds->ds_ctl0 & (AR_TXC0_RTS_ENABLE | AR_TXC0_CTS_ENABLE));
1149bd6ea91dSdamien 
1150bd6ea91dSdamien 	/* Update rate control statistics. */
1151d2dd70acSstsp 	if ((ni->ni_flags & IEEE80211_NODE_HT) && ic->ic_fixed_mcs == -1) {
1152d2dd70acSstsp 		const struct ieee80211_ht_rateset *rs =
1153ca785ca0Sstsp 		    ieee80211_ra_get_ht_rateset(bf->bf_txmcs, 0 /* chan40 */,
1154d2dd70acSstsp 		    ieee80211_node_supports_ht_sgi20(ni));
1155d2dd70acSstsp 		unsigned int retries = 0, i;
1156d2dd70acSstsp 		int mcs = bf->bf_txmcs;
1157d2dd70acSstsp 
1158d2dd70acSstsp 		/* With RTS/CTS each Tx series used the same MCS. */
1159d2dd70acSstsp 		if (rtscts) {
1160d2dd70acSstsp 			retries = failcnt;
1161d2dd70acSstsp 		} else {
1162d2dd70acSstsp 			for (i = 0; i < failcnt; i++) {
1163d2dd70acSstsp 				if (mcs > rs->min_mcs) {
1164d2dd70acSstsp 					ieee80211_ra_add_stats_ht(&an->rn,
1165d2dd70acSstsp 					    ic, ni, mcs, 1, 1);
1166d2dd70acSstsp 					if (i % 2) /* two tries per series */
1167d2dd70acSstsp 						mcs--;
1168d2dd70acSstsp 				} else
1169d2dd70acSstsp 					retries++;
1170d2dd70acSstsp 			}
1171d2dd70acSstsp 		}
1172d2dd70acSstsp 
1173d2dd70acSstsp 		if (txfail && retries == 0) {
1174d2dd70acSstsp 			ieee80211_ra_add_stats_ht(&an->rn, ic, ni,
1175d2dd70acSstsp 			    mcs, 1, 1);
1176d2dd70acSstsp 		} else {
1177d2dd70acSstsp 			ieee80211_ra_add_stats_ht(&an->rn, ic, ni,
1178d2dd70acSstsp 			    mcs, retries + 1, retries);
1179d2dd70acSstsp 		}
11807363c99eSstsp 		if (ic->ic_state == IEEE80211_S_RUN) {
11817363c99eSstsp #ifndef IEEE80211_STA_ONLY
11827363c99eSstsp 			if (ic->ic_opmode != IEEE80211_M_HOSTAP ||
11837363c99eSstsp 			    ni->ni_state == IEEE80211_STA_ASSOC)
11847363c99eSstsp #endif
1185d2dd70acSstsp 				ieee80211_ra_choose(&an->rn, ic, ni);
11867363c99eSstsp 		}
1187d2dd70acSstsp 	} else if (ic->ic_fixed_rate == -1) {
1188bd6ea91dSdamien 		an->amn.amn_txcnt++;
1189bd6ea91dSdamien 		if (failcnt > 0)
1190bd6ea91dSdamien 			an->amn.amn_retrycnt++;
11917363c99eSstsp 	}
1192bd6ea91dSdamien 	DPRINTFN(5, ("Tx done qid=%d status1=%d fail count=%d\n",
1193bd6ea91dSdamien 	    qid, ds->ds_status1, failcnt));
1194bd6ea91dSdamien 
1195bd6ea91dSdamien 	bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, bf->bf_map->dm_mapsize,
1196bd6ea91dSdamien 	    BUS_DMASYNC_POSTWRITE);
1197bd6ea91dSdamien 	bus_dmamap_unload(sc->sc_dmat, bf->bf_map);
1198bd6ea91dSdamien 
1199bd6ea91dSdamien 	m_freem(bf->bf_m);
1200bd6ea91dSdamien 	bf->bf_m = NULL;
1201bd6ea91dSdamien 	ieee80211_release_node(ic, bf->bf_ni);
1202bd6ea91dSdamien 	bf->bf_ni = NULL;
1203bd6ea91dSdamien 
1204bd6ea91dSdamien 	/* Link Tx buffer back to global free list. */
1205bd6ea91dSdamien 	SIMPLEQ_INSERT_TAIL(&sc->txbufs, bf, bf_list);
1206bd6ea91dSdamien 	return (0);
1207bd6ea91dSdamien }
1208bd6ea91dSdamien 
1209bd6ea91dSdamien void
ar5008_tx_intr(struct athn_softc * sc)1210bd6ea91dSdamien ar5008_tx_intr(struct athn_softc *sc)
1211bd6ea91dSdamien {
1212bd6ea91dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
1213bd6ea91dSdamien 	struct ifnet *ifp = &ic->ic_if;
1214bd6ea91dSdamien 	uint16_t mask = 0;
1215bd6ea91dSdamien 	uint32_t reg;
1216bd6ea91dSdamien 	int qid;
1217bd6ea91dSdamien 
1218bd6ea91dSdamien 	reg = AR_READ(sc, AR_ISR_S0_S);
1219bd6ea91dSdamien 	mask |= MS(reg, AR_ISR_S0_QCU_TXOK);
1220bd6ea91dSdamien 	mask |= MS(reg, AR_ISR_S0_QCU_TXDESC);
1221bd6ea91dSdamien 
1222bd6ea91dSdamien 	reg = AR_READ(sc, AR_ISR_S1_S);
1223bd6ea91dSdamien 	mask |= MS(reg, AR_ISR_S1_QCU_TXERR);
1224bd6ea91dSdamien 	mask |= MS(reg, AR_ISR_S1_QCU_TXEOL);
1225bd6ea91dSdamien 
1226bd6ea91dSdamien 	DPRINTFN(4, ("Tx interrupt mask=0x%x\n", mask));
1227bd6ea91dSdamien 	for (qid = 0; mask != 0; mask >>= 1, qid++) {
1228bd6ea91dSdamien 		if (mask & 1)
1229bd6ea91dSdamien 			while (ar5008_tx_process(sc, qid) == 0);
1230bd6ea91dSdamien 	}
1231bd6ea91dSdamien 	if (!SIMPLEQ_EMPTY(&sc->txbufs)) {
1232de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
1233bd6ea91dSdamien 		ifp->if_start(ifp);
1234bd6ea91dSdamien 	}
1235bd6ea91dSdamien }
1236bd6ea91dSdamien 
1237eff5798eSdamien #ifndef IEEE80211_STA_ONLY
1238eff5798eSdamien /*
1239eff5798eSdamien  * Process Software Beacon Alert interrupts.
1240eff5798eSdamien  */
1241eff5798eSdamien int
ar5008_swba_intr(struct athn_softc * sc)1242eff5798eSdamien ar5008_swba_intr(struct athn_softc *sc)
1243eff5798eSdamien {
1244eff5798eSdamien 	struct ieee80211com *ic = &sc->sc_ic;
12455dde5fe4Skettenis 	struct ifnet *ifp = &ic->ic_if;
12465dde5fe4Skettenis 	struct ieee80211_node *ni = ic->ic_bss;
1247eff5798eSdamien 	struct athn_tx_buf *bf = sc->bcnbuf;
1248eff5798eSdamien 	struct ieee80211_frame *wh;
1249eff5798eSdamien 	struct ar_tx_desc *ds;
1250eff5798eSdamien 	struct mbuf *m;
1251eff5798eSdamien 	uint8_t ridx, hwrate;
1252eff5798eSdamien 	int error, totlen;
1253eff5798eSdamien 
12545dde5fe4Skettenis 	if (ic->ic_tim_mcast_pending &&
1255351e1934Sdlg 	    mq_empty(&ni->ni_savedq) &&
12565dde5fe4Skettenis 	    SIMPLEQ_EMPTY(&sc->txq[ATHN_QID_CAB].head))
12575dde5fe4Skettenis 		ic->ic_tim_mcast_pending = 0;
12585dde5fe4Skettenis 
1259eff5798eSdamien 	if (ic->ic_dtim_count == 0)
1260eff5798eSdamien 		ic->ic_dtim_count = ic->ic_dtim_period - 1;
1261eff5798eSdamien 	else
1262eff5798eSdamien 		ic->ic_dtim_count--;
1263eff5798eSdamien 
1264eff5798eSdamien 	/* Make sure previous beacon has been sent. */
1265eff5798eSdamien 	if (athn_tx_pending(sc, ATHN_QID_BEACON)) {
1266eff5798eSdamien 		DPRINTF(("beacon stuck\n"));
1267eff5798eSdamien 		return (EBUSY);
1268eff5798eSdamien 	}
1269eff5798eSdamien 	/* Get new beacon. */
1270eff5798eSdamien 	m = ieee80211_beacon_alloc(ic, ic->ic_bss);
1271eff5798eSdamien 	if (__predict_false(m == NULL))
1272eff5798eSdamien 		return (ENOBUFS);
1273eff5798eSdamien 	/* Assign sequence number. */
1274eff5798eSdamien 	wh = mtod(m, struct ieee80211_frame *);
1275eff5798eSdamien 	*(uint16_t *)&wh->i_seq[0] =
1276eff5798eSdamien 	    htole16(ic->ic_bss->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT);
1277eff5798eSdamien 	ic->ic_bss->ni_txseq++;
1278eff5798eSdamien 
1279eff5798eSdamien 	/* Unmap and free old beacon if any. */
1280eff5798eSdamien 	if (__predict_true(bf->bf_m != NULL)) {
1281eff5798eSdamien 		bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0,
1282eff5798eSdamien 		    bf->bf_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
1283eff5798eSdamien 		bus_dmamap_unload(sc->sc_dmat, bf->bf_map);
1284eff5798eSdamien 		m_freem(bf->bf_m);
1285eff5798eSdamien 		bf->bf_m = NULL;
1286eff5798eSdamien 	}
1287eff5798eSdamien 	/* DMA map new beacon. */
1288eff5798eSdamien 	error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_map, m,
1289eff5798eSdamien 	    BUS_DMA_NOWAIT | BUS_DMA_WRITE);
1290eff5798eSdamien 	if (__predict_false(error != 0)) {
1291eff5798eSdamien 		m_freem(m);
1292eff5798eSdamien 		return (error);
1293eff5798eSdamien 	}
1294eff5798eSdamien 	bf->bf_m = m;
1295eff5798eSdamien 
1296eff5798eSdamien 	/* Setup Tx descriptor (simplified ar5008_tx()). */
1297eff5798eSdamien 	ds = bf->bf_descs;
1298eff5798eSdamien 	memset(ds, 0, sizeof(*ds));
1299eff5798eSdamien 
1300eff5798eSdamien 	totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
1301eff5798eSdamien 	ds->ds_ctl0 = SM(AR_TXC0_FRAME_LEN, totlen);
1302eff5798eSdamien 	ds->ds_ctl0 |= SM(AR_TXC0_XMIT_POWER, AR_MAX_RATE_POWER);
1303eff5798eSdamien 	ds->ds_ctl1 = SM(AR_TXC1_FRAME_TYPE, AR_FRAME_TYPE_BEACON);
1304eff5798eSdamien 	ds->ds_ctl1 |= AR_TXC1_NO_ACK;
1305eff5798eSdamien 	ds->ds_ctl6 = SM(AR_TXC6_ENCR_TYPE, AR_ENCR_TYPE_CLEAR);
1306eff5798eSdamien 
1307eff5798eSdamien 	/* Write number of tries. */
1308eff5798eSdamien 	ds->ds_ctl2 = SM(AR_TXC2_XMIT_DATA_TRIES0, 1);
1309eff5798eSdamien 
1310eff5798eSdamien 	/* Write Tx rate. */
13117363c99eSstsp 	ridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
1312eff5798eSdamien 	    ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK1;
1313eff5798eSdamien 	hwrate = athn_rates[ridx].hwrate;
1314eff5798eSdamien 	ds->ds_ctl3 = SM(AR_TXC3_XMIT_RATE0, hwrate);
1315eff5798eSdamien 
1316eff5798eSdamien 	/* Write Tx chains. */
1317eff5798eSdamien 	ds->ds_ctl7 = SM(AR_TXC7_CHAIN_SEL0, sc->txchainmask);
1318eff5798eSdamien 
1319eff5798eSdamien 	ds->ds_data = bf->bf_map->dm_segs[0].ds_addr;
1320eff5798eSdamien 	/* Segment length must be a multiple of 4. */
1321eff5798eSdamien 	ds->ds_ctl1 |= SM(AR_TXC1_BUF_LEN,
1322eff5798eSdamien 	    (bf->bf_map->dm_segs[0].ds_len + 3) & ~3);
1323eff5798eSdamien 
1324eff5798eSdamien 	bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, bf->bf_map->dm_mapsize,
1325eff5798eSdamien 	    BUS_DMASYNC_PREWRITE);
1326eff5798eSdamien 
1327eff5798eSdamien 	/* Stop Tx DMA before putting the new beacon on the queue. */
1328eff5798eSdamien 	athn_stop_tx_dma(sc, ATHN_QID_BEACON);
1329eff5798eSdamien 
1330eff5798eSdamien 	AR_WRITE(sc, AR_QTXDP(ATHN_QID_BEACON), bf->bf_daddr);
1331eff5798eSdamien 
13325dde5fe4Skettenis 	for(;;) {
13335dde5fe4Skettenis 		if (SIMPLEQ_EMPTY(&sc->txbufs))
13345dde5fe4Skettenis 			break;
13355dde5fe4Skettenis 
1336351e1934Sdlg 		m = mq_dequeue(&ni->ni_savedq);
13375dde5fe4Skettenis 		if (m == NULL)
13385dde5fe4Skettenis 			break;
1339351e1934Sdlg 		if (!mq_empty(&ni->ni_savedq)) {
13405dde5fe4Skettenis 			/* more queued frames, set the more data bit */
13415dde5fe4Skettenis 			wh = mtod(m, struct ieee80211_frame *);
13425dde5fe4Skettenis 			wh->i_fc[1] |= IEEE80211_FC1_MORE_DATA;
13435dde5fe4Skettenis 		}
13445dde5fe4Skettenis 
13455dde5fe4Skettenis 		if (sc->ops.tx(sc, m, ni, ATHN_TXFLAG_CAB) != 0) {
13465dde5fe4Skettenis 			ieee80211_release_node(ic, ni);
13475dde5fe4Skettenis 			ifp->if_oerrors++;
13485dde5fe4Skettenis 			break;
13495dde5fe4Skettenis 		}
13505dde5fe4Skettenis 	}
13515dde5fe4Skettenis 
1352eff5798eSdamien 	/* Kick Tx. */
1353eff5798eSdamien 	AR_WRITE(sc, AR_Q_TXE, 1 << ATHN_QID_BEACON);
1354c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1355eff5798eSdamien 	return (0);
1356eff5798eSdamien }
1357eff5798eSdamien #endif
1358eff5798eSdamien 
1359bd6ea91dSdamien int
ar5008_intr(struct athn_softc * sc)1360bd6ea91dSdamien ar5008_intr(struct athn_softc *sc)
1361bd6ea91dSdamien {
1362bd6ea91dSdamien 	uint32_t intr, intr2, intr5, sync;
1363bd6ea91dSdamien 
1364bd6ea91dSdamien 	/* Get pending interrupts. */
1365bd6ea91dSdamien 	intr = AR_READ(sc, AR_INTR_ASYNC_CAUSE);
1366bd6ea91dSdamien 	if (!(intr & AR_INTR_MAC_IRQ) || intr == AR_INTR_SPURIOUS) {
1367bd6ea91dSdamien 		intr = AR_READ(sc, AR_INTR_SYNC_CAUSE);
1368bd6ea91dSdamien 		if (intr == AR_INTR_SPURIOUS || (intr & sc->isync) == 0)
1369bd6ea91dSdamien 			return (0);	/* Not for us. */
1370bd6ea91dSdamien 	}
1371bd6ea91dSdamien 
1372bd6ea91dSdamien 	if ((AR_READ(sc, AR_INTR_ASYNC_CAUSE) & AR_INTR_MAC_IRQ) &&
1373bd6ea91dSdamien 	    (AR_READ(sc, AR_RTC_STATUS) & AR_RTC_STATUS_M) == AR_RTC_STATUS_ON)
1374bd6ea91dSdamien 		intr = AR_READ(sc, AR_ISR);
1375bd6ea91dSdamien 	else
1376bd6ea91dSdamien 		intr = 0;
1377bd6ea91dSdamien 	sync = AR_READ(sc, AR_INTR_SYNC_CAUSE) & sc->isync;
1378bd6ea91dSdamien 	if (intr == 0 && sync == 0)
1379bd6ea91dSdamien 		return (0);	/* Not for us. */
1380bd6ea91dSdamien 
1381bd6ea91dSdamien 	if (intr != 0) {
1382bd6ea91dSdamien 		if (intr & AR_ISR_BCNMISC) {
1383bd6ea91dSdamien 			intr2 = AR_READ(sc, AR_ISR_S2);
1384bd6ea91dSdamien 			if (intr2 & AR_ISR_S2_TIM)
1385bd6ea91dSdamien 				/* TBD */;
1386bd6ea91dSdamien 			if (intr2 & AR_ISR_S2_TSFOOR)
1387bd6ea91dSdamien 				/* TBD */;
1388bd6ea91dSdamien 		}
1389bd6ea91dSdamien 		intr = AR_READ(sc, AR_ISR_RAC);
1390bd6ea91dSdamien 		if (intr == AR_INTR_SPURIOUS)
1391bd6ea91dSdamien 			return (1);
1392bd6ea91dSdamien 
1393eff5798eSdamien #ifndef IEEE80211_STA_ONLY
1394eff5798eSdamien 		if (intr & AR_ISR_SWBA)
1395eff5798eSdamien 			ar5008_swba_intr(sc);
1396eff5798eSdamien #endif
1397bd6ea91dSdamien 		if (intr & (AR_ISR_RXMINTR | AR_ISR_RXINTM))
1398bd6ea91dSdamien 			ar5008_rx_intr(sc);
1399bd6ea91dSdamien 		if (intr & (AR_ISR_RXOK | AR_ISR_RXERR | AR_ISR_RXORN))
1400bd6ea91dSdamien 			ar5008_rx_intr(sc);
1401bd6ea91dSdamien 
1402bd6ea91dSdamien 		if (intr & (AR_ISR_TXOK | AR_ISR_TXDESC |
1403bd6ea91dSdamien 		    AR_ISR_TXERR | AR_ISR_TXEOL))
1404bd6ea91dSdamien 			ar5008_tx_intr(sc);
1405bd6ea91dSdamien 
1406bd6ea91dSdamien 		intr5 = AR_READ(sc, AR_ISR_S5_S);
1407c0a11cf8Sdamien 		if (intr & AR_ISR_GENTMR) {
1408bd6ea91dSdamien 			if (intr5 & AR_ISR_GENTMR) {
1409bd6ea91dSdamien 				DPRINTF(("GENTMR trigger=%d thresh=%d\n",
1410bd6ea91dSdamien 				    MS(intr5, AR_ISR_S5_GENTIMER_TRIG),
1411bd6ea91dSdamien 				    MS(intr5, AR_ISR_S5_GENTIMER_THRESH)));
1412bd6ea91dSdamien 			}
1413bd6ea91dSdamien 		}
1414bd6ea91dSdamien 
1415bd6ea91dSdamien 		if (intr5 & AR_ISR_S5_TIM_TIMER)
1416bd6ea91dSdamien 			/* TBD */;
1417bd6ea91dSdamien 	}
1418bd6ea91dSdamien 	if (sync != 0) {
1419bd6ea91dSdamien 		if (sync & (AR_INTR_SYNC_HOST1_FATAL |
1420bd6ea91dSdamien 		    AR_INTR_SYNC_HOST1_PERR))
1421bd6ea91dSdamien 			/* TBD */;
1422bd6ea91dSdamien 
1423bd6ea91dSdamien 		if (sync & AR_INTR_SYNC_RADM_CPL_TIMEOUT) {
1424bd6ea91dSdamien 			AR_WRITE(sc, AR_RC, AR_RC_HOSTIF);
1425bd6ea91dSdamien 			AR_WRITE(sc, AR_RC, 0);
1426bd6ea91dSdamien 		}
1427bd6ea91dSdamien 
1428bd6ea91dSdamien 		if ((sc->flags & ATHN_FLAG_RFSILENT) &&
1429bd6ea91dSdamien 		    (sync & AR_INTR_SYNC_GPIO_PIN(sc->rfsilent_pin))) {
1430bd6ea91dSdamien 			struct ifnet *ifp = &sc->sc_ic.ic_if;
1431bd6ea91dSdamien 
1432bd6ea91dSdamien 			printf("%s: radio switch turned off\n",
1433bd6ea91dSdamien 			    sc->sc_dev.dv_xname);
1434bd6ea91dSdamien 			/* Turn the interface down. */
1435bd6ea91dSdamien 			athn_stop(ifp, 1);
1436bd6ea91dSdamien 			return (1);
1437bd6ea91dSdamien 		}
1438bd6ea91dSdamien 
1439bd6ea91dSdamien 		AR_WRITE(sc, AR_INTR_SYNC_CAUSE, sync);
1440bd6ea91dSdamien 		(void)AR_READ(sc, AR_INTR_SYNC_CAUSE);
1441bd6ea91dSdamien 	}
1442bd6ea91dSdamien 	return (1);
1443bd6ea91dSdamien }
1444bd6ea91dSdamien 
1445bd6ea91dSdamien int
ar5008_ccmp_encap(struct mbuf * m,u_int hdrlen,struct ieee80211_key * k)1446ff1dd4b7Sstsp ar5008_ccmp_encap(struct mbuf *m, u_int hdrlen, struct ieee80211_key *k)
1447ff1dd4b7Sstsp {
1448ff1dd4b7Sstsp 	struct mbuf *n;
1449ff1dd4b7Sstsp 	uint8_t *ivp;
1450ff1dd4b7Sstsp 	int off;
1451ff1dd4b7Sstsp 
1452ff1dd4b7Sstsp 	/* Insert IV for CCMP hardware encryption. */
1453ff1dd4b7Sstsp 	n = m_makespace(m, hdrlen, IEEE80211_CCMP_HDRLEN, &off);
1454ff1dd4b7Sstsp 	if (n == NULL) {
1455ff1dd4b7Sstsp 		m_freem(m);
1456ff1dd4b7Sstsp 		return (ENOBUFS);
1457ff1dd4b7Sstsp 	}
1458ff1dd4b7Sstsp 	ivp = mtod(n, uint8_t *) + off;
1459ff1dd4b7Sstsp 	k->k_tsc++;
1460ff1dd4b7Sstsp 	ivp[0] = k->k_tsc;
1461ff1dd4b7Sstsp 	ivp[1] = k->k_tsc >> 8;
1462ff1dd4b7Sstsp 	ivp[2] = 0;
1463ff1dd4b7Sstsp 	ivp[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV;
1464ff1dd4b7Sstsp 	ivp[4] = k->k_tsc >> 16;
1465ff1dd4b7Sstsp 	ivp[5] = k->k_tsc >> 24;
1466ff1dd4b7Sstsp 	ivp[6] = k->k_tsc >> 32;
1467ff1dd4b7Sstsp 	ivp[7] = k->k_tsc >> 40;
1468ff1dd4b7Sstsp 
1469ff1dd4b7Sstsp 	return 0;
1470ff1dd4b7Sstsp }
1471ff1dd4b7Sstsp 
1472ff1dd4b7Sstsp int
ar5008_tx(struct athn_softc * sc,struct mbuf * m,struct ieee80211_node * ni,int txflags)1473436170c5Sdamien ar5008_tx(struct athn_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
1474436170c5Sdamien     int txflags)
1475bd6ea91dSdamien {
1476bd6ea91dSdamien 	struct ieee80211com *ic = &sc->sc_ic;
1477bd6ea91dSdamien 	struct ieee80211_key *k = NULL;
1478bd6ea91dSdamien 	struct ieee80211_frame *wh;
1479bd6ea91dSdamien 	struct athn_series series[4];
1480bd6ea91dSdamien 	struct ar_tx_desc *ds, *lastds;
1481bd6ea91dSdamien 	struct athn_txq *txq;
1482bd6ea91dSdamien 	struct athn_tx_buf *bf;
1483bd6ea91dSdamien 	struct athn_node *an = (void *)ni;
1484bd6ea91dSdamien 	uintptr_t entry;
1485bd6ea91dSdamien 	uint16_t qos;
1486bd6ea91dSdamien 	uint8_t txpower, type, encrtype, tid, ridx[4];
1487bd6ea91dSdamien 	int i, error, totlen, hasqos, qid;
1488bd6ea91dSdamien 
1489bd6ea91dSdamien 	/* Grab a Tx buffer from our global free list. */
1490bd6ea91dSdamien 	bf = SIMPLEQ_FIRST(&sc->txbufs);
1491bd6ea91dSdamien 	KASSERT(bf != NULL);
1492bd6ea91dSdamien 
1493bd6ea91dSdamien 	/* Map 802.11 frame type to hardware frame type. */
1494bd6ea91dSdamien 	wh = mtod(m, struct ieee80211_frame *);
1495bd6ea91dSdamien 	if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
1496bd6ea91dSdamien 	    IEEE80211_FC0_TYPE_MGT) {
1497eff5798eSdamien 		/* NB: Beacons do not use ar5008_tx(). */
1498bd6ea91dSdamien 		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
1499bd6ea91dSdamien 		    IEEE80211_FC0_SUBTYPE_PROBE_RESP)
1500bd6ea91dSdamien 			type = AR_FRAME_TYPE_PROBE_RESP;
1501bd6ea91dSdamien 		else if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
1502bd6ea91dSdamien 		    IEEE80211_FC0_SUBTYPE_ATIM)
1503bd6ea91dSdamien 			type = AR_FRAME_TYPE_ATIM;
1504bd6ea91dSdamien 		else
1505bd6ea91dSdamien 			type = AR_FRAME_TYPE_NORMAL;
1506bd6ea91dSdamien 	} else if ((wh->i_fc[0] &
1507bd6ea91dSdamien 	    (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
1508bd6ea91dSdamien 	    (IEEE80211_FC0_TYPE_CTL  | IEEE80211_FC0_SUBTYPE_PS_POLL)) {
1509bd6ea91dSdamien 		type = AR_FRAME_TYPE_PSPOLL;
1510bd6ea91dSdamien 	} else
1511bd6ea91dSdamien 		type = AR_FRAME_TYPE_NORMAL;
1512bd6ea91dSdamien 
1513bd6ea91dSdamien 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
1514bd6ea91dSdamien 		k = ieee80211_get_txkey(ic, wh, ni);
1515ff1dd4b7Sstsp 		if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
1516ff1dd4b7Sstsp 			u_int hdrlen = ieee80211_get_hdrlen(wh);
1517ff1dd4b7Sstsp 			if (ar5008_ccmp_encap(m, hdrlen, k) != 0)
1518ff1dd4b7Sstsp 				return (ENOBUFS);
1519ff1dd4b7Sstsp 		} else {
1520bd6ea91dSdamien 			if ((m = ieee80211_encrypt(ic, m, k)) == NULL)
1521bd6ea91dSdamien 				return (ENOBUFS);
1522ff1dd4b7Sstsp 			k = NULL; /* skip hardware crypto further below */
1523ff1dd4b7Sstsp 		}
1524bd6ea91dSdamien 		wh = mtod(m, struct ieee80211_frame *);
1525bd6ea91dSdamien 	}
1526bd6ea91dSdamien 
1527bd6ea91dSdamien 	/* XXX 2-byte padding for QoS and 4-addr headers. */
1528bd6ea91dSdamien 
1529bd6ea91dSdamien 	/* Select the HW Tx queue to use for this frame. */
1530bd6ea91dSdamien 	if ((hasqos = ieee80211_has_qos(wh))) {
1531bd6ea91dSdamien 		qos = ieee80211_get_qos(wh);
1532bd6ea91dSdamien 		tid = qos & IEEE80211_QOS_TID;
1533bd6ea91dSdamien 		qid = athn_ac2qid[ieee80211_up_to_ac(ic, tid)];
1534bd6ea91dSdamien 	} else if (type == AR_FRAME_TYPE_PSPOLL) {
1535bd6ea91dSdamien 		qid = ATHN_QID_PSPOLL;
15365dde5fe4Skettenis 	} else if (txflags & ATHN_TXFLAG_CAB) {
15375dde5fe4Skettenis 		qid = ATHN_QID_CAB;
1538bd6ea91dSdamien 	} else
1539bd6ea91dSdamien 		qid = ATHN_QID_AC_BE;
1540bd6ea91dSdamien 	txq = &sc->txq[qid];
1541bd6ea91dSdamien 
1542bd6ea91dSdamien 	/* Select the transmit rates to use for this frame. */
1543bd6ea91dSdamien 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
1544bd6ea91dSdamien 	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
1545bd6ea91dSdamien 	    IEEE80211_FC0_TYPE_DATA) {
1546bd6ea91dSdamien 		/* Use lowest rate for all tries. */
1547bd6ea91dSdamien 		ridx[0] = ridx[1] = ridx[2] = ridx[3] =
15487363c99eSstsp 		    (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
15497363c99eSstsp 			ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK1);
15507363c99eSstsp 	} else if ((ni->ni_flags & IEEE80211_NODE_HT) &&
15517363c99eSstsp 	    ic->ic_fixed_mcs != -1) {
15527363c99eSstsp 		/* Use same fixed rate for all tries. */
15537363c99eSstsp 		ridx[0] = ridx[1] = ridx[2] = ridx[3] =
15547363c99eSstsp 		    ATHN_RIDX_MCS0 + ic->ic_fixed_mcs;
1555bd6ea91dSdamien 	} else if (ic->ic_fixed_rate != -1) {
1556bd6ea91dSdamien 		/* Use same fixed rate for all tries. */
1557bd6ea91dSdamien 		ridx[0] = ridx[1] = ridx[2] = ridx[3] =
1558bd6ea91dSdamien 		    sc->fixed_ridx;
1559bd6ea91dSdamien 	} else {
1560bd6ea91dSdamien 		/* Use fallback table of the node. */
15617363c99eSstsp 		int txrate;
15627363c99eSstsp 
15637363c99eSstsp 		if (ni->ni_flags & IEEE80211_NODE_HT)
15647363c99eSstsp 			txrate = ATHN_NUM_LEGACY_RATES + ni->ni_txmcs;
15657363c99eSstsp 		else
15667363c99eSstsp 			txrate = ni->ni_txrate;
1567bd6ea91dSdamien 		for (i = 0; i < 4; i++) {
1568bd6ea91dSdamien 			ridx[i] = an->ridx[txrate];
1569bd6ea91dSdamien 			txrate = an->fallback[txrate];
1570bd6ea91dSdamien 		}
1571bd6ea91dSdamien 	}
1572bd6ea91dSdamien 
1573bd6ea91dSdamien #if NBPFILTER > 0
1574bd6ea91dSdamien 	if (__predict_false(sc->sc_drvbpf != NULL)) {
1575bd6ea91dSdamien 		struct athn_tx_radiotap_header *tap = &sc->sc_txtap;
1576bd6ea91dSdamien 
1577bd6ea91dSdamien 		tap->wt_flags = 0;
1578bd6ea91dSdamien 		/* Use initial transmit rate. */
15797363c99eSstsp 		if (athn_rates[ridx[0]].hwrate & 0x80) /* MCS */
15807363c99eSstsp 			tap->wt_rate = athn_rates[ridx[0]].hwrate;
15817363c99eSstsp 		else
1582bd6ea91dSdamien 			tap->wt_rate = athn_rates[ridx[0]].rate;
1583bd6ea91dSdamien 		tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
1584bd6ea91dSdamien 		tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
158577a2d566Sstsp 		if (athn_rates[ridx[0]].phy == IEEE80211_T_DS &&
158677a2d566Sstsp 		    ridx[0] != ATHN_RIDX_CCK1 &&
1587bd6ea91dSdamien 		    (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
1588bd6ea91dSdamien 			tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
1589526f68fbSclaudio 		bpf_mtap_hdr(sc->sc_drvbpf, tap, sc->sc_txtap_len, m,
1590526f68fbSclaudio 		    BPF_DIRECTION_OUT);
1591bd6ea91dSdamien 	}
1592bd6ea91dSdamien #endif
1593bd6ea91dSdamien 
1594bd6ea91dSdamien 	/* DMA map mbuf. */
1595bd6ea91dSdamien 	error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_map, m,
1596bd6ea91dSdamien 	    BUS_DMA_NOWAIT | BUS_DMA_WRITE);
1597bd6ea91dSdamien 	if (__predict_false(error != 0)) {
1598bd6ea91dSdamien 		if (error != EFBIG) {
1599bd6ea91dSdamien 			printf("%s: can't map mbuf (error %d)\n",
1600bd6ea91dSdamien 			    sc->sc_dev.dv_xname, error);
1601bd6ea91dSdamien 			m_freem(m);
1602bd6ea91dSdamien 			return (error);
1603bd6ea91dSdamien 		}
1604bd6ea91dSdamien 		/*
1605bd6ea91dSdamien 		 * DMA mapping requires too many DMA segments; linearize
1606bd6ea91dSdamien 		 * mbuf in kernel virtual address space and retry.
1607bd6ea91dSdamien 		 */
1608fc0f10f6Sclaudio 		if (m_defrag(m, M_DONTWAIT) != 0) {
1609bd6ea91dSdamien 			m_freem(m);
1610bd6ea91dSdamien 			return (ENOBUFS);
1611bd6ea91dSdamien 		}
1612bd6ea91dSdamien 
1613bd6ea91dSdamien 		error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_map, m,
1614bd6ea91dSdamien 		    BUS_DMA_NOWAIT | BUS_DMA_WRITE);
1615bd6ea91dSdamien 		if (error != 0) {
1616bd6ea91dSdamien 			printf("%s: can't map mbuf (error %d)\n",
1617bd6ea91dSdamien 			    sc->sc_dev.dv_xname, error);
1618bd6ea91dSdamien 			m_freem(m);
1619bd6ea91dSdamien 			return (error);
1620bd6ea91dSdamien 		}
1621bd6ea91dSdamien 	}
1622bd6ea91dSdamien 	bf->bf_m = m;
1623bd6ea91dSdamien 	bf->bf_ni = ni;
1624d2dd70acSstsp 	bf->bf_txmcs = ni->ni_txmcs;
1625436170c5Sdamien 	bf->bf_txflags = txflags;
1626bd6ea91dSdamien 
1627bd6ea91dSdamien 	wh = mtod(m, struct ieee80211_frame *);
1628bd6ea91dSdamien 
1629bd6ea91dSdamien 	totlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
1630bd6ea91dSdamien 
1631bd6ea91dSdamien 	/* Clear all Tx descriptors that we will use. */
1632bd6ea91dSdamien 	memset(bf->bf_descs, 0, bf->bf_map->dm_nsegs * sizeof(*ds));
1633bd6ea91dSdamien 
1634bd6ea91dSdamien 	/* Setup first Tx descriptor. */
1635bd6ea91dSdamien 	ds = bf->bf_descs;
1636bd6ea91dSdamien 
1637bd6ea91dSdamien 	ds->ds_ctl0 = AR_TXC0_INTR_REQ | AR_TXC0_CLR_DEST_MASK;
1638bd6ea91dSdamien 	txpower = AR_MAX_RATE_POWER;	/* Get from per-rate registers. */
1639bd6ea91dSdamien 	ds->ds_ctl0 |= SM(AR_TXC0_XMIT_POWER, txpower);
1640bd6ea91dSdamien 
1641bd6ea91dSdamien 	ds->ds_ctl1 = SM(AR_TXC1_FRAME_TYPE, type);
1642bd6ea91dSdamien 
1643bd6ea91dSdamien 	if (IEEE80211_IS_MULTICAST(wh->i_addr1) ||
1644bd6ea91dSdamien 	    (hasqos && (qos & IEEE80211_QOS_ACK_POLICY_MASK) ==
1645bd6ea91dSdamien 	     IEEE80211_QOS_ACK_POLICY_NOACK))
1646bd6ea91dSdamien 		ds->ds_ctl1 |= AR_TXC1_NO_ACK;
1647bd6ea91dSdamien 
1648ff1dd4b7Sstsp 	if (k != NULL) {
1649ff1dd4b7Sstsp 		/* Map 802.11 cipher to hardware encryption type. */
1650ff1dd4b7Sstsp 		if (k->k_cipher == IEEE80211_CIPHER_CCMP) {
1651bd6ea91dSdamien 			encrtype = AR_ENCR_TYPE_AES;
1652ff1dd4b7Sstsp 			totlen += IEEE80211_CCMP_MICLEN;
1653ff1dd4b7Sstsp 		} else
165406f8da2dSdamien 			panic("unsupported cipher");
1655bd6ea91dSdamien 		/*
1656bd6ea91dSdamien 		 * NB: The key cache entry index is stored in the key
1657bd6ea91dSdamien 		 * private field when the key is installed.
1658bd6ea91dSdamien 		 */
1659bd6ea91dSdamien 		entry = (uintptr_t)k->k_priv;
1660bd6ea91dSdamien 		ds->ds_ctl1 |= SM(AR_TXC1_DEST_IDX, entry);
1661bd6ea91dSdamien 		ds->ds_ctl0 |= AR_TXC0_DEST_IDX_VALID;
1662bd6ea91dSdamien 	} else
1663bd6ea91dSdamien 		encrtype = AR_ENCR_TYPE_CLEAR;
1664bd6ea91dSdamien 	ds->ds_ctl6 = SM(AR_TXC6_ENCR_TYPE, encrtype);
1665bd6ea91dSdamien 
1666bd6ea91dSdamien 	/* Check if frame must be protected using RTS/CTS or CTS-to-self. */
166796497feaSstsp 	if (!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
166896497feaSstsp 	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
166996497feaSstsp 	    IEEE80211_FC0_TYPE_DATA) {
16707363c99eSstsp 		enum ieee80211_htprot htprot;
16717363c99eSstsp 
16727363c99eSstsp 		htprot = (ic->ic_bss->ni_htop1 & IEEE80211_HTOP1_PROT_MASK);
167322fc3993Sstsp 
1674bd6ea91dSdamien 		/* NB: Group frames are sent using CCK in 802.11b/g. */
1675d2dd70acSstsp 		if (totlen > ic->ic_rtsthreshold) {
1676bd6ea91dSdamien 			ds->ds_ctl0 |= AR_TXC0_RTS_ENABLE;
16777363c99eSstsp 		} else if (((ic->ic_flags & IEEE80211_F_USEPROT) &&
16787363c99eSstsp 		    athn_rates[ridx[0]].phy == IEEE80211_T_OFDM) ||
167922c4b2feSstsp 		    ((ni->ni_flags & IEEE80211_NODE_HT) &&
16807363c99eSstsp 		    htprot != IEEE80211_HTPROT_NONE)) {
1681bd6ea91dSdamien 			if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
1682bd6ea91dSdamien 				ds->ds_ctl0 |= AR_TXC0_RTS_ENABLE;
1683bd6ea91dSdamien 			else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
1684bd6ea91dSdamien 				ds->ds_ctl0 |= AR_TXC0_CTS_ENABLE;
1685bd6ea91dSdamien 		}
1686bd6ea91dSdamien 	}
1687468af7efSstsp 	/*
1688468af7efSstsp 	 * Disable multi-rate retries when protection is used.
1689468af7efSstsp 	 * The RTS/CTS frame's duration field is fixed and won't be
1690468af7efSstsp 	 * updated by hardware when the data rate changes.
1691468af7efSstsp 	 */
1692bd6ea91dSdamien 	if (ds->ds_ctl0 & (AR_TXC0_RTS_ENABLE | AR_TXC0_CTS_ENABLE)) {
1693bd6ea91dSdamien 		ridx[1] = ridx[2] = ridx[3] = ridx[0];
1694bd6ea91dSdamien 	}
1695bd6ea91dSdamien 	/* Setup multi-rate retries. */
1696bd6ea91dSdamien 	for (i = 0; i < 4; i++) {
1697bd6ea91dSdamien 		series[i].hwrate = athn_rates[ridx[i]].hwrate;
1698bd6ea91dSdamien 		if (athn_rates[ridx[i]].phy == IEEE80211_T_DS &&
1699bd6ea91dSdamien 		    ridx[i] != ATHN_RIDX_CCK1 &&
1700bd6ea91dSdamien 		    (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
1701bd6ea91dSdamien 			series[i].hwrate |= 0x04;
1702bd6ea91dSdamien 		/* Compute duration for each series. */
1703e23d4b70Sstsp 		series[i].dur = athn_txtime(sc, totlen, ridx[i], ic->ic_flags);
1704e23d4b70Sstsp 		if (!(ds->ds_ctl1 & AR_TXC1_NO_ACK)) {
1705e23d4b70Sstsp 			/* Account for ACK duration. */
1706e23d4b70Sstsp 			series[i].dur += athn_txtime(sc, IEEE80211_ACK_LEN,
1707bd6ea91dSdamien 			    athn_rates[ridx[i]].rspridx, ic->ic_flags);
1708bd6ea91dSdamien 		}
1709bd6ea91dSdamien 	}
1710bd6ea91dSdamien 
1711bd6ea91dSdamien 	/* Write number of tries for each series. */
1712bd6ea91dSdamien 	ds->ds_ctl2 =
1713bd6ea91dSdamien 	    SM(AR_TXC2_XMIT_DATA_TRIES0, 2) |
1714bd6ea91dSdamien 	    SM(AR_TXC2_XMIT_DATA_TRIES1, 2) |
1715bd6ea91dSdamien 	    SM(AR_TXC2_XMIT_DATA_TRIES2, 2) |
1716bd6ea91dSdamien 	    SM(AR_TXC2_XMIT_DATA_TRIES3, 4);
1717bd6ea91dSdamien 
1718bd6ea91dSdamien 	/* Tell HW to update duration field in 802.11 header. */
1719bd6ea91dSdamien 	if (type != AR_FRAME_TYPE_PSPOLL)
1720bd6ea91dSdamien 		ds->ds_ctl2 |= AR_TXC2_DUR_UPDATE_ENA;
1721bd6ea91dSdamien 
1722bd6ea91dSdamien 	/* Write Tx rate for each series. */
1723bd6ea91dSdamien 	ds->ds_ctl3 =
1724bd6ea91dSdamien 	    SM(AR_TXC3_XMIT_RATE0, series[0].hwrate) |
1725bd6ea91dSdamien 	    SM(AR_TXC3_XMIT_RATE1, series[1].hwrate) |
1726bd6ea91dSdamien 	    SM(AR_TXC3_XMIT_RATE2, series[2].hwrate) |
1727bd6ea91dSdamien 	    SM(AR_TXC3_XMIT_RATE3, series[3].hwrate);
1728bd6ea91dSdamien 
1729bd6ea91dSdamien 	/* Write duration for each series. */
1730bd6ea91dSdamien 	ds->ds_ctl4 =
1731bd6ea91dSdamien 	    SM(AR_TXC4_PACKET_DUR0, series[0].dur) |
1732bd6ea91dSdamien 	    SM(AR_TXC4_PACKET_DUR1, series[1].dur);
1733bd6ea91dSdamien 	ds->ds_ctl5 =
1734bd6ea91dSdamien 	    SM(AR_TXC5_PACKET_DUR2, series[2].dur) |
1735bd6ea91dSdamien 	    SM(AR_TXC5_PACKET_DUR3, series[3].dur);
1736bd6ea91dSdamien 
1737bd6ea91dSdamien 	/* Use the same Tx chains for all tries. */
1738bd6ea91dSdamien 	ds->ds_ctl7 =
1739bd6ea91dSdamien 	    SM(AR_TXC7_CHAIN_SEL0, sc->txchainmask) |
1740bd6ea91dSdamien 	    SM(AR_TXC7_CHAIN_SEL1, sc->txchainmask) |
1741bd6ea91dSdamien 	    SM(AR_TXC7_CHAIN_SEL2, sc->txchainmask) |
1742bd6ea91dSdamien 	    SM(AR_TXC7_CHAIN_SEL3, sc->txchainmask);
1743bd6ea91dSdamien #ifdef notyet
1744bd6ea91dSdamien 	/* Use the same short GI setting for all tries. */
17457363c99eSstsp 	if (ni->ni_htcaps & IEEE80211_HTCAP_SGI20)
1746bd6ea91dSdamien 		ds->ds_ctl7 |= AR_TXC7_GI0123;
1747bd6ea91dSdamien 	/* Use the same channel width for all tries. */
1748bd6ea91dSdamien 	if (ic->ic_flags & IEEE80211_F_CBW40)
1749bd6ea91dSdamien 		ds->ds_ctl7 |= AR_TXC7_2040_0123;
1750bd6ea91dSdamien #endif
1751bd6ea91dSdamien 
1752d55551faSstsp 	/* Set Tx power for series 1 - 3 */
1753d55551faSstsp 	ds->ds_ctl9 = SM(AR_TXC9_XMIT_POWER1, txpower);
1754d55551faSstsp 	ds->ds_ctl10 = SM(AR_TXC10_XMIT_POWER2, txpower);
1755d55551faSstsp 	ds->ds_ctl11 = SM(AR_TXC11_XMIT_POWER3, txpower);
1756d55551faSstsp 
1757bd6ea91dSdamien 	if (ds->ds_ctl0 & (AR_TXC0_RTS_ENABLE | AR_TXC0_CTS_ENABLE)) {
1758bd6ea91dSdamien 		uint8_t protridx, hwrate;
1759bd6ea91dSdamien 		uint16_t dur = 0;
1760bd6ea91dSdamien 
1761bd6ea91dSdamien 		/* Use the same protection mode for all tries. */
1762bd6ea91dSdamien 		if (ds->ds_ctl0 & AR_TXC0_RTS_ENABLE) {
1763bd6ea91dSdamien 			ds->ds_ctl4 |= AR_TXC4_RTSCTS_QUAL01;
1764bd6ea91dSdamien 			ds->ds_ctl5 |= AR_TXC5_RTSCTS_QUAL23;
1765bd6ea91dSdamien 		}
17667a911050Sdamien 		/* Select protection rate (suboptimal but ok). */
17677363c99eSstsp 		protridx = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ?
17688793b9ceSstsp 		    ATHN_RIDX_OFDM6 : ATHN_RIDX_CCK2;
1769bd6ea91dSdamien 		if (ds->ds_ctl0 & AR_TXC0_RTS_ENABLE) {
1770bd6ea91dSdamien 			/* Account for CTS duration. */
1771bd6ea91dSdamien 			dur += athn_txtime(sc, IEEE80211_ACK_LEN,
1772bd6ea91dSdamien 			    athn_rates[protridx].rspridx, ic->ic_flags);
1773bd6ea91dSdamien 		}
1774bd6ea91dSdamien 		dur += athn_txtime(sc, totlen, ridx[0], ic->ic_flags);
1775bd6ea91dSdamien 		if (!(ds->ds_ctl1 & AR_TXC1_NO_ACK)) {
1776bd6ea91dSdamien 			/* Account for ACK duration. */
1777bd6ea91dSdamien 			dur += athn_txtime(sc, IEEE80211_ACK_LEN,
1778bd6ea91dSdamien 			    athn_rates[ridx[0]].rspridx, ic->ic_flags);
1779bd6ea91dSdamien 		}
1780bd6ea91dSdamien 		/* Write protection frame duration and rate. */
1781bd6ea91dSdamien 		ds->ds_ctl2 |= SM(AR_TXC2_BURST_DUR, dur);
1782bd6ea91dSdamien 		hwrate = athn_rates[protridx].hwrate;
1783bd6ea91dSdamien 		if (protridx == ATHN_RIDX_CCK2 &&
1784bd6ea91dSdamien 		    (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
1785bd6ea91dSdamien 			hwrate |= 0x04;
1786bd6ea91dSdamien 		ds->ds_ctl7 |= SM(AR_TXC7_RTSCTS_RATE, hwrate);
1787bd6ea91dSdamien 	}
1788bd6ea91dSdamien 
17897a911050Sdamien 	/* Finalize first Tx descriptor and fill others (if any). */
1790bd6ea91dSdamien 	ds->ds_ctl0 |= SM(AR_TXC0_FRAME_LEN, totlen);
1791bd6ea91dSdamien 
1792bd6ea91dSdamien 	for (i = 0; i < bf->bf_map->dm_nsegs; i++, ds++) {
1793bd6ea91dSdamien 		ds->ds_data = bf->bf_map->dm_segs[i].ds_addr;
1794bd6ea91dSdamien 		ds->ds_ctl1 |= SM(AR_TXC1_BUF_LEN,
1795bd6ea91dSdamien 		    bf->bf_map->dm_segs[i].ds_len);
1796bd6ea91dSdamien 
1797bd6ea91dSdamien 		if (i != bf->bf_map->dm_nsegs - 1)
1798bd6ea91dSdamien 			ds->ds_ctl1 |= AR_TXC1_MORE;
1799bd6ea91dSdamien 		ds->ds_link = 0;
1800bd6ea91dSdamien 
1801bd6ea91dSdamien 		/* Chain Tx descriptor. */
1802bd6ea91dSdamien 		if (i != 0)
1803bd6ea91dSdamien 			lastds->ds_link = bf->bf_daddr + i * sizeof(*ds);
1804bd6ea91dSdamien 		lastds = ds;
1805bd6ea91dSdamien 	}
1806bd6ea91dSdamien 	bus_dmamap_sync(sc->sc_dmat, bf->bf_map, 0, bf->bf_map->dm_mapsize,
1807bd6ea91dSdamien 	    BUS_DMASYNC_PREWRITE);
1808bd6ea91dSdamien 
1809bd6ea91dSdamien 	if (!SIMPLEQ_EMPTY(&txq->head))
1810bd6ea91dSdamien 		((struct ar_tx_desc *)txq->lastds)->ds_link = bf->bf_daddr;
1811bd6ea91dSdamien 	else
1812bd6ea91dSdamien 		AR_WRITE(sc, AR_QTXDP(qid), bf->bf_daddr);
1813bd6ea91dSdamien 	txq->lastds = lastds;
181494261de9Sdamien 	SIMPLEQ_REMOVE_HEAD(&sc->txbufs, bf_list);
1815bd6ea91dSdamien 	SIMPLEQ_INSERT_TAIL(&txq->head, bf, bf_list);
1816bd6ea91dSdamien 
1817bd6ea91dSdamien 	ds = bf->bf_descs;
1818bd6ea91dSdamien 	DPRINTFN(6, ("Tx qid=%d nsegs=%d ctl0=0x%x ctl1=0x%x ctl3=0x%x\n",
1819bd6ea91dSdamien 	    qid, bf->bf_map->dm_nsegs, ds->ds_ctl0, ds->ds_ctl1, ds->ds_ctl3));
1820bd6ea91dSdamien 
1821bd6ea91dSdamien 	/* Kick Tx. */
1822bd6ea91dSdamien 	AR_WRITE(sc, AR_Q_TXE, 1 << qid);
1823c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1824bd6ea91dSdamien 	return (0);
1825bd6ea91dSdamien }
1826bd6ea91dSdamien 
1827bd6ea91dSdamien void
ar5008_set_rf_mode(struct athn_softc * sc,struct ieee80211_channel * c)1828bd6ea91dSdamien ar5008_set_rf_mode(struct athn_softc *sc, struct ieee80211_channel *c)
1829bd6ea91dSdamien {
1830bd6ea91dSdamien 	uint32_t reg;
1831bd6ea91dSdamien 
1832bd6ea91dSdamien 	reg = IEEE80211_IS_CHAN_2GHZ(c) ?
1833bd6ea91dSdamien 	    AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
1834bd6ea91dSdamien 	if (!AR_SREV_9280_10_OR_LATER(sc)) {
1835bd6ea91dSdamien 		reg |= IEEE80211_IS_CHAN_2GHZ(c) ?
1836bd6ea91dSdamien 		    AR_PHY_MODE_RF2GHZ : AR_PHY_MODE_RF5GHZ;
183797bf8fdcSdamien 	} else if (IEEE80211_IS_CHAN_5GHZ(c) &&
183897bf8fdcSdamien 	    (sc->flags & ATHN_FLAG_FAST_PLL_CLOCK)) {
1839bd6ea91dSdamien 		reg |= AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE;
1840bd6ea91dSdamien 	}
1841bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_MODE, reg);
1842c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1843bd6ea91dSdamien }
1844bd6ea91dSdamien 
1845bd6ea91dSdamien static __inline uint32_t
ar5008_synth_delay(struct athn_softc * sc)1846bd6ea91dSdamien ar5008_synth_delay(struct athn_softc *sc)
1847bd6ea91dSdamien {
1848bd6ea91dSdamien 	uint32_t delay;
1849bd6ea91dSdamien 
1850bd6ea91dSdamien 	delay = MS(AR_READ(sc, AR_PHY_RX_DELAY), AR_PHY_RX_DELAY_DELAY);
1851bd6ea91dSdamien 	if (sc->sc_ic.ic_curmode == IEEE80211_MODE_11B)
1852bd6ea91dSdamien 		delay = (delay * 4) / 22;
1853bd6ea91dSdamien 	else
1854bd6ea91dSdamien 		delay = delay / 10;	/* in 100ns steps */
1855bd6ea91dSdamien 	return (delay);
1856bd6ea91dSdamien }
1857bd6ea91dSdamien 
1858bd6ea91dSdamien int
ar5008_rf_bus_request(struct athn_softc * sc)1859bd6ea91dSdamien ar5008_rf_bus_request(struct athn_softc *sc)
1860bd6ea91dSdamien {
1861bd6ea91dSdamien 	int ntries;
1862bd6ea91dSdamien 
1863bd6ea91dSdamien 	/* Request RF Bus grant. */
1864bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_EN);
1865bd6ea91dSdamien 	for (ntries = 0; ntries < 10000; ntries++) {
1866bd6ea91dSdamien 		if (AR_READ(sc, AR_PHY_RFBUS_GRANT) & AR_PHY_RFBUS_GRANT_EN)
1867bd6ea91dSdamien 			return (0);
1868bd6ea91dSdamien 		DELAY(10);
1869bd6ea91dSdamien 	}
1870bd6ea91dSdamien 	DPRINTF(("could not kill baseband Rx"));
1871bd6ea91dSdamien 	return (ETIMEDOUT);
1872bd6ea91dSdamien }
1873bd6ea91dSdamien 
1874bd6ea91dSdamien void
ar5008_rf_bus_release(struct athn_softc * sc)1875bd6ea91dSdamien ar5008_rf_bus_release(struct athn_softc *sc)
1876bd6ea91dSdamien {
1877bd6ea91dSdamien 	/* Wait for the synthesizer to settle. */
1878bd6ea91dSdamien 	DELAY(AR_BASE_PHY_ACTIVE_DELAY + ar5008_synth_delay(sc));
1879bd6ea91dSdamien 
1880bd6ea91dSdamien 	/* Release the RF Bus grant. */
1881bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_RFBUS_REQ, 0);
1882c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1883bd6ea91dSdamien }
1884bd6ea91dSdamien 
1885bd6ea91dSdamien void
ar5008_set_phy(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)1886bd6ea91dSdamien ar5008_set_phy(struct athn_softc *sc, struct ieee80211_channel *c,
1887bd6ea91dSdamien     struct ieee80211_channel *extc)
1888bd6ea91dSdamien {
1889bd6ea91dSdamien 	uint32_t phy;
1890bd6ea91dSdamien 
1891bd6ea91dSdamien 	if (AR_SREV_9285_10_OR_LATER(sc))
1892bd6ea91dSdamien 		phy = AR_READ(sc, AR_PHY_TURBO) & AR_PHY_FC_ENABLE_DAC_FIFO;
1893bd6ea91dSdamien 	else
1894bd6ea91dSdamien 		phy = 0;
1895bd6ea91dSdamien 	phy |= AR_PHY_FC_HT_EN | AR_PHY_FC_SHORT_GI_40 |
1896bd6ea91dSdamien 	    AR_PHY_FC_SINGLE_HT_LTF1 | AR_PHY_FC_WALSH;
1897bd6ea91dSdamien 	if (extc != NULL) {
1898bd6ea91dSdamien 		phy |= AR_PHY_FC_DYN2040_EN;
1899bd6ea91dSdamien 		if (extc > c)	/* XXX */
1900bd6ea91dSdamien 			phy |= AR_PHY_FC_DYN2040_PRI_CH;
1901bd6ea91dSdamien 	}
1902bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TURBO, phy);
1903bd6ea91dSdamien 
1904bd6ea91dSdamien 	AR_WRITE(sc, AR_2040_MODE,
1905bd6ea91dSdamien 	    (extc != NULL) ? AR_2040_JOINED_RX_CLEAR : 0);
1906bd6ea91dSdamien 
1907bd6ea91dSdamien 	/* Set global transmit timeout. */
1908bd6ea91dSdamien 	AR_WRITE(sc, AR_GTXTO, SM(AR_GTXTO_TIMEOUT_LIMIT, 25));
1909bd6ea91dSdamien 	/* Set carrier sense timeout. */
1910bd6ea91dSdamien 	AR_WRITE(sc, AR_CST, SM(AR_CST_TIMEOUT_LIMIT, 15));
1911c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1912bd6ea91dSdamien }
1913bd6ea91dSdamien 
1914bd6ea91dSdamien void
ar5008_set_delta_slope(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)1915bd6ea91dSdamien ar5008_set_delta_slope(struct athn_softc *sc, struct ieee80211_channel *c,
1916bd6ea91dSdamien     struct ieee80211_channel *extc)
1917bd6ea91dSdamien {
1918bd6ea91dSdamien 	uint32_t coeff, exp, man, reg;
1919bd6ea91dSdamien 
1920bd6ea91dSdamien 	/* Set Delta Slope (exponent and mantissa). */
1921bd6ea91dSdamien 	coeff = (100 << 24) / c->ic_freq;
1922bd6ea91dSdamien 	athn_get_delta_slope(coeff, &exp, &man);
1923bd6ea91dSdamien 	DPRINTFN(5, ("delta slope coeff exp=%u man=%u\n", exp, man));
1924bd6ea91dSdamien 
1925bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_TIMING3);
1926bd6ea91dSdamien 	reg = RW(reg, AR_PHY_TIMING3_DSC_EXP, exp);
1927bd6ea91dSdamien 	reg = RW(reg, AR_PHY_TIMING3_DSC_MAN, man);
1928bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TIMING3, reg);
1929bd6ea91dSdamien 
1930bd6ea91dSdamien 	/* For Short GI, coeff is 9/10 that of normal coeff. */
1931bd6ea91dSdamien 	coeff = (9 * coeff) / 10;
1932bd6ea91dSdamien 	athn_get_delta_slope(coeff, &exp, &man);
1933bd6ea91dSdamien 	DPRINTFN(5, ("delta slope coeff exp=%u man=%u\n", exp, man));
1934bd6ea91dSdamien 
1935bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_HALFGI);
1936bd6ea91dSdamien 	reg = RW(reg, AR_PHY_HALFGI_DSC_EXP, exp);
1937bd6ea91dSdamien 	reg = RW(reg, AR_PHY_HALFGI_DSC_MAN, man);
1938bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_HALFGI, reg);
1939c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1940bd6ea91dSdamien }
1941bd6ea91dSdamien 
1942bd6ea91dSdamien void
ar5008_enable_antenna_diversity(struct athn_softc * sc)1943bd6ea91dSdamien ar5008_enable_antenna_diversity(struct athn_softc *sc)
1944bd6ea91dSdamien {
1945bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_CCK_DETECT,
1946bd6ea91dSdamien 	    AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
1947c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1948bd6ea91dSdamien }
1949bd6ea91dSdamien 
1950bd6ea91dSdamien void
ar5008_init_baseband(struct athn_softc * sc)1951bd6ea91dSdamien ar5008_init_baseband(struct athn_softc *sc)
1952bd6ea91dSdamien {
1953bd6ea91dSdamien 	uint32_t synth_delay;
1954bd6ea91dSdamien 
1955bd6ea91dSdamien 	synth_delay = ar5008_synth_delay(sc);
1956bd6ea91dSdamien 	/* Activate the PHY (includes baseband activate and synthesizer on). */
1957bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
1958c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1959bd6ea91dSdamien 	DELAY(AR_BASE_PHY_ACTIVE_DELAY + synth_delay);
1960bd6ea91dSdamien }
1961bd6ea91dSdamien 
1962bd6ea91dSdamien void
ar5008_disable_phy(struct athn_softc * sc)1963bd6ea91dSdamien ar5008_disable_phy(struct athn_softc *sc)
1964bd6ea91dSdamien {
1965bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS);
1966c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1967bd6ea91dSdamien }
1968bd6ea91dSdamien 
1969bd6ea91dSdamien void
ar5008_init_chains(struct athn_softc * sc)1970bd6ea91dSdamien ar5008_init_chains(struct athn_softc *sc)
1971bd6ea91dSdamien {
1972bd6ea91dSdamien 	if (sc->rxchainmask == 0x5 || sc->txchainmask == 0x5)
1973bd6ea91dSdamien 		AR_SETBITS(sc, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN);
1974bd6ea91dSdamien 
1975bd6ea91dSdamien 	/* Setup chain masks. */
1976bd6ea91dSdamien 	if (sc->mac_ver <= AR_SREV_VERSION_9160 &&
1977bd6ea91dSdamien 	    (sc->rxchainmask == 0x3 || sc->rxchainmask == 0x5)) {
1978bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_RX_CHAINMASK,  0x7);
1979bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_CAL_CHAINMASK, 0x7);
1980bd6ea91dSdamien 	} else {
1981bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_RX_CHAINMASK,  sc->rxchainmask);
1982bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_CAL_CHAINMASK, sc->rxchainmask);
1983bd6ea91dSdamien 	}
1984bd6ea91dSdamien 	AR_WRITE(sc, AR_SELFGEN_MASK, sc->txchainmask);
1985c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
1986bd6ea91dSdamien }
1987bd6ea91dSdamien 
1988bd6ea91dSdamien void
ar5008_set_rxchains(struct athn_softc * sc)1989bd6ea91dSdamien ar5008_set_rxchains(struct athn_softc *sc)
1990bd6ea91dSdamien {
1991bd6ea91dSdamien 	if (sc->rxchainmask == 0x3 || sc->rxchainmask == 0x5) {
1992bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_RX_CHAINMASK,  sc->rxchainmask);
1993bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_CAL_CHAINMASK, sc->rxchainmask);
1994c0a11cf8Sdamien 		AR_WRITE_BARRIER(sc);
1995bd6ea91dSdamien 	}
1996bd6ea91dSdamien }
1997bd6ea91dSdamien 
1998bd6ea91dSdamien void
ar5008_read_noisefloor(struct athn_softc * sc,int16_t * nf,int16_t * nf_ext)1999bd6ea91dSdamien ar5008_read_noisefloor(struct athn_softc *sc, int16_t *nf, int16_t *nf_ext)
2000bd6ea91dSdamien {
20017a911050Sdamien /* Sign-extends 9-bit value (assumes upper bits are zeroes). */
20026c0255d5Sdamien #define SIGN_EXT(v)	(((v) ^ 0x100) - 0x100)
2003bd6ea91dSdamien 	uint32_t reg;
2004bd6ea91dSdamien 	int i;
2005bd6ea91dSdamien 
2006bd6ea91dSdamien 	for (i = 0; i < sc->nrxchains; i++) {
2007bd6ea91dSdamien 		reg = AR_READ(sc, AR_PHY_CCA(i));
2008bd6ea91dSdamien 		if (AR_SREV_9280_10_OR_LATER(sc))
2009bd6ea91dSdamien 			nf[i] = MS(reg, AR9280_PHY_MINCCA_PWR);
2010bd6ea91dSdamien 		else
2011bd6ea91dSdamien 			nf[i] = MS(reg, AR_PHY_MINCCA_PWR);
2012bd6ea91dSdamien 		nf[i] = SIGN_EXT(nf[i]);
2013bd6ea91dSdamien 
2014bd6ea91dSdamien 		reg = AR_READ(sc, AR_PHY_EXT_CCA(i));
2015bd6ea91dSdamien 		if (AR_SREV_9280_10_OR_LATER(sc))
2016bd6ea91dSdamien 			nf_ext[i] = MS(reg, AR9280_PHY_EXT_MINCCA_PWR);
2017bd6ea91dSdamien 		else
2018bd6ea91dSdamien 			nf_ext[i] = MS(reg, AR_PHY_EXT_MINCCA_PWR);
2019bd6ea91dSdamien 		nf_ext[i] = SIGN_EXT(nf_ext[i]);
2020bd6ea91dSdamien 	}
2021bd6ea91dSdamien #undef SIGN_EXT
2022bd6ea91dSdamien }
2023bd6ea91dSdamien 
2024bd6ea91dSdamien void
ar5008_write_noisefloor(struct athn_softc * sc,int16_t * nf,int16_t * nf_ext)2025bd6ea91dSdamien ar5008_write_noisefloor(struct athn_softc *sc, int16_t *nf, int16_t *nf_ext)
2026bd6ea91dSdamien {
2027bd6ea91dSdamien 	uint32_t reg;
2028bd6ea91dSdamien 	int i;
2029bd6ea91dSdamien 
2030bd6ea91dSdamien 	for (i = 0; i < sc->nrxchains; i++) {
2031bd6ea91dSdamien 		reg = AR_READ(sc, AR_PHY_CCA(i));
2032bd6ea91dSdamien 		reg = RW(reg, AR_PHY_MAXCCA_PWR, nf[i]);
2033bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_CCA(i), reg);
2034bd6ea91dSdamien 
2035bd6ea91dSdamien 		reg = AR_READ(sc, AR_PHY_EXT_CCA(i));
2036bd6ea91dSdamien 		reg = RW(reg, AR_PHY_EXT_MAXCCA_PWR, nf_ext[i]);
2037bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_EXT_CCA(i), reg);
2038bd6ea91dSdamien 	}
2039c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2040bd6ea91dSdamien }
2041bd6ea91dSdamien 
20429d1f2812Sstsp int
ar5008_get_noisefloor(struct athn_softc * sc)20439d1f2812Sstsp ar5008_get_noisefloor(struct athn_softc *sc)
2044bd6ea91dSdamien {
2045bd6ea91dSdamien 	int16_t nf[AR_MAX_CHAINS], nf_ext[AR_MAX_CHAINS];
2046bd6ea91dSdamien 	int i;
2047bd6ea91dSdamien 
2048bd6ea91dSdamien 	if (AR_READ(sc, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
2049bd6ea91dSdamien 		/* Noisefloor calibration not finished. */
20509d1f2812Sstsp 		return 0;
2051bd6ea91dSdamien 	}
2052bd6ea91dSdamien 	/* Noisefloor calibration is finished. */
2053bd6ea91dSdamien 	ar5008_read_noisefloor(sc, nf, nf_ext);
2054bd6ea91dSdamien 
2055bd6ea91dSdamien 	/* Update noisefloor history. */
2056bd6ea91dSdamien 	for (i = 0; i < sc->nrxchains; i++) {
2057bd6ea91dSdamien 		sc->nf_hist[sc->nf_hist_cur].nf[i] = nf[i];
2058bd6ea91dSdamien 		sc->nf_hist[sc->nf_hist_cur].nf_ext[i] = nf_ext[i];
2059bd6ea91dSdamien 	}
2060bd6ea91dSdamien 	if (++sc->nf_hist_cur >= ATHN_NF_CAL_HIST_MAX)
2061bd6ea91dSdamien 		sc->nf_hist_cur = 0;
20629d1f2812Sstsp 	return 1;
2063bd6ea91dSdamien }
2064bd6ea91dSdamien 
2065bd6ea91dSdamien void
ar5008_bb_load_noisefloor(struct athn_softc * sc)2066bd6ea91dSdamien ar5008_bb_load_noisefloor(struct athn_softc *sc)
2067bd6ea91dSdamien {
2068bd6ea91dSdamien 	int16_t nf[AR_MAX_CHAINS], nf_ext[AR_MAX_CHAINS];
2069bd6ea91dSdamien 	int i, ntries;
2070bd6ea91dSdamien 
2071bd6ea91dSdamien 	/* Write filtered noisefloor values. */
2072bd6ea91dSdamien 	for (i = 0; i < sc->nrxchains; i++) {
2073bd6ea91dSdamien 		nf[i] = sc->nf_priv[i] * 2;
2074bd6ea91dSdamien 		nf_ext[i] = sc->nf_ext_priv[i] * 2;
2075bd6ea91dSdamien 	}
2076bd6ea91dSdamien 	ar5008_write_noisefloor(sc, nf, nf_ext);
2077bd6ea91dSdamien 
2078bd6ea91dSdamien 	/* Load filtered noisefloor values into baseband. */
2079bd6ea91dSdamien 	AR_CLRBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
2080bd6ea91dSdamien 	AR_CLRBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
2081bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
2082bd6ea91dSdamien 	/* Wait for load to complete. */
2083d439f961Sdamien 	for (ntries = 0; ntries < 1000; ntries++) {
2084bd6ea91dSdamien 		if (!(AR_READ(sc, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF))
2085bd6ea91dSdamien 			break;
2086bd6ea91dSdamien 		DELAY(50);
2087bd6ea91dSdamien 	}
2088d439f961Sdamien 	if (ntries == 1000) {
2089d439f961Sdamien 		DPRINTF(("failed to load noisefloor values\n"));
2090d439f961Sdamien 		return;
2091d439f961Sdamien 	}
2092bd6ea91dSdamien 
20939d1f2812Sstsp 	/*
20949d1f2812Sstsp 	 * Restore noisefloor values to initial (max) values. These will
20959d1f2812Sstsp 	 * be used as initial values during the next NF calibration.
20969d1f2812Sstsp 	 */
2097bd6ea91dSdamien 	for (i = 0; i < AR_MAX_CHAINS; i++)
209804586493Sstsp 		nf[i] = nf_ext[i] = AR_DEFAULT_NOISE_FLOOR;
2099bd6ea91dSdamien 	ar5008_write_noisefloor(sc, nf, nf_ext);
2100bd6ea91dSdamien }
2101bd6ea91dSdamien 
2102bd6ea91dSdamien void
ar5008_apply_noisefloor(struct athn_softc * sc)21039d1f2812Sstsp ar5008_apply_noisefloor(struct athn_softc *sc)
21049d1f2812Sstsp {
21059d1f2812Sstsp 	uint32_t agc_nfcal;
21069d1f2812Sstsp 
21079d1f2812Sstsp 	agc_nfcal = AR_READ(sc, AR_PHY_AGC_CONTROL) &
21089d1f2812Sstsp 	    (AR_PHY_AGC_CONTROL_NF | AR_PHY_AGC_CONTROL_ENABLE_NF |
21099d1f2812Sstsp 	    AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
21109d1f2812Sstsp 
21119d1f2812Sstsp 	if (agc_nfcal & AR_PHY_AGC_CONTROL_NF) {
21129d1f2812Sstsp 		/* Pause running NF calibration while values are updated. */
21139d1f2812Sstsp 		AR_CLRBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
21149d1f2812Sstsp 		AR_WRITE_BARRIER(sc);
21159d1f2812Sstsp 	}
21169d1f2812Sstsp 
21179d1f2812Sstsp 	ar5008_bb_load_noisefloor(sc);
21189d1f2812Sstsp 
21199d1f2812Sstsp 	if (agc_nfcal & AR_PHY_AGC_CONTROL_NF) {
21209d1f2812Sstsp 		/* Restart interrupted NF calibration. */
21219d1f2812Sstsp 		AR_SETBITS(sc, AR_PHY_AGC_CONTROL, agc_nfcal);
21229d1f2812Sstsp 		AR_WRITE_BARRIER(sc);
21239d1f2812Sstsp 	}
21249d1f2812Sstsp }
21259d1f2812Sstsp 
21269d1f2812Sstsp void
ar5008_do_noisefloor_calib(struct athn_softc * sc)21279d1f2812Sstsp ar5008_do_noisefloor_calib(struct athn_softc *sc)
2128bd6ea91dSdamien {
2129bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
2130bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
2131bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
2132c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2133bd6ea91dSdamien }
2134bd6ea91dSdamien 
2135bd6ea91dSdamien void
ar5008_init_noisefloor_calib(struct athn_softc * sc)21369d1f2812Sstsp ar5008_init_noisefloor_calib(struct athn_softc *sc)
2137bd6ea91dSdamien {
2138bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
2139c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2140bd6ea91dSdamien }
2141bd6ea91dSdamien 
2142bd6ea91dSdamien void
ar5008_do_calib(struct athn_softc * sc)2143bd6ea91dSdamien ar5008_do_calib(struct athn_softc *sc)
2144bd6ea91dSdamien {
214526bcd0d6Sdamien 	uint32_t mode, reg;
214626bcd0d6Sdamien 	int log;
2147bd6ea91dSdamien 
2148bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_TIMING_CTRL4_0);
214926bcd0d6Sdamien 	log = AR_SREV_9280_10_OR_LATER(sc) ? 10 : 2;
2150bd6ea91dSdamien 	reg = RW(reg, AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX, log);
2151bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TIMING_CTRL4_0, reg);
2152bd6ea91dSdamien 
215326bcd0d6Sdamien 	if (sc->cur_calib_mask & ATHN_CAL_ADC_GAIN)
2154bd6ea91dSdamien 		mode = AR_PHY_CALMODE_ADC_GAIN;
215526bcd0d6Sdamien 	else if (sc->cur_calib_mask & ATHN_CAL_ADC_DC)
2156bd6ea91dSdamien 		mode = AR_PHY_CALMODE_ADC_DC_PER;
215726bcd0d6Sdamien 	else	/* ATHN_CAL_IQ */
2158bd6ea91dSdamien 		mode = AR_PHY_CALMODE_IQ;
2159bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_CALMODE, mode);
2160bd6ea91dSdamien 
216126bcd0d6Sdamien 	DPRINTF(("starting calibration mode=0x%x\n", mode));
2162bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_TIMING_CTRL4_0, AR_PHY_TIMING_CTRL4_DO_CAL);
2163c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2164bd6ea91dSdamien }
2165bd6ea91dSdamien 
2166bd6ea91dSdamien void
ar5008_next_calib(struct athn_softc * sc)2167bd6ea91dSdamien ar5008_next_calib(struct athn_softc *sc)
2168bd6ea91dSdamien {
216926bcd0d6Sdamien 	/* Check if we have any calibration in progress. */
217026bcd0d6Sdamien 	if (sc->cur_calib_mask != 0) {
217126bcd0d6Sdamien 		if (!(AR_READ(sc, AR_PHY_TIMING_CTRL4_0) &
217226bcd0d6Sdamien 		    AR_PHY_TIMING_CTRL4_DO_CAL)) {
217326bcd0d6Sdamien 			/* Calibration completed for current sample. */
217426bcd0d6Sdamien 			if (sc->cur_calib_mask & ATHN_CAL_ADC_GAIN)
2175bd6ea91dSdamien 				ar5008_calib_adc_gain(sc);
217626bcd0d6Sdamien 			else if (sc->cur_calib_mask & ATHN_CAL_ADC_DC)
2177bd6ea91dSdamien 				ar5008_calib_adc_dc_off(sc);
217826bcd0d6Sdamien 			else	/* ATHN_CAL_IQ */
217926bcd0d6Sdamien 				ar5008_calib_iq(sc);
218026bcd0d6Sdamien 		}
218126bcd0d6Sdamien 	}
2182bd6ea91dSdamien }
2183bd6ea91dSdamien 
2184bd6ea91dSdamien void
ar5008_calib_iq(struct athn_softc * sc)2185bd6ea91dSdamien ar5008_calib_iq(struct athn_softc *sc)
2186bd6ea91dSdamien {
2187bd6ea91dSdamien 	struct athn_iq_cal *cal;
2188bd6ea91dSdamien 	uint32_t reg, i_coff_denom, q_coff_denom;
2189bd6ea91dSdamien 	int32_t i_coff, q_coff;
2190bd6ea91dSdamien 	int i, iq_corr_neg;
2191bd6ea91dSdamien 
2192bd6ea91dSdamien 	for (i = 0; i < AR_MAX_CHAINS; i++) {
2193bd6ea91dSdamien 		cal = &sc->calib.iq[i];
2194bd6ea91dSdamien 
2195bd6ea91dSdamien 		/* Accumulate IQ calibration measures (clear on read). */
2196bd6ea91dSdamien 		cal->pwr_meas_i += AR_READ(sc, AR_PHY_CAL_MEAS_0(i));
2197bd6ea91dSdamien 		cal->pwr_meas_q += AR_READ(sc, AR_PHY_CAL_MEAS_1(i));
2198bd6ea91dSdamien 		cal->iq_corr_meas +=
2199bd6ea91dSdamien 		    (int32_t)AR_READ(sc, AR_PHY_CAL_MEAS_2(i));
2200bd6ea91dSdamien 	}
220126bcd0d6Sdamien 	if (!AR_SREV_9280_10_OR_LATER(sc) &&
220226bcd0d6Sdamien 	    ++sc->calib.nsamples < AR_CAL_SAMPLES) {
2203bd6ea91dSdamien 		/* Not enough samples accumulated, continue. */
2204bd6ea91dSdamien 		ar5008_do_calib(sc);
2205bd6ea91dSdamien 		return;
2206bd6ea91dSdamien 	}
2207bd6ea91dSdamien 
2208bd6ea91dSdamien 	for (i = 0; i < sc->nrxchains; i++) {
2209bd6ea91dSdamien 		cal = &sc->calib.iq[i];
2210bd6ea91dSdamien 
2211bd6ea91dSdamien 		if (cal->pwr_meas_q == 0)
2212bd6ea91dSdamien 			continue;
2213bd6ea91dSdamien 
2214bd6ea91dSdamien 		if ((iq_corr_neg = cal->iq_corr_meas < 0))
2215bd6ea91dSdamien 			cal->iq_corr_meas = -cal->iq_corr_meas;
2216bd6ea91dSdamien 
2217bd6ea91dSdamien 		i_coff_denom =
2218bd6ea91dSdamien 		    (cal->pwr_meas_i / 2 + cal->pwr_meas_q / 2) / 128;
2219bd6ea91dSdamien 		q_coff_denom = cal->pwr_meas_q / 64;
2220bd6ea91dSdamien 
2221bd6ea91dSdamien 		if (i_coff_denom == 0 || q_coff_denom == 0)
2222bd6ea91dSdamien 			continue;	/* Prevents division by zero. */
2223bd6ea91dSdamien 
2224bd6ea91dSdamien 		i_coff = cal->iq_corr_meas / i_coff_denom;
2225bd6ea91dSdamien 		q_coff = (cal->pwr_meas_i / q_coff_denom) - 64;
2226bd6ea91dSdamien 
2227bd6ea91dSdamien 		/* Negate i_coff if iq_corr_meas is positive. */
2228bd6ea91dSdamien 		if (!iq_corr_neg)
2229bd6ea91dSdamien 			i_coff = 0x40 - (i_coff & 0x3f);
2230bd6ea91dSdamien 		if (q_coff > 15)
2231bd6ea91dSdamien 			q_coff = 15;
2232bd6ea91dSdamien 		else if (q_coff <= -16)
2233bd6ea91dSdamien 			q_coff = -16;	/* XXX Linux has a bug here? */
2234bd6ea91dSdamien 
2235bd6ea91dSdamien 		DPRINTFN(2, ("IQ calibration for chain %d\n", i));
2236bd6ea91dSdamien 		reg = AR_READ(sc, AR_PHY_TIMING_CTRL4(i));
2237bd6ea91dSdamien 		reg = RW(reg, AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, i_coff);
2238bd6ea91dSdamien 		reg = RW(reg, AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, q_coff);
2239bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_TIMING_CTRL4(i), reg);
2240bd6ea91dSdamien 	}
2241bd6ea91dSdamien 
224226bcd0d6Sdamien 	/* Apply new settings. */
2243bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_TIMING_CTRL4_0,
2244bd6ea91dSdamien 	    AR_PHY_TIMING_CTRL4_IQCORR_ENABLE);
2245c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
224626bcd0d6Sdamien 
224726bcd0d6Sdamien 	/* IQ calibration done. */
224826bcd0d6Sdamien 	sc->cur_calib_mask &= ~ATHN_CAL_IQ;
224926bcd0d6Sdamien 	memset(&sc->calib, 0, sizeof(sc->calib));
2250bd6ea91dSdamien }
2251bd6ea91dSdamien 
2252bd6ea91dSdamien void
ar5008_calib_adc_gain(struct athn_softc * sc)2253bd6ea91dSdamien ar5008_calib_adc_gain(struct athn_softc *sc)
2254bd6ea91dSdamien {
2255bd6ea91dSdamien 	struct athn_adc_cal *cal;
2256bd6ea91dSdamien 	uint32_t reg, gain_mismatch_i, gain_mismatch_q;
2257bd6ea91dSdamien 	int i;
2258bd6ea91dSdamien 
2259bd6ea91dSdamien 	for (i = 0; i < AR_MAX_CHAINS; i++) {
2260bd6ea91dSdamien 		cal = &sc->calib.adc_gain[i];
2261bd6ea91dSdamien 
2262bd6ea91dSdamien 		/* Accumulate ADC gain measures (clear on read). */
2263bd6ea91dSdamien 		cal->pwr_meas_odd_i  += AR_READ(sc, AR_PHY_CAL_MEAS_0(i));
2264bd6ea91dSdamien 		cal->pwr_meas_even_i += AR_READ(sc, AR_PHY_CAL_MEAS_1(i));
2265bd6ea91dSdamien 		cal->pwr_meas_odd_q  += AR_READ(sc, AR_PHY_CAL_MEAS_2(i));
2266bd6ea91dSdamien 		cal->pwr_meas_even_q += AR_READ(sc, AR_PHY_CAL_MEAS_3(i));
2267bd6ea91dSdamien 	}
226826bcd0d6Sdamien 	if (!AR_SREV_9280_10_OR_LATER(sc) &&
226926bcd0d6Sdamien 	    ++sc->calib.nsamples < AR_CAL_SAMPLES) {
2270bd6ea91dSdamien 		/* Not enough samples accumulated, continue. */
2271bd6ea91dSdamien 		ar5008_do_calib(sc);
2272bd6ea91dSdamien 		return;
2273bd6ea91dSdamien 	}
2274bd6ea91dSdamien 
2275bd6ea91dSdamien 	for (i = 0; i < sc->nrxchains; i++) {
2276bd6ea91dSdamien 		cal = &sc->calib.adc_gain[i];
2277bd6ea91dSdamien 
2278bd6ea91dSdamien 		if (cal->pwr_meas_odd_i == 0 || cal->pwr_meas_even_q == 0)
2279bd6ea91dSdamien 			continue;	/* Prevents division by zero. */
2280bd6ea91dSdamien 
2281bd6ea91dSdamien 		gain_mismatch_i =
2282bd6ea91dSdamien 		    (cal->pwr_meas_even_i * 32) / cal->pwr_meas_odd_i;
2283bd6ea91dSdamien 		gain_mismatch_q =
2284bd6ea91dSdamien 		    (cal->pwr_meas_odd_q * 32) / cal->pwr_meas_even_q;
2285bd6ea91dSdamien 
2286bd6ea91dSdamien 		DPRINTFN(2, ("ADC gain calibration for chain %d\n", i));
2287bd6ea91dSdamien 		reg = AR_READ(sc, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
2288bd6ea91dSdamien 		reg = RW(reg, AR_PHY_NEW_ADC_DC_GAIN_IGAIN, gain_mismatch_i);
2289bd6ea91dSdamien 		reg = RW(reg, AR_PHY_NEW_ADC_DC_GAIN_QGAIN, gain_mismatch_q);
2290bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), reg);
2291bd6ea91dSdamien 	}
2292bd6ea91dSdamien 
229326bcd0d6Sdamien 	/* Apply new settings. */
2294bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
2295bd6ea91dSdamien 	    AR_PHY_NEW_ADC_GAIN_CORR_ENABLE);
2296c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
229726bcd0d6Sdamien 
229826bcd0d6Sdamien 	/* ADC gain calibration done. */
229926bcd0d6Sdamien 	sc->cur_calib_mask &= ~ATHN_CAL_ADC_GAIN;
230026bcd0d6Sdamien 	memset(&sc->calib, 0, sizeof(sc->calib));
2301bd6ea91dSdamien }
2302bd6ea91dSdamien 
2303bd6ea91dSdamien void
ar5008_calib_adc_dc_off(struct athn_softc * sc)2304bd6ea91dSdamien ar5008_calib_adc_dc_off(struct athn_softc *sc)
2305bd6ea91dSdamien {
2306bd6ea91dSdamien 	struct athn_adc_cal *cal;
2307bd6ea91dSdamien 	int32_t dc_offset_mismatch_i, dc_offset_mismatch_q;
2308bd6ea91dSdamien 	uint32_t reg;
2309bd6ea91dSdamien 	int count, i;
2310bd6ea91dSdamien 
2311bd6ea91dSdamien 	for (i = 0; i < AR_MAX_CHAINS; i++) {
2312bd6ea91dSdamien 		cal = &sc->calib.adc_dc_offset[i];
2313bd6ea91dSdamien 
2314bd6ea91dSdamien 		/* Accumulate ADC DC offset measures (clear on read). */
2315bd6ea91dSdamien 		cal->pwr_meas_odd_i  += AR_READ(sc, AR_PHY_CAL_MEAS_0(i));
2316bd6ea91dSdamien 		cal->pwr_meas_even_i += AR_READ(sc, AR_PHY_CAL_MEAS_1(i));
2317bd6ea91dSdamien 		cal->pwr_meas_odd_q  += AR_READ(sc, AR_PHY_CAL_MEAS_2(i));
2318bd6ea91dSdamien 		cal->pwr_meas_even_q += AR_READ(sc, AR_PHY_CAL_MEAS_3(i));
2319bd6ea91dSdamien 	}
232026bcd0d6Sdamien 	if (!AR_SREV_9280_10_OR_LATER(sc) &&
232126bcd0d6Sdamien 	    ++sc->calib.nsamples < AR_CAL_SAMPLES) {
2322bd6ea91dSdamien 		/* Not enough samples accumulated, continue. */
2323bd6ea91dSdamien 		ar5008_do_calib(sc);
2324bd6ea91dSdamien 		return;
2325bd6ea91dSdamien 	}
2326bd6ea91dSdamien 
232726bcd0d6Sdamien 	if (AR_SREV_9280_10_OR_LATER(sc))
232826bcd0d6Sdamien 		count = (1 << (10 + 5));
232926bcd0d6Sdamien 	else
233026bcd0d6Sdamien 		count = (1 << ( 2 + 5)) * AR_CAL_SAMPLES;
2331bd6ea91dSdamien 	for (i = 0; i < sc->nrxchains; i++) {
2332bd6ea91dSdamien 		cal = &sc->calib.adc_dc_offset[i];
2333bd6ea91dSdamien 
2334bd6ea91dSdamien 		dc_offset_mismatch_i =
2335bd6ea91dSdamien 		    (cal->pwr_meas_even_i - cal->pwr_meas_odd_i * 2) / count;
2336bd6ea91dSdamien 		dc_offset_mismatch_q =
2337bd6ea91dSdamien 		    (cal->pwr_meas_odd_q - cal->pwr_meas_even_q * 2) / count;
2338bd6ea91dSdamien 
2339bd6ea91dSdamien 		DPRINTFN(2, ("ADC DC offset calibration for chain %d\n", i));
2340bd6ea91dSdamien 		reg = AR_READ(sc, AR_PHY_NEW_ADC_DC_GAIN_CORR(i));
2341bd6ea91dSdamien 		reg = RW(reg, AR_PHY_NEW_ADC_DC_GAIN_QDC,
2342bd6ea91dSdamien 		    dc_offset_mismatch_q);
2343bd6ea91dSdamien 		reg = RW(reg, AR_PHY_NEW_ADC_DC_GAIN_IDC,
2344bd6ea91dSdamien 		    dc_offset_mismatch_i);
2345bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY_NEW_ADC_DC_GAIN_CORR(i), reg);
2346bd6ea91dSdamien 	}
2347bd6ea91dSdamien 
234826bcd0d6Sdamien 	/* Apply new settings. */
2349bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_NEW_ADC_DC_GAIN_CORR(0),
2350bd6ea91dSdamien 	    AR_PHY_NEW_ADC_DC_OFFSET_CORR_ENABLE);
2351c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
235226bcd0d6Sdamien 
235326bcd0d6Sdamien 	/* ADC DC offset calibration done. */
235426bcd0d6Sdamien 	sc->cur_calib_mask &= ~ATHN_CAL_ADC_DC;
235526bcd0d6Sdamien 	memset(&sc->calib, 0, sizeof(sc->calib));
2356bd6ea91dSdamien }
2357bd6ea91dSdamien 
2358bd6ea91dSdamien void
ar5008_write_txpower(struct athn_softc * sc,int16_t power[ATHN_POWER_COUNT])2359bd6ea91dSdamien ar5008_write_txpower(struct athn_softc *sc, int16_t power[ATHN_POWER_COUNT])
2360bd6ea91dSdamien {
2361bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE1,
2362bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM18  ] & 0x3f) << 24 |
2363bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM12  ] & 0x3f) << 16 |
2364bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM9   ] & 0x3f) <<  8 |
2365bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM6   ] & 0x3f));
2366bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE2,
2367bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM54  ] & 0x3f) << 24 |
2368bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM48  ] & 0x3f) << 16 |
2369bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM36  ] & 0x3f) <<  8 |
2370bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM24  ] & 0x3f));
2371bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE3,
2372bd6ea91dSdamien 	    (power[ATHN_POWER_CCK2_SP ] & 0x3f) << 24 |
2373bd6ea91dSdamien 	    (power[ATHN_POWER_CCK2_LP ] & 0x3f) << 16 |
2374bd6ea91dSdamien 	    (power[ATHN_POWER_XR      ] & 0x3f) <<  8 |
2375bd6ea91dSdamien 	    (power[ATHN_POWER_CCK1_LP ] & 0x3f));
2376bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE4,
2377bd6ea91dSdamien 	    (power[ATHN_POWER_CCK11_SP] & 0x3f) << 24 |
2378bd6ea91dSdamien 	    (power[ATHN_POWER_CCK11_LP] & 0x3f) << 16 |
2379bd6ea91dSdamien 	    (power[ATHN_POWER_CCK55_SP] & 0x3f) <<  8 |
2380bd6ea91dSdamien 	    (power[ATHN_POWER_CCK55_LP] & 0x3f));
2381bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE5,
2382bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(3) ] & 0x3f) << 24 |
2383bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(2) ] & 0x3f) << 16 |
2384bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(1) ] & 0x3f) <<  8 |
2385bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(0) ] & 0x3f));
2386bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE6,
2387bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(7) ] & 0x3f) << 24 |
2388bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(6) ] & 0x3f) << 16 |
2389bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(5) ] & 0x3f) <<  8 |
2390bd6ea91dSdamien 	    (power[ATHN_POWER_HT20(4) ] & 0x3f));
2391bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE7,
2392bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(3) ] & 0x3f) << 24 |
2393bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(2) ] & 0x3f) << 16 |
2394bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(1) ] & 0x3f) <<  8 |
2395bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(0) ] & 0x3f));
2396bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE8,
2397bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(7) ] & 0x3f) << 24 |
2398bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(6) ] & 0x3f) << 16 |
2399bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(5) ] & 0x3f) <<  8 |
2400bd6ea91dSdamien 	    (power[ATHN_POWER_HT40(4) ] & 0x3f));
2401bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_POWER_TX_RATE9,
2402bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM_EXT] & 0x3f) << 24 |
2403bd6ea91dSdamien 	    (power[ATHN_POWER_CCK_EXT ] & 0x3f) << 16 |
2404bd6ea91dSdamien 	    (power[ATHN_POWER_OFDM_DUP] & 0x3f) <<  8 |
2405bd6ea91dSdamien 	    (power[ATHN_POWER_CCK_DUP ] & 0x3f));
2406c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2407bd6ea91dSdamien }
2408bd6ea91dSdamien 
2409bd6ea91dSdamien void
ar5008_set_viterbi_mask(struct athn_softc * sc,int bin)2410bd6ea91dSdamien ar5008_set_viterbi_mask(struct athn_softc *sc, int bin)
2411bd6ea91dSdamien {
2412bd6ea91dSdamien 	uint32_t mask[4], reg;
2413bd6ea91dSdamien 	uint8_t m[62], p[62];	/* XXX use bit arrays? */
2414bd6ea91dSdamien 	int i, bit, cur;
2415bd6ea91dSdamien 
2416bd6ea91dSdamien 	/* Compute pilot mask. */
2417bd6ea91dSdamien 	cur = -6000;
2418bd6ea91dSdamien 	for (i = 0; i < 4; i++) {
2419bd6ea91dSdamien 		mask[i] = 0;
2420bd6ea91dSdamien 		for (bit = 0; bit < 30; bit++) {
2421bd6ea91dSdamien 			if (abs(cur - bin) < 100)
2422bd6ea91dSdamien 				mask[i] |= 1 << bit;
2423bd6ea91dSdamien 			cur += 100;
2424bd6ea91dSdamien 		}
2425bd6ea91dSdamien 		if (cur == 0)	/* Skip entry "0". */
2426bd6ea91dSdamien 			cur = 100;
2427bd6ea91dSdamien 	}
2428bd6ea91dSdamien 	/* Write entries from -6000 to -3100. */
2429bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TIMING7, mask[0]);
2430bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TIMING9, mask[0]);
2431bd6ea91dSdamien 	/* Write entries from -3000 to -100. */
2432bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TIMING8, mask[1]);
2433bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TIMING10, mask[1]);
2434bd6ea91dSdamien 	/* Write entries from 100 to 3000. */
2435bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_PILOT_MASK_01_30, mask[2]);
2436bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_CHANNEL_MASK_01_30, mask[2]);
2437bd6ea91dSdamien 	/* Write entries from 3100 to 6000. */
2438bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_PILOT_MASK_31_60, mask[3]);
2439bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_CHANNEL_MASK_31_60, mask[3]);
2440bd6ea91dSdamien 
2441bd6ea91dSdamien 	/* Compute viterbi mask. */
2442bd6ea91dSdamien 	for (cur = 6100; cur >= 0; cur -= 100)
2443bd6ea91dSdamien 		p[+cur / 100] = abs(cur - bin) < 75;
2444bd6ea91dSdamien 	for (cur = -100; cur >= -6100; cur -= 100)
2445bd6ea91dSdamien 		m[-cur / 100] = abs(cur - bin) < 75;
2446bd6ea91dSdamien 
24477a911050Sdamien 	/* Write viterbi mask (XXX needs to be reworked). */
2448bd6ea91dSdamien 	reg =
2449bd6ea91dSdamien 	    m[46] << 30 | m[47] << 28 | m[48] << 26 | m[49] << 24 |
2450bd6ea91dSdamien 	    m[50] << 22 | m[51] << 20 | m[52] << 18 | m[53] << 16 |
2451bd6ea91dSdamien 	    m[54] << 14 | m[55] << 12 | m[56] << 10 | m[57] <<  8 |
2452bd6ea91dSdamien 	    m[58] <<  6 | m[59] <<  4 | m[60] <<  2 | m[61] <<  0;
2453bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_BIN_MASK_1, reg);
2454bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_M_46_61, reg);
2455bd6ea91dSdamien 
2456bd6ea91dSdamien 	/* XXX m[48] should be m[38] ? */
2457bd6ea91dSdamien 	reg =             m[31] << 28 | m[32] << 26 | m[33] << 24 |
2458bd6ea91dSdamien 	    m[34] << 22 | m[35] << 20 | m[36] << 18 | m[37] << 16 |
2459bd6ea91dSdamien 	    m[48] << 14 | m[39] << 12 | m[40] << 10 | m[41] <<  8 |
2460bd6ea91dSdamien 	    m[42] <<  6 | m[43] <<  4 | m[44] <<  2 | m[45] <<  0;
2461bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_BIN_MASK_2, reg);
2462bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_M_31_45, reg);
2463bd6ea91dSdamien 
2464bd6ea91dSdamien 	/* XXX This one is weird too. */
2465bd6ea91dSdamien 	reg =
2466bd6ea91dSdamien 	    m[16] << 30 | m[16] << 28 | m[18] << 26 | m[18] << 24 |
2467bd6ea91dSdamien 	    m[20] << 22 | m[20] << 20 | m[22] << 18 | m[22] << 16 |
2468bd6ea91dSdamien 	    m[24] << 14 | m[24] << 12 | m[25] << 10 | m[26] <<  8 |
2469bd6ea91dSdamien 	    m[27] <<  6 | m[28] <<  4 | m[29] <<  2 | m[30] <<  0;
2470bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_BIN_MASK_3, reg);
2471bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_M_16_30, reg);
2472bd6ea91dSdamien 
2473bd6ea91dSdamien 	reg =
2474bd6ea91dSdamien 	    m[ 0] << 30 | m[ 1] << 28 | m[ 2] << 26 | m[ 3] << 24 |
2475bd6ea91dSdamien 	    m[ 4] << 22 | m[ 5] << 20 | m[ 6] << 18 | m[ 7] << 16 |
2476bd6ea91dSdamien 	    m[ 8] << 14 | m[ 9] << 12 | m[10] << 10 | m[11] <<  8 |
2477bd6ea91dSdamien 	    m[12] <<  6 | m[13] <<  4 | m[14] <<  2 | m[15] <<  0;
2478bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_MASK_CTL, reg);
2479bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_M_00_15, reg);
2480bd6ea91dSdamien 
2481bd6ea91dSdamien 	reg =             p[15] << 28 | p[14] << 26 | p[13] << 24 |
2482bd6ea91dSdamien 	    p[12] << 22 | p[11] << 20 | p[10] << 18 | p[ 9] << 16 |
2483bd6ea91dSdamien 	    p[ 8] << 14 | p[ 7] << 12 | p[ 6] << 10 | p[ 5] <<  8 |
2484bd6ea91dSdamien 	    p[ 4] <<  6 | p[ 3] <<  4 | p[ 2] <<  2 | p[ 1] <<  0;
2485bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_BIN_MASK2_1, reg);
2486bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_P_15_01, reg);
2487bd6ea91dSdamien 
2488bd6ea91dSdamien 	reg =             p[30] << 28 | p[29] << 26 | p[28] << 24 |
2489bd6ea91dSdamien 	    p[27] << 22 | p[26] << 20 | p[25] << 18 | p[24] << 16 |
2490bd6ea91dSdamien 	    p[23] << 14 | p[22] << 12 | p[21] << 10 | p[20] <<  8 |
2491bd6ea91dSdamien 	    p[19] <<  6 | p[18] <<  4 | p[17] <<  2 | p[16] <<  0;
2492bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_BIN_MASK2_2, reg);
2493bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_P_30_16, reg);
2494bd6ea91dSdamien 
2495bd6ea91dSdamien 	reg =             p[45] << 28 | p[44] << 26 | p[43] << 24 |
2496bd6ea91dSdamien 	    p[42] << 22 | p[41] << 20 | p[40] << 18 | p[39] << 16 |
2497bd6ea91dSdamien 	    p[38] << 14 | p[37] << 12 | p[36] << 10 | p[35] <<  8 |
2498bd6ea91dSdamien 	    p[34] <<  6 | p[33] <<  4 | p[32] <<  2 | p[31] <<  0;
2499bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_BIN_MASK2_3, reg);
2500bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_P_45_31, reg);
2501bd6ea91dSdamien 
2502bd6ea91dSdamien 	reg =
2503bd6ea91dSdamien 	    p[61] << 30 | p[60] << 28 | p[59] << 26 | p[58] << 24 |
2504bd6ea91dSdamien 	    p[57] << 22 | p[56] << 20 | p[55] << 18 | p[54] << 16 |
2505bd6ea91dSdamien 	    p[53] << 14 | p[52] << 12 | p[51] << 10 | p[50] <<  8 |
2506bd6ea91dSdamien 	    p[49] <<  6 | p[48] <<  4 | p[47] <<  2 | p[46] <<  0;
2507bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_BIN_MASK2_4, reg);
2508bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_VIT_MASK2_P_61_46, reg);
2509c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2510bd6ea91dSdamien }
2511bd6ea91dSdamien 
2512bd6ea91dSdamien void
ar5008_hw_init(struct athn_softc * sc,struct ieee80211_channel * c,struct ieee80211_channel * extc)2513bd6ea91dSdamien ar5008_hw_init(struct athn_softc *sc, struct ieee80211_channel *c,
2514bd6ea91dSdamien     struct ieee80211_channel *extc)
2515bd6ea91dSdamien {
2516bd6ea91dSdamien 	struct athn_ops *ops = &sc->ops;
2517bd6ea91dSdamien 	const struct athn_ini *ini = sc->ini;
2518bd6ea91dSdamien 	const uint32_t *pvals;
25197a911050Sdamien 	uint32_t reg;
2520bd6ea91dSdamien 	int i;
2521bd6ea91dSdamien 
2522bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY(0), 0x00000007);
2523bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
2524bd6ea91dSdamien 
2525bd6ea91dSdamien 	if (!AR_SINGLE_CHIP(sc))
2526bd6ea91dSdamien 		ar5416_reset_addac(sc, c);
2527bd6ea91dSdamien 
2528bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC);
2529bd6ea91dSdamien 
2530bd6ea91dSdamien 	/* First initialization step (depends on channel band/bandwidth). */
2531bd6ea91dSdamien 	if (extc != NULL) {
2532bd6ea91dSdamien 		if (IEEE80211_IS_CHAN_2GHZ(c))
2533bd6ea91dSdamien 			pvals = ini->vals_2g40;
2534bd6ea91dSdamien 		else
2535bd6ea91dSdamien 			pvals = ini->vals_5g40;
25365e32cd22Sstsp 	} else {
2537bd6ea91dSdamien 		if (IEEE80211_IS_CHAN_2GHZ(c))
2538bd6ea91dSdamien 			pvals = ini->vals_2g20;
2539bd6ea91dSdamien 		else
2540bd6ea91dSdamien 			pvals = ini->vals_5g20;
2541bd6ea91dSdamien 	}
2542bd6ea91dSdamien 	DPRINTFN(4, ("writing modal init vals\n"));
2543bd6ea91dSdamien 	for (i = 0; i < ini->nregs; i++) {
2544ffd25815Sdamien 		uint32_t val = pvals[i];
2545ffd25815Sdamien 
2546ffd25815Sdamien 		/* Fix AR_AN_TOP2 initialization value if required. */
2547ffd25815Sdamien 		if (ini->regs[i] == AR_AN_TOP2 &&
2548ffd25815Sdamien 		    (sc->flags & ATHN_FLAG_AN_TOP2_FIXUP))
2549ffd25815Sdamien 			val &= ~AR_AN_TOP2_PWDCLKIND;
2550ffd25815Sdamien 		AR_WRITE(sc, ini->regs[i], val);
2551c0a11cf8Sdamien 		if (AR_IS_ANALOG_REG(ini->regs[i])) {
2552c0a11cf8Sdamien 			AR_WRITE_BARRIER(sc);
2553bd6ea91dSdamien 			DELAY(100);
2554c0a11cf8Sdamien 		}
2555bd6ea91dSdamien 		if ((i & 0x1f) == 0)
2556bd6ea91dSdamien 			DELAY(1);
2557bd6ea91dSdamien 	}
2558c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2559bd6ea91dSdamien 
2560bd6ea91dSdamien 	if (sc->rx_gain != NULL)
2561bd6ea91dSdamien 		ar9280_reset_rx_gain(sc, c);
2562bd6ea91dSdamien 	if (sc->tx_gain != NULL)
2563bd6ea91dSdamien 		ar9280_reset_tx_gain(sc, c);
2564bd6ea91dSdamien 
256513236e8dSdamien 	if (AR_SREV_9271_10(sc)) {
256613236e8dSdamien 		AR_WRITE(sc, AR_PHY(68), 0x30002311);
256713236e8dSdamien 		AR_WRITE(sc, AR_PHY_RF_CTL3, 0x0a020001);
256813236e8dSdamien 	}
256913236e8dSdamien 	AR_WRITE_BARRIER(sc);
257013236e8dSdamien 
2571bd6ea91dSdamien 	/* Second initialization step (common to all channels). */
2572bd6ea91dSdamien 	DPRINTFN(4, ("writing common init vals\n"));
2573bd6ea91dSdamien 	for (i = 0; i < ini->ncmregs; i++) {
2574bd6ea91dSdamien 		AR_WRITE(sc, ini->cmregs[i], ini->cmvals[i]);
2575c0a11cf8Sdamien 		if (AR_IS_ANALOG_REG(ini->cmregs[i])) {
2576c0a11cf8Sdamien 			AR_WRITE_BARRIER(sc);
2577bd6ea91dSdamien 			DELAY(100);
2578c0a11cf8Sdamien 		}
2579bd6ea91dSdamien 		if ((i & 0x1f) == 0)
2580bd6ea91dSdamien 			DELAY(1);
2581bd6ea91dSdamien 	}
258213236e8dSdamien 	AR_WRITE_BARRIER(sc);
2583bd6ea91dSdamien 
2584bd6ea91dSdamien 	if (!AR_SINGLE_CHIP(sc))
2585bd6ea91dSdamien 		ar5416_reset_bb_gain(sc, c);
2586bd6ea91dSdamien 
258797bf8fdcSdamien 	if (IEEE80211_IS_CHAN_5GHZ(c) &&
258897bf8fdcSdamien 	    (sc->flags & ATHN_FLAG_FAST_PLL_CLOCK)) {
258997bf8fdcSdamien 		/* Update modal values for fast PLL clock. */
259097bf8fdcSdamien 		if (extc != NULL)
259197bf8fdcSdamien 			pvals = ini->fastvals_5g40;
259297bf8fdcSdamien 		else
259397bf8fdcSdamien 			pvals = ini->fastvals_5g20;
259497bf8fdcSdamien 		DPRINTFN(4, ("writing fast pll clock init vals\n"));
259597bf8fdcSdamien 		for (i = 0; i < ini->nfastregs; i++) {
259697bf8fdcSdamien 			AR_WRITE(sc, ini->fastregs[i], pvals[i]);
2597c0a11cf8Sdamien 			if (AR_IS_ANALOG_REG(ini->fastregs[i])) {
2598c0a11cf8Sdamien 				AR_WRITE_BARRIER(sc);
259997bf8fdcSdamien 				DELAY(100);
2600c0a11cf8Sdamien 			}
260197bf8fdcSdamien 			if ((i & 0x1f) == 0)
260297bf8fdcSdamien 				DELAY(1);
260397bf8fdcSdamien 		}
260497bf8fdcSdamien 	}
260597bf8fdcSdamien 
2606bd6ea91dSdamien 	/*
2607bd6ea91dSdamien 	 * Set the RX_ABORT and RX_DIS bits to prevent frames with corrupted
2608bd6ea91dSdamien 	 * descriptor status.
2609bd6ea91dSdamien 	 */
2610bd6ea91dSdamien 	AR_SETBITS(sc, AR_DIAG_SW, AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT);
2611bd6ea91dSdamien 
2612bd6ea91dSdamien 	/* Hardware workarounds for occasional Rx data corruption. */
26137a911050Sdamien 	if (AR_SREV_9280_10_OR_LATER(sc)) {
26147a911050Sdamien 		reg = AR_READ(sc, AR_PCU_MISC_MODE2);
26157a911050Sdamien 		if (!AR_SREV_9271(sc))
26167a911050Sdamien 			reg &= ~AR_PCU_MISC_MODE2_HWWAR1;
2617bd6ea91dSdamien 		if (AR_SREV_9287_10_OR_LATER(sc))
26187a911050Sdamien 			reg &= ~AR_PCU_MISC_MODE2_HWWAR2;
26197a911050Sdamien 		AR_WRITE(sc, AR_PCU_MISC_MODE2, reg);
2620bd6ea91dSdamien 
26217a911050Sdamien 	} else if (AR_SREV_5416_20_OR_LATER(sc)) {
2622bd6ea91dSdamien 		/* Disable baseband clock gating. */
2623bd6ea91dSdamien 		AR_WRITE(sc, AR_PHY(651), 0x11);
2624bd6ea91dSdamien 
2625bd6ea91dSdamien 		if (AR_SREV_9160(sc)) {
2626bd6ea91dSdamien 			/* Disable RIFS search to fix baseband hang. */
2627bd6ea91dSdamien 			AR_CLRBITS(sc, AR_PHY_HEAVY_CLIP_FACTOR_RIFS,
2628bd6ea91dSdamien 			    AR_PHY_RIFS_INIT_DELAY_M);
2629bd6ea91dSdamien 		}
2630bd6ea91dSdamien 	}
2631c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2632bd6ea91dSdamien 
2633bd6ea91dSdamien 	ar5008_set_phy(sc, c, extc);
2634bd6ea91dSdamien 	ar5008_init_chains(sc);
2635bd6ea91dSdamien 
2636b46f6896Sdamien 	if (sc->flags & ATHN_FLAG_OLPC) {
2637b46f6896Sdamien 		extern int ticks;
2638b46f6896Sdamien 		sc->olpc_ticks = ticks;
2639bd6ea91dSdamien 		ops->olpc_init(sc);
2640b46f6896Sdamien 	}
2641b46f6896Sdamien 
2642bd6ea91dSdamien 	ops->set_txpower(sc, c, extc);
2643bd6ea91dSdamien 
2644bd6ea91dSdamien 	if (!AR_SINGLE_CHIP(sc))
2645bd6ea91dSdamien 		ar5416_rf_reset(sc, c);
2646bd6ea91dSdamien }
2647bd6ea91dSdamien 
2648bd6ea91dSdamien uint8_t
ar5008_get_vpd(uint8_t pwr,const uint8_t * pwrPdg,const uint8_t * vpdPdg,int nicepts)2649bd6ea91dSdamien ar5008_get_vpd(uint8_t pwr, const uint8_t *pwrPdg, const uint8_t *vpdPdg,
2650bd6ea91dSdamien     int nicepts)
2651bd6ea91dSdamien {
2652bd6ea91dSdamien 	uint8_t vpd;
2653bd6ea91dSdamien 	int i, lo, hi;
2654bd6ea91dSdamien 
2655bd6ea91dSdamien 	for (i = 0; i < nicepts; i++)
2656bd6ea91dSdamien 		if (pwrPdg[i] > pwr)
2657bd6ea91dSdamien 			break;
2658bd6ea91dSdamien 	hi = i;
2659bd6ea91dSdamien 	lo = hi - 1;
2660bd6ea91dSdamien 	if (lo == -1)
2661bd6ea91dSdamien 		lo = hi;
2662bd6ea91dSdamien 	else if (hi == nicepts)
2663bd6ea91dSdamien 		hi = lo;
2664bd6ea91dSdamien 
2665bd6ea91dSdamien 	vpd = athn_interpolate(pwr, pwrPdg[lo], vpdPdg[lo],
2666bd6ea91dSdamien 	    pwrPdg[hi], vpdPdg[hi]);
2667bd6ea91dSdamien 	return (vpd);
2668bd6ea91dSdamien }
2669bd6ea91dSdamien 
2670bd6ea91dSdamien void
ar5008_get_pdadcs(struct athn_softc * sc,uint8_t fbin,struct athn_pier * lopier,struct athn_pier * hipier,int nxpdgains,int nicepts,uint8_t overlap,uint8_t * boundaries,uint8_t * pdadcs)2671bd6ea91dSdamien ar5008_get_pdadcs(struct athn_softc *sc, uint8_t fbin,
2672bd6ea91dSdamien     struct athn_pier *lopier, struct athn_pier *hipier, int nxpdgains,
2673bd6ea91dSdamien     int nicepts, uint8_t overlap, uint8_t *boundaries, uint8_t *pdadcs)
2674bd6ea91dSdamien {
2675bd6ea91dSdamien #define DB(x)	((x) / 2)	/* Convert half dB to dB. */
2676bd6ea91dSdamien 	uint8_t minpwr[AR_PD_GAINS_IN_MASK], maxpwr[AR_PD_GAINS_IN_MASK];
2677bd6ea91dSdamien 	uint8_t vpd[AR_MAX_PWR_RANGE_IN_HALF_DB], pwr;
2678bd6ea91dSdamien 	uint8_t lovpd, hivpd, boundary;
2679bd6ea91dSdamien 	int16_t ss, delta, vpdstep, val;
2680bd6ea91dSdamien 	int i, j, npdadcs, nvpds, maxidx, tgtidx;
2681bd6ea91dSdamien 
2682bd6ea91dSdamien 	/* Compute min and max power in half dB for each pdGain. */
2683bd6ea91dSdamien 	for (i = 0; i < nxpdgains; i++) {
2684bd6ea91dSdamien 		minpwr[i] = MAX(lopier->pwr[i][0], hipier->pwr[i][0]);
2685bd6ea91dSdamien 		maxpwr[i] = MIN(lopier->pwr[i][nicepts - 1],
2686bd6ea91dSdamien 		    hipier->pwr[i][nicepts - 1]);
2687bd6ea91dSdamien 	}
2688bd6ea91dSdamien 
2689ffd25815Sdamien 	/* Fill phase domain analog-to-digital converter (PDADC) table. */
2690bd6ea91dSdamien 	npdadcs = 0;
2691bd6ea91dSdamien 	for (i = 0; i < nxpdgains; i++) {
2692bd6ea91dSdamien 		if (i != nxpdgains - 1)
2693bd6ea91dSdamien 			boundaries[i] = DB(maxpwr[i] + minpwr[i + 1]) / 2;
2694bd6ea91dSdamien 		else
2695bd6ea91dSdamien 			boundaries[i] = DB(maxpwr[i]);
2696bd6ea91dSdamien 		if (boundaries[i] > AR_MAX_RATE_POWER)
2697bd6ea91dSdamien 			boundaries[i] = AR_MAX_RATE_POWER;
2698bd6ea91dSdamien 
2699bd6ea91dSdamien 		if (i == 0 && !AR_SREV_5416_20_OR_LATER(sc)) {
27007a911050Sdamien 			/* Fix the gain delta (AR5416 1.0 only). */
2701bd6ea91dSdamien 			delta = boundaries[0] - 23;
2702bd6ea91dSdamien 			boundaries[0] = 23;
2703bd6ea91dSdamien 		} else
2704bd6ea91dSdamien 			delta = 0;
2705bd6ea91dSdamien 
2706bd6ea91dSdamien 		/* Find starting index for this pdGain. */
2707bd6ea91dSdamien 		if (i != 0) {
2708bd6ea91dSdamien 			ss = boundaries[i - 1] - DB(minpwr[i]) -
2709bd6ea91dSdamien 			    overlap + 1 + delta;
2710bd6ea91dSdamien 		} else if (AR_SREV_9280_10_OR_LATER(sc)) {
2711bd6ea91dSdamien 			ss = -DB(minpwr[i]);
2712bd6ea91dSdamien 		} else
2713bd6ea91dSdamien 			ss = 0;
2714bd6ea91dSdamien 
2715bd6ea91dSdamien 		/* Compute Vpd table for this pdGain. */
2716bd6ea91dSdamien 		nvpds = DB(maxpwr[i] - minpwr[i]) + 1;
2717bd6ea91dSdamien 		memset(vpd, 0, sizeof(vpd));
2718bd6ea91dSdamien 		pwr = minpwr[i];
2719bd6ea91dSdamien 		for (j = 0; j < nvpds; j++) {
2720bd6ea91dSdamien 			/* Get lower and higher Vpd. */
2721bd6ea91dSdamien 			lovpd = ar5008_get_vpd(pwr, lopier->pwr[i],
2722bd6ea91dSdamien 			    lopier->vpd[i], nicepts);
2723bd6ea91dSdamien 			hivpd = ar5008_get_vpd(pwr, hipier->pwr[i],
2724bd6ea91dSdamien 			    hipier->vpd[i], nicepts);
2725bd6ea91dSdamien 
2726bd6ea91dSdamien 			/* Interpolate the final Vpd. */
2727bd6ea91dSdamien 			vpd[j] = athn_interpolate(fbin,
2728bd6ea91dSdamien 			    lopier->fbin, lovpd, hipier->fbin, hivpd);
2729bd6ea91dSdamien 
2730bd6ea91dSdamien 			pwr += 2;	/* In half dB. */
2731bd6ea91dSdamien 		}
2732bd6ea91dSdamien 
2733bd6ea91dSdamien 		/* Extrapolate data for ss < 0. */
2734bd6ea91dSdamien 		if (vpd[1] > vpd[0])
2735bd6ea91dSdamien 			vpdstep = vpd[1] - vpd[0];
2736bd6ea91dSdamien 		else
2737bd6ea91dSdamien 			vpdstep = 1;
2738bd6ea91dSdamien 		while (ss < 0 && npdadcs < AR_NUM_PDADC_VALUES - 1) {
2739bd6ea91dSdamien 			val = vpd[0] + ss * vpdstep;
2740bd6ea91dSdamien 			pdadcs[npdadcs++] = MAX(val, 0);
2741bd6ea91dSdamien 			ss++;
2742bd6ea91dSdamien 		}
2743bd6ea91dSdamien 
2744bd6ea91dSdamien 		tgtidx = boundaries[i] + overlap - DB(minpwr[i]);
2745bd6ea91dSdamien 		maxidx = MIN(tgtidx, nvpds);
2746bd6ea91dSdamien 		while (ss < maxidx && npdadcs < AR_NUM_PDADC_VALUES - 1)
2747bd6ea91dSdamien 			pdadcs[npdadcs++] = vpd[ss++];
2748bd6ea91dSdamien 
27495a2e8763Sdamien 		if (tgtidx < maxidx)
2750bd6ea91dSdamien 			continue;
2751bd6ea91dSdamien 
2752bd6ea91dSdamien 		/* Extrapolate data for maxidx <= ss <= tgtidx. */
2753bd6ea91dSdamien 		if (vpd[nvpds - 1] > vpd[nvpds - 2])
2754bd6ea91dSdamien 			vpdstep = vpd[nvpds - 1] - vpd[nvpds - 2];
2755bd6ea91dSdamien 		else
2756bd6ea91dSdamien 			vpdstep = 1;
2757bd6ea91dSdamien 		while (ss <= tgtidx && npdadcs < AR_NUM_PDADC_VALUES - 1) {
2758bd6ea91dSdamien 			val = vpd[nvpds - 1] + (ss - maxidx + 1) * vpdstep;
2759bd6ea91dSdamien 			pdadcs[npdadcs++] = MIN(val, 255);
2760bd6ea91dSdamien 			ss++;
2761bd6ea91dSdamien 		}
2762bd6ea91dSdamien 	}
2763bd6ea91dSdamien 
2764bd6ea91dSdamien 	/* Fill remaining PDADC and boundaries entries. */
2765bd6ea91dSdamien 	if (AR_SREV_9285(sc))
2766bd6ea91dSdamien 		boundary = AR9285_PD_GAIN_BOUNDARY_DEFAULT;
2767bd6ea91dSdamien 	else	/* Fill with latest. */
2768bd6ea91dSdamien 		boundary = boundaries[nxpdgains - 1];
2769bd6ea91dSdamien 
2770bd6ea91dSdamien 	for (; nxpdgains < AR_PD_GAINS_IN_MASK; nxpdgains++)
2771bd6ea91dSdamien 		boundaries[nxpdgains] = boundary;
2772bd6ea91dSdamien 
2773bd6ea91dSdamien 	for (; npdadcs < AR_NUM_PDADC_VALUES; npdadcs++)
2774bd6ea91dSdamien 		pdadcs[npdadcs] = pdadcs[npdadcs - 1];
2775bd6ea91dSdamien #undef DB
2776bd6ea91dSdamien }
2777bd6ea91dSdamien 
2778bd6ea91dSdamien void
ar5008_get_lg_tpow(struct athn_softc * sc,struct ieee80211_channel * c,uint8_t ctl,const struct ar_cal_target_power_leg * tgt,int nchans,uint8_t tpow[4])2779bd6ea91dSdamien ar5008_get_lg_tpow(struct athn_softc *sc, struct ieee80211_channel *c,
2780bd6ea91dSdamien     uint8_t ctl, const struct ar_cal_target_power_leg *tgt, int nchans,
2781bd6ea91dSdamien     uint8_t tpow[4])
2782bd6ea91dSdamien {
2783bd6ea91dSdamien 	uint8_t fbin;
2784bd6ea91dSdamien 	int i, lo, hi;
2785bd6ea91dSdamien 
27867a911050Sdamien 	/* Find interval (lower and upper indices). */
2787bd6ea91dSdamien 	fbin = athn_chan2fbin(c);
2788bd6ea91dSdamien 	for (i = 0; i < nchans; i++) {
2789bd6ea91dSdamien 		if (tgt[i].bChannel == AR_BCHAN_UNUSED ||
2790bd6ea91dSdamien 		    tgt[i].bChannel > fbin)
2791bd6ea91dSdamien 			break;
2792bd6ea91dSdamien 	}
2793bd6ea91dSdamien 	hi = i;
2794bd6ea91dSdamien 	lo = hi - 1;
2795bd6ea91dSdamien 	if (lo == -1)
2796bd6ea91dSdamien 		lo = hi;
2797bd6ea91dSdamien 	else if (hi == nchans || tgt[hi].bChannel == AR_BCHAN_UNUSED)
2798bd6ea91dSdamien 		hi = lo;
2799bd6ea91dSdamien 
2800bd6ea91dSdamien 	/* Interpolate values. */
2801bd6ea91dSdamien 	for (i = 0; i < 4; i++) {
2802bd6ea91dSdamien 		tpow[i] = athn_interpolate(fbin,
2803bd6ea91dSdamien 		    tgt[lo].bChannel, tgt[lo].tPow2x[i],
2804bd6ea91dSdamien 		    tgt[hi].bChannel, tgt[hi].tPow2x[i]);
2805bd6ea91dSdamien 	}
2806ffd25815Sdamien 	/* XXX Apply conformance testing limit. */
2807bd6ea91dSdamien }
2808bd6ea91dSdamien 
2809bd6ea91dSdamien void
ar5008_get_ht_tpow(struct athn_softc * sc,struct ieee80211_channel * c,uint8_t ctl,const struct ar_cal_target_power_ht * tgt,int nchans,uint8_t tpow[8])2810bd6ea91dSdamien ar5008_get_ht_tpow(struct athn_softc *sc, struct ieee80211_channel *c,
2811bd6ea91dSdamien     uint8_t ctl, const struct ar_cal_target_power_ht *tgt, int nchans,
2812bd6ea91dSdamien     uint8_t tpow[8])
2813bd6ea91dSdamien {
2814bd6ea91dSdamien 	uint8_t fbin;
2815bd6ea91dSdamien 	int i, lo, hi;
2816bd6ea91dSdamien 
28177a911050Sdamien 	/* Find interval (lower and upper indices). */
2818bd6ea91dSdamien 	fbin = athn_chan2fbin(c);
2819bd6ea91dSdamien 	for (i = 0; i < nchans; i++) {
2820bd6ea91dSdamien 		if (tgt[i].bChannel == AR_BCHAN_UNUSED ||
2821bd6ea91dSdamien 		    tgt[i].bChannel > fbin)
2822bd6ea91dSdamien 			break;
2823bd6ea91dSdamien 	}
2824bd6ea91dSdamien 	hi = i;
2825bd6ea91dSdamien 	lo = hi - 1;
2826bd6ea91dSdamien 	if (lo == -1)
2827bd6ea91dSdamien 		lo = hi;
2828bd6ea91dSdamien 	else if (hi == nchans || tgt[hi].bChannel == AR_BCHAN_UNUSED)
2829bd6ea91dSdamien 		hi = lo;
2830bd6ea91dSdamien 
2831bd6ea91dSdamien 	/* Interpolate values. */
2832bd6ea91dSdamien 	for (i = 0; i < 8; i++) {
2833bd6ea91dSdamien 		tpow[i] = athn_interpolate(fbin,
2834bd6ea91dSdamien 		    tgt[lo].bChannel, tgt[lo].tPow2x[i],
2835bd6ea91dSdamien 		    tgt[hi].bChannel, tgt[hi].tPow2x[i]);
2836bd6ea91dSdamien 	}
2837ffd25815Sdamien 	/* XXX Apply conformance testing limit. */
2838bd6ea91dSdamien }
2839bd6ea91dSdamien 
2840bd6ea91dSdamien /*
2841bd6ea91dSdamien  * Adaptive noise immunity.
2842bd6ea91dSdamien  */
2843bd6ea91dSdamien void
ar5008_set_noise_immunity_level(struct athn_softc * sc,int level)2844bd6ea91dSdamien ar5008_set_noise_immunity_level(struct athn_softc *sc, int level)
2845bd6ea91dSdamien {
2846bd6ea91dSdamien 	int high = level == 4;
2847bd6ea91dSdamien 	uint32_t reg;
2848bd6ea91dSdamien 
2849bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_DESIRED_SZ);
2850bd6ea91dSdamien 	reg = RW(reg, AR_PHY_DESIRED_SZ_TOT_DES, high ? -62 : -55);
2851bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_DESIRED_SZ, reg);
2852bd6ea91dSdamien 
2853bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_AGC_CTL1);
2854bd6ea91dSdamien 	reg = RW(reg, AR_PHY_AGC_CTL1_COARSE_LOW, high ? -70 : -64);
2855bd6ea91dSdamien 	reg = RW(reg, AR_PHY_AGC_CTL1_COARSE_HIGH, high ? -12 : -14);
2856bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_AGC_CTL1, reg);
2857bd6ea91dSdamien 
2858bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_FIND_SIG);
2859bd6ea91dSdamien 	reg = RW(reg, AR_PHY_FIND_SIG_FIRPWR, high ? -80 : -78);
2860bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_FIND_SIG, reg);
2861c0a11cf8Sdamien 
2862c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2863bd6ea91dSdamien }
2864bd6ea91dSdamien 
2865bd6ea91dSdamien void
ar5008_enable_ofdm_weak_signal(struct athn_softc * sc)2866bd6ea91dSdamien ar5008_enable_ofdm_weak_signal(struct athn_softc *sc)
2867bd6ea91dSdamien {
2868bd6ea91dSdamien 	uint32_t reg;
2869bd6ea91dSdamien 
2870bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_SFCORR_LOW);
2871bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_LOW_M1_THRESH_LOW, 50);
2872bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_LOW_M2_THRESH_LOW, 40);
2873bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, 48);
2874bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_SFCORR_LOW, reg);
2875bd6ea91dSdamien 
2876bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_SFCORR);
2877bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_M1_THRESH, 77);
2878bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_M2_THRESH, 64);
2879bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_M2COUNT_THR, 16);
2880bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_SFCORR, reg);
2881bd6ea91dSdamien 
2882bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_SFCORR_EXT);
2883bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M1_THRESH_LOW, 50);
2884bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M2_THRESH_LOW, 40);
2885bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M1_THRESH, 77);
2886bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M2_THRESH, 64);
2887bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_SFCORR_EXT, reg);
2888bd6ea91dSdamien 
2889bd6ea91dSdamien 	AR_SETBITS(sc, AR_PHY_SFCORR_LOW,
2890bd6ea91dSdamien 	    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
2891c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2892bd6ea91dSdamien }
2893bd6ea91dSdamien 
2894bd6ea91dSdamien void
ar5008_disable_ofdm_weak_signal(struct athn_softc * sc)2895bd6ea91dSdamien ar5008_disable_ofdm_weak_signal(struct athn_softc *sc)
2896bd6ea91dSdamien {
2897bd6ea91dSdamien 	uint32_t reg;
2898bd6ea91dSdamien 
2899bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_SFCORR_LOW);
2900bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_LOW_M1_THRESH_LOW, 127);
2901bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_LOW_M2_THRESH_LOW, 127);
2902bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, 63);
2903bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_SFCORR_LOW, reg);
2904bd6ea91dSdamien 
2905bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_SFCORR);
2906bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_M1_THRESH, 127);
2907bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_M2_THRESH, 127);
2908bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_M2COUNT_THR, 31);
2909bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_SFCORR, reg);
2910bd6ea91dSdamien 
2911bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_SFCORR_EXT);
2912bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M1_THRESH_LOW, 127);
2913bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M2_THRESH_LOW, 127);
2914bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M1_THRESH, 127);
2915bd6ea91dSdamien 	reg = RW(reg, AR_PHY_SFCORR_EXT_M2_THRESH, 127);
2916bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_SFCORR_EXT, reg);
2917bd6ea91dSdamien 
2918bd6ea91dSdamien 	AR_CLRBITS(sc, AR_PHY_SFCORR_LOW,
2919bd6ea91dSdamien 	    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
2920c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2921bd6ea91dSdamien }
2922bd6ea91dSdamien 
2923bd6ea91dSdamien void
ar5008_set_cck_weak_signal(struct athn_softc * sc,int high)2924bd6ea91dSdamien ar5008_set_cck_weak_signal(struct athn_softc *sc, int high)
2925bd6ea91dSdamien {
2926bd6ea91dSdamien 	uint32_t reg;
2927bd6ea91dSdamien 
2928bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_CCK_DETECT);
2929bd6ea91dSdamien 	reg = RW(reg, AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, high ? 6 : 8);
2930bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_CCK_DETECT, reg);
2931c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2932bd6ea91dSdamien }
2933bd6ea91dSdamien 
2934bd6ea91dSdamien void
ar5008_set_firstep_level(struct athn_softc * sc,int level)2935bd6ea91dSdamien ar5008_set_firstep_level(struct athn_softc *sc, int level)
2936bd6ea91dSdamien {
2937bd6ea91dSdamien 	uint32_t reg;
2938bd6ea91dSdamien 
2939bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_FIND_SIG);
2940bd6ea91dSdamien 	reg = RW(reg, AR_PHY_FIND_SIG_FIRSTEP, level * 4);
2941bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_FIND_SIG, reg);
2942c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2943bd6ea91dSdamien }
2944bd6ea91dSdamien 
2945bd6ea91dSdamien void
ar5008_set_spur_immunity_level(struct athn_softc * sc,int level)2946bd6ea91dSdamien ar5008_set_spur_immunity_level(struct athn_softc *sc, int level)
2947bd6ea91dSdamien {
2948bd6ea91dSdamien 	uint32_t reg;
2949bd6ea91dSdamien 
2950bd6ea91dSdamien 	reg = AR_READ(sc, AR_PHY_TIMING5);
2951bd6ea91dSdamien 	reg = RW(reg, AR_PHY_TIMING5_CYCPWR_THR1, (level + 1) * 2);
2952bd6ea91dSdamien 	AR_WRITE(sc, AR_PHY_TIMING5, reg);
2953c0a11cf8Sdamien 	AR_WRITE_BARRIER(sc);
2954bd6ea91dSdamien }
2955