xref: /freebsd-src/sys/dev/ath/ath_hal/ah.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
16e778a7eSPedro F. Giffuni /*-
26e778a7eSPedro F. Giffuni  * SPDX-License-Identifier: ISC
36e778a7eSPedro F. Giffuni  *
459efa8b5SSam Leffler  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
514779705SSam Leffler  * Copyright (c) 2002-2008 Atheros Communications, Inc.
614779705SSam Leffler  *
714779705SSam Leffler  * Permission to use, copy, modify, and/or distribute this software for any
814779705SSam Leffler  * purpose with or without fee is hereby granted, provided that the above
914779705SSam Leffler  * copyright notice and this permission notice appear in all copies.
1014779705SSam Leffler  *
1114779705SSam Leffler  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1214779705SSam Leffler  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1314779705SSam Leffler  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414779705SSam Leffler  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1514779705SSam Leffler  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1614779705SSam Leffler  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1714779705SSam Leffler  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1814779705SSam Leffler  */
1914779705SSam Leffler #include "opt_ah.h"
2014779705SSam Leffler 
2114779705SSam Leffler #include "ah.h"
2214779705SSam Leffler #include "ah_internal.h"
2314779705SSam Leffler #include "ah_devid.h"
2497efbf40SAdrian Chadd #include "ah_eeprom.h"			/* for 5ghz fast clock flag */
2514779705SSam Leffler 
26498657cfSSam Leffler #include "ar5416/ar5416reg.h"		/* NB: includes ar5212reg.h */
27a3611b59SAdrian Chadd #include "ar9003/ar9300_devid.h"
28498657cfSSam Leffler 
2914779705SSam Leffler /* linker set of registered chips */
3014779705SSam Leffler OS_SET_DECLARE(ah_chips, struct ath_hal_chip);
3141059135SAdrian Chadd TAILQ_HEAD(, ath_hal_chip) ah_chip_list = TAILQ_HEAD_INITIALIZER(ah_chip_list);
3241059135SAdrian Chadd 
3341059135SAdrian Chadd int
ath_hal_add_chip(struct ath_hal_chip * ahc)3441059135SAdrian Chadd ath_hal_add_chip(struct ath_hal_chip *ahc)
3541059135SAdrian Chadd {
3641059135SAdrian Chadd 
3741059135SAdrian Chadd 	TAILQ_INSERT_TAIL(&ah_chip_list, ahc, node);
3841059135SAdrian Chadd 	return (0);
3941059135SAdrian Chadd }
4041059135SAdrian Chadd 
4141059135SAdrian Chadd int
ath_hal_remove_chip(struct ath_hal_chip * ahc)4241059135SAdrian Chadd ath_hal_remove_chip(struct ath_hal_chip *ahc)
4341059135SAdrian Chadd {
4441059135SAdrian Chadd 
4541059135SAdrian Chadd 	TAILQ_REMOVE(&ah_chip_list, ahc, node);
4641059135SAdrian Chadd 	return (0);
4741059135SAdrian Chadd }
4814779705SSam Leffler 
4914779705SSam Leffler /*
5014779705SSam Leffler  * Check the set of registered chips to see if any recognize
5114779705SSam Leffler  * the device as one they can support.
5214779705SSam Leffler  */
5314779705SSam Leffler const char*
ath_hal_probe(uint16_t vendorid,uint16_t devid)5414779705SSam Leffler ath_hal_probe(uint16_t vendorid, uint16_t devid)
5514779705SSam Leffler {
56aed75e9dSSam Leffler 	struct ath_hal_chip * const *pchip;
5741059135SAdrian Chadd 	struct ath_hal_chip *pc;
5814779705SSam Leffler 
5941059135SAdrian Chadd 	/* Linker set */
6014779705SSam Leffler 	OS_SET_FOREACH(pchip, ah_chips) {
6114779705SSam Leffler 		const char *name = (*pchip)->probe(vendorid, devid);
6214779705SSam Leffler 		if (name != AH_NULL)
6314779705SSam Leffler 			return name;
6414779705SSam Leffler 	}
6541059135SAdrian Chadd 
6641059135SAdrian Chadd 	/* List */
6741059135SAdrian Chadd 	TAILQ_FOREACH(pc, &ah_chip_list, node) {
6841059135SAdrian Chadd 		const char *name = pc->probe(vendorid, devid);
6941059135SAdrian Chadd 		if (name != AH_NULL)
7041059135SAdrian Chadd 			return name;
7141059135SAdrian Chadd 	}
7241059135SAdrian Chadd 
7314779705SSam Leffler 	return AH_NULL;
7414779705SSam Leffler }
7514779705SSam Leffler 
7614779705SSam Leffler /*
7714779705SSam Leffler  * Attach detects device chip revisions, initializes the hwLayer
7814779705SSam Leffler  * function list, reads EEPROM information,
7914779705SSam Leffler  * selects reset vectors, and performs a short self test.
8014779705SSam Leffler  * Any failures will return an error that should cause a hardware
8114779705SSam Leffler  * disable.
8214779705SSam Leffler  */
8314779705SSam Leffler struct ath_hal*
ath_hal_attach(uint16_t devid,HAL_SOFTC sc,HAL_BUS_TAG st,HAL_BUS_HANDLE sh,uint16_t * eepromdata,HAL_OPS_CONFIG * ah_config,HAL_STATUS * error)8414779705SSam Leffler ath_hal_attach(uint16_t devid, HAL_SOFTC sc,
859389d5a9SAdrian Chadd 	HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata,
869389d5a9SAdrian Chadd 	HAL_OPS_CONFIG *ah_config,
879389d5a9SAdrian Chadd 	HAL_STATUS *error)
8814779705SSam Leffler {
89aed75e9dSSam Leffler 	struct ath_hal_chip * const *pchip;
9041059135SAdrian Chadd 	struct ath_hal_chip *pc;
9114779705SSam Leffler 
9214779705SSam Leffler 	OS_SET_FOREACH(pchip, ah_chips) {
9314779705SSam Leffler 		struct ath_hal_chip *chip = *pchip;
9414779705SSam Leffler 		struct ath_hal *ah;
9514779705SSam Leffler 
9614779705SSam Leffler 		/* XXX don't have vendorid, assume atheros one works */
9714779705SSam Leffler 		if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL)
9814779705SSam Leffler 			continue;
999389d5a9SAdrian Chadd 		ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config,
1009389d5a9SAdrian Chadd 		    error);
10114779705SSam Leffler 		if (ah != AH_NULL) {
10214779705SSam Leffler 			/* copy back private state to public area */
10314779705SSam Leffler 			ah->ah_devid = AH_PRIVATE(ah)->ah_devid;
10414779705SSam Leffler 			ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid;
10514779705SSam Leffler 			ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion;
10614779705SSam Leffler 			ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev;
10714779705SSam Leffler 			ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev;
10814779705SSam Leffler 			ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev;
10914779705SSam Leffler 			ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev;
11014779705SSam Leffler 			return ah;
11114779705SSam Leffler 		}
11214779705SSam Leffler 	}
11341059135SAdrian Chadd 
11441059135SAdrian Chadd 	/* List */
11541059135SAdrian Chadd 	TAILQ_FOREACH(pc, &ah_chip_list, node) {
11641059135SAdrian Chadd 		struct ath_hal_chip *chip = pc;
11741059135SAdrian Chadd 		struct ath_hal *ah;
11841059135SAdrian Chadd 
11941059135SAdrian Chadd 		/* XXX don't have vendorid, assume atheros one works */
12041059135SAdrian Chadd 		if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL)
12141059135SAdrian Chadd 			continue;
12241059135SAdrian Chadd 		ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config,
12341059135SAdrian Chadd 		    error);
12441059135SAdrian Chadd 		if (ah != AH_NULL) {
12541059135SAdrian Chadd 			/* copy back private state to public area */
12641059135SAdrian Chadd 			ah->ah_devid = AH_PRIVATE(ah)->ah_devid;
12741059135SAdrian Chadd 			ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid;
12841059135SAdrian Chadd 			ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion;
12941059135SAdrian Chadd 			ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev;
13041059135SAdrian Chadd 			ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev;
13141059135SAdrian Chadd 			ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev;
13241059135SAdrian Chadd 			ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev;
13341059135SAdrian Chadd 			return ah;
13441059135SAdrian Chadd 		}
13541059135SAdrian Chadd 	}
13641059135SAdrian Chadd 
13714779705SSam Leffler 	return AH_NULL;
13814779705SSam Leffler }
13914779705SSam Leffler 
140498657cfSSam Leffler const char *
ath_hal_mac_name(struct ath_hal * ah)141498657cfSSam Leffler ath_hal_mac_name(struct ath_hal *ah)
142498657cfSSam Leffler {
143498657cfSSam Leffler 	switch (ah->ah_macVersion) {
144498657cfSSam Leffler 	case AR_SREV_VERSION_CRETE:
145498657cfSSam Leffler 	case AR_SREV_VERSION_MAUI_1:
146b2585567SAdrian Chadd 		return "AR5210";
147498657cfSSam Leffler 	case AR_SREV_VERSION_MAUI_2:
148498657cfSSam Leffler 	case AR_SREV_VERSION_OAHU:
149b2585567SAdrian Chadd 		return "AR5211";
150498657cfSSam Leffler 	case AR_SREV_VERSION_VENICE:
151b2585567SAdrian Chadd 		return "AR5212";
152498657cfSSam Leffler 	case AR_SREV_VERSION_GRIFFIN:
153b2585567SAdrian Chadd 		return "AR2413";
154498657cfSSam Leffler 	case AR_SREV_VERSION_CONDOR:
155b2585567SAdrian Chadd 		return "AR5424";
156498657cfSSam Leffler 	case AR_SREV_VERSION_EAGLE:
157b2585567SAdrian Chadd 		return "AR5413";
158498657cfSSam Leffler 	case AR_SREV_VERSION_COBRA:
159b2585567SAdrian Chadd 		return "AR2415";
160168c1a30SAdrian Chadd 	case AR_SREV_2425:	/* Swan */
161b2585567SAdrian Chadd 		return "AR2425";
162168c1a30SAdrian Chadd 	case AR_SREV_2417:	/* Nala */
163b2585567SAdrian Chadd 		return "AR2417";
164498657cfSSam Leffler 	case AR_XSREV_VERSION_OWL_PCI:
165b2585567SAdrian Chadd 		return "AR5416";
166498657cfSSam Leffler 	case AR_XSREV_VERSION_OWL_PCIE:
167b2585567SAdrian Chadd 		return "AR5418";
1689f25ad52SAdrian Chadd 	case AR_XSREV_VERSION_HOWL:
169b2585567SAdrian Chadd 		return "AR9130";
170498657cfSSam Leffler 	case AR_XSREV_VERSION_SOWL:
171b2585567SAdrian Chadd 		return "AR9160";
172498657cfSSam Leffler 	case AR_XSREV_VERSION_MERLIN:
1735b77f8e9SAdrian Chadd 		if (AH_PRIVATE(ah)->ah_ispcie)
174b2585567SAdrian Chadd 			return "AR9280";
175b2585567SAdrian Chadd 		return "AR9220";
176498657cfSSam Leffler 	case AR_XSREV_VERSION_KITE:
177b2585567SAdrian Chadd 		return "AR9285";
17890759dbeSAdrian Chadd 	case AR_XSREV_VERSION_KIWI:
1795b77f8e9SAdrian Chadd 		if (AH_PRIVATE(ah)->ah_ispcie)
180b2585567SAdrian Chadd 			return "AR9287";
181b2585567SAdrian Chadd 		return "AR9227";
182a3611b59SAdrian Chadd 	case AR_SREV_VERSION_AR9380:
183a3611b59SAdrian Chadd 		if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10)
184b2585567SAdrian Chadd 			return "AR9580";
185b2585567SAdrian Chadd 		return "AR9380";
186a3611b59SAdrian Chadd 	case AR_SREV_VERSION_AR9460:
187b2585567SAdrian Chadd 		return "AR9460";
188a3611b59SAdrian Chadd 	case AR_SREV_VERSION_AR9330:
189b2585567SAdrian Chadd 		return "AR9330";
190a3611b59SAdrian Chadd 	case AR_SREV_VERSION_AR9340:
191b2585567SAdrian Chadd 		return "AR9340";
192a3611b59SAdrian Chadd 	case AR_SREV_VERSION_QCA9550:
193b2585567SAdrian Chadd 		return "QCA9550";
194a3611b59SAdrian Chadd 	case AR_SREV_VERSION_AR9485:
195b2585567SAdrian Chadd 		return "AR9485";
1962f544eedSAdrian Chadd 	case AR_SREV_VERSION_QCA9565:
197b2585567SAdrian Chadd 		return "QCA9565";
1982c9b30a9SAdrian Chadd 	case AR_SREV_VERSION_QCA9530:
199b2585567SAdrian Chadd 		return "QCA9530";
200498657cfSSam Leffler 	}
201498657cfSSam Leffler 	return "????";
202498657cfSSam Leffler }
203498657cfSSam Leffler 
20459efa8b5SSam Leffler /*
20559efa8b5SSam Leffler  * Return the mask of available modes based on the hardware capabilities.
20659efa8b5SSam Leffler  */
20759efa8b5SSam Leffler u_int
ath_hal_getwirelessmodes(struct ath_hal * ah)20859efa8b5SSam Leffler ath_hal_getwirelessmodes(struct ath_hal*ah)
20959efa8b5SSam Leffler {
21059efa8b5SSam Leffler 	return ath_hal_getWirelessModes(ah);
21159efa8b5SSam Leffler }
21259efa8b5SSam Leffler 
21314779705SSam Leffler /* linker set of registered RF backends */
21414779705SSam Leffler OS_SET_DECLARE(ah_rfs, struct ath_hal_rf);
21541059135SAdrian Chadd TAILQ_HEAD(, ath_hal_rf) ah_rf_list = TAILQ_HEAD_INITIALIZER(ah_rf_list);
21641059135SAdrian Chadd 
21741059135SAdrian Chadd int
ath_hal_add_rf(struct ath_hal_rf * arf)21841059135SAdrian Chadd ath_hal_add_rf(struct ath_hal_rf *arf)
21941059135SAdrian Chadd {
22041059135SAdrian Chadd 
22141059135SAdrian Chadd 	TAILQ_INSERT_TAIL(&ah_rf_list, arf, node);
22241059135SAdrian Chadd 	return (0);
22341059135SAdrian Chadd }
22441059135SAdrian Chadd 
22541059135SAdrian Chadd int
ath_hal_remove_rf(struct ath_hal_rf * arf)22641059135SAdrian Chadd ath_hal_remove_rf(struct ath_hal_rf *arf)
22741059135SAdrian Chadd {
22841059135SAdrian Chadd 
22941059135SAdrian Chadd 	TAILQ_REMOVE(&ah_rf_list, arf, node);
23041059135SAdrian Chadd 	return (0);
23141059135SAdrian Chadd }
23214779705SSam Leffler 
23314779705SSam Leffler /*
23414779705SSam Leffler  * Check the set of registered RF backends to see if
23514779705SSam Leffler  * any recognize the device as one they can support.
23614779705SSam Leffler  */
23714779705SSam Leffler struct ath_hal_rf *
ath_hal_rfprobe(struct ath_hal * ah,HAL_STATUS * ecode)23814779705SSam Leffler ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode)
23914779705SSam Leffler {
240aed75e9dSSam Leffler 	struct ath_hal_rf * const *prf;
24141059135SAdrian Chadd 	struct ath_hal_rf * rf;
24214779705SSam Leffler 
24314779705SSam Leffler 	OS_SET_FOREACH(prf, ah_rfs) {
24414779705SSam Leffler 		struct ath_hal_rf *rf = *prf;
24514779705SSam Leffler 		if (rf->probe(ah))
24614779705SSam Leffler 			return rf;
24714779705SSam Leffler 	}
24841059135SAdrian Chadd 
24941059135SAdrian Chadd 	TAILQ_FOREACH(rf, &ah_rf_list, node) {
25041059135SAdrian Chadd 		if (rf->probe(ah))
25141059135SAdrian Chadd 			return rf;
25241059135SAdrian Chadd 	}
25314779705SSam Leffler 	*ecode = HAL_ENOTSUPP;
25414779705SSam Leffler 	return AH_NULL;
25514779705SSam Leffler }
25614779705SSam Leffler 
257498657cfSSam Leffler const char *
ath_hal_rf_name(struct ath_hal * ah)258498657cfSSam Leffler ath_hal_rf_name(struct ath_hal *ah)
259498657cfSSam Leffler {
260498657cfSSam Leffler 	switch (ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) {
261498657cfSSam Leffler 	case 0:			/* 5210 */
262498657cfSSam Leffler 		return "5110";	/* NB: made up */
263498657cfSSam Leffler 	case AR_RAD5111_SREV_MAJOR:
264498657cfSSam Leffler 	case AR_RAD5111_SREV_PROD:
265498657cfSSam Leffler 		return "5111";
266498657cfSSam Leffler 	case AR_RAD2111_SREV_MAJOR:
267498657cfSSam Leffler 		return "2111";
268498657cfSSam Leffler 	case AR_RAD5112_SREV_MAJOR:
269498657cfSSam Leffler 	case AR_RAD5112_SREV_2_0:
270498657cfSSam Leffler 	case AR_RAD5112_SREV_2_1:
271498657cfSSam Leffler 		return "5112";
272498657cfSSam Leffler 	case AR_RAD2112_SREV_MAJOR:
273498657cfSSam Leffler 	case AR_RAD2112_SREV_2_0:
274498657cfSSam Leffler 	case AR_RAD2112_SREV_2_1:
275498657cfSSam Leffler 		return "2112";
276498657cfSSam Leffler 	case AR_RAD2413_SREV_MAJOR:
277498657cfSSam Leffler 		return "2413";
278498657cfSSam Leffler 	case AR_RAD5413_SREV_MAJOR:
279498657cfSSam Leffler 		return "5413";
280498657cfSSam Leffler 	case AR_RAD2316_SREV_MAJOR:
281498657cfSSam Leffler 		return "2316";
282498657cfSSam Leffler 	case AR_RAD2317_SREV_MAJOR:
283498657cfSSam Leffler 		return "2317";
284498657cfSSam Leffler 	case AR_RAD5424_SREV_MAJOR:
285498657cfSSam Leffler 		return "5424";
286498657cfSSam Leffler 
287498657cfSSam Leffler 	case AR_RAD5133_SREV_MAJOR:
288498657cfSSam Leffler 		return "5133";
289498657cfSSam Leffler 	case AR_RAD2133_SREV_MAJOR:
290498657cfSSam Leffler 		return "2133";
291498657cfSSam Leffler 	case AR_RAD5122_SREV_MAJOR:
292498657cfSSam Leffler 		return "5122";
293498657cfSSam Leffler 	case AR_RAD2122_SREV_MAJOR:
294498657cfSSam Leffler 		return "2122";
295498657cfSSam Leffler 	}
296498657cfSSam Leffler 	return "????";
297498657cfSSam Leffler }
298498657cfSSam Leffler 
29914779705SSam Leffler /*
30014779705SSam Leffler  * Poll the register looking for a specific value.
30114779705SSam Leffler  */
30214779705SSam Leffler HAL_BOOL
ath_hal_wait(struct ath_hal * ah,u_int reg,uint32_t mask,uint32_t val)30314779705SSam Leffler ath_hal_wait(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val)
30414779705SSam Leffler {
30571083394SWojciech Macek #define	AH_TIMEOUT	5000
3067fbdd9a0SAdrian Chadd 	return ath_hal_waitfor(ah, reg, mask, val, AH_TIMEOUT);
3077fbdd9a0SAdrian Chadd #undef AH_TIMEOUT
3087fbdd9a0SAdrian Chadd }
3097fbdd9a0SAdrian Chadd 
3107fbdd9a0SAdrian Chadd HAL_BOOL
ath_hal_waitfor(struct ath_hal * ah,u_int reg,uint32_t mask,uint32_t val,uint32_t timeout)3117fbdd9a0SAdrian Chadd ath_hal_waitfor(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout)
3127fbdd9a0SAdrian Chadd {
31314779705SSam Leffler 	int i;
31414779705SSam Leffler 
3157fbdd9a0SAdrian Chadd 	for (i = 0; i < timeout; i++) {
31614779705SSam Leffler 		if ((OS_REG_READ(ah, reg) & mask) == val)
31714779705SSam Leffler 			return AH_TRUE;
31814779705SSam Leffler 		OS_DELAY(10);
31914779705SSam Leffler 	}
32014779705SSam Leffler 	HALDEBUG(ah, HAL_DEBUG_REGIO | HAL_DEBUG_PHYIO,
32114779705SSam Leffler 	    "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
32214779705SSam Leffler 	    __func__, reg, OS_REG_READ(ah, reg), mask, val);
32314779705SSam Leffler 	return AH_FALSE;
32414779705SSam Leffler }
32514779705SSam Leffler 
32614779705SSam Leffler /*
32714779705SSam Leffler  * Reverse the bits starting at the low bit for a value of
32814779705SSam Leffler  * bit_count in size
32914779705SSam Leffler  */
33014779705SSam Leffler uint32_t
ath_hal_reverseBits(uint32_t val,uint32_t n)33114779705SSam Leffler ath_hal_reverseBits(uint32_t val, uint32_t n)
33214779705SSam Leffler {
33314779705SSam Leffler 	uint32_t retval;
33414779705SSam Leffler 	int i;
33514779705SSam Leffler 
33614779705SSam Leffler 	for (i = 0, retval = 0; i < n; i++) {
33714779705SSam Leffler 		retval = (retval << 1) | (val & 1);
33814779705SSam Leffler 		val >>= 1;
33914779705SSam Leffler 	}
34014779705SSam Leffler 	return retval;
34114779705SSam Leffler }
34214779705SSam Leffler 
343d054f3a8SAdrian Chadd /* 802.11n related timing definitions */
344d054f3a8SAdrian Chadd 
345d054f3a8SAdrian Chadd #define	OFDM_PLCP_BITS	22
346d054f3a8SAdrian Chadd #define	HT_L_STF	8
347d054f3a8SAdrian Chadd #define	HT_L_LTF	8
348d054f3a8SAdrian Chadd #define	HT_L_SIG	4
349d054f3a8SAdrian Chadd #define	HT_SIG		8
350d054f3a8SAdrian Chadd #define	HT_STF		4
351d054f3a8SAdrian Chadd #define	HT_LTF(n)	((n) * 4)
352d054f3a8SAdrian Chadd 
35390d3a30aSAdrian Chadd #define	HT_RC_2_MCS(_rc)	((_rc) & 0x1f)
354d054f3a8SAdrian Chadd #define	HT_RC_2_STREAMS(_rc)	((((_rc) & 0x78) >> 3) + 1)
355d054f3a8SAdrian Chadd #define	IS_HT_RATE(_rc)		( (_rc) & IEEE80211_RATE_MCS)
356d054f3a8SAdrian Chadd 
357d054f3a8SAdrian Chadd /*
358d054f3a8SAdrian Chadd  * Calculate the duration of a packet whether it is 11n or legacy.
359d054f3a8SAdrian Chadd  */
360d054f3a8SAdrian Chadd uint32_t
ath_hal_pkt_txtime(struct ath_hal * ah,const HAL_RATE_TABLE * rates,uint32_t frameLen,uint16_t rateix,HAL_BOOL isht40,HAL_BOOL shortPreamble,HAL_BOOL includeSifs)361d054f3a8SAdrian Chadd ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen,
3627ff1939dSAdrian Chadd     uint16_t rateix, HAL_BOOL isht40, HAL_BOOL shortPreamble,
3637ff1939dSAdrian Chadd     HAL_BOOL includeSifs)
364d054f3a8SAdrian Chadd {
365d054f3a8SAdrian Chadd 	uint8_t rc;
366d054f3a8SAdrian Chadd 	int numStreams;
367d054f3a8SAdrian Chadd 
368d054f3a8SAdrian Chadd 	rc = rates->info[rateix].rateCode;
369d054f3a8SAdrian Chadd 
370d054f3a8SAdrian Chadd 	/* Legacy rate? Return the old way */
371d054f3a8SAdrian Chadd 	if (! IS_HT_RATE(rc))
3727ff1939dSAdrian Chadd 		return ath_hal_computetxtime(ah, rates, frameLen, rateix,
3737ff1939dSAdrian Chadd 		    shortPreamble, includeSifs);
374d054f3a8SAdrian Chadd 
375d054f3a8SAdrian Chadd 	/* 11n frame - extract out the number of spatial streams */
376d054f3a8SAdrian Chadd 	numStreams = HT_RC_2_STREAMS(rc);
3776c03eb4cSAdrian Chadd 	KASSERT(numStreams > 0 && numStreams <= 4,
3786c03eb4cSAdrian Chadd 	    ("number of spatial streams needs to be 1..3: MCS rate 0x%x!",
3796c03eb4cSAdrian Chadd 	    rateix));
380d054f3a8SAdrian Chadd 
3817ff1939dSAdrian Chadd 	/* XXX TODO: Add SIFS */
3827ff1939dSAdrian Chadd 	return ath_computedur_ht(frameLen, rc, numStreams, isht40,
3837ff1939dSAdrian Chadd 	    shortPreamble);
384d054f3a8SAdrian Chadd }
385d054f3a8SAdrian Chadd 
3866c03eb4cSAdrian Chadd static const uint16_t ht20_bps[32] = {
3876c03eb4cSAdrian Chadd     26, 52, 78, 104, 156, 208, 234, 260,
3886c03eb4cSAdrian Chadd     52, 104, 156, 208, 312, 416, 468, 520,
3896c03eb4cSAdrian Chadd     78, 156, 234, 312, 468, 624, 702, 780,
3906c03eb4cSAdrian Chadd     104, 208, 312, 416, 624, 832, 936, 1040
3916c03eb4cSAdrian Chadd };
3926c03eb4cSAdrian Chadd static const uint16_t ht40_bps[32] = {
3936c03eb4cSAdrian Chadd     54, 108, 162, 216, 324, 432, 486, 540,
3946c03eb4cSAdrian Chadd     108, 216, 324, 432, 648, 864, 972, 1080,
3956c03eb4cSAdrian Chadd     162, 324, 486, 648, 972, 1296, 1458, 1620,
3966c03eb4cSAdrian Chadd     216, 432, 648, 864, 1296, 1728, 1944, 2160
3976c03eb4cSAdrian Chadd };
3986c03eb4cSAdrian Chadd 
399d054f3a8SAdrian Chadd /*
400d054f3a8SAdrian Chadd  * Calculate the transmit duration of an 11n frame.
401d054f3a8SAdrian Chadd  */
402d054f3a8SAdrian Chadd uint32_t
ath_computedur_ht(uint32_t frameLen,uint16_t rate,int streams,HAL_BOOL isht40,HAL_BOOL isShortGI)4036c03eb4cSAdrian Chadd ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams,
4046c03eb4cSAdrian Chadd     HAL_BOOL isht40, HAL_BOOL isShortGI)
405d054f3a8SAdrian Chadd {
406d054f3a8SAdrian Chadd 	uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
407d054f3a8SAdrian Chadd 
408d054f3a8SAdrian Chadd 	KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
4096c03eb4cSAdrian Chadd 	KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
410d054f3a8SAdrian Chadd 
411d054f3a8SAdrian Chadd 	if (isht40)
41290d3a30aSAdrian Chadd 		bitsPerSymbol = ht40_bps[HT_RC_2_MCS(rate)];
413d054f3a8SAdrian Chadd 	else
41490d3a30aSAdrian Chadd 		bitsPerSymbol = ht20_bps[HT_RC_2_MCS(rate)];
415d054f3a8SAdrian Chadd 	numBits = OFDM_PLCP_BITS + (frameLen << 3);
416d054f3a8SAdrian Chadd 	numSymbols = howmany(numBits, bitsPerSymbol);
417d054f3a8SAdrian Chadd 	if (isShortGI)
418d054f3a8SAdrian Chadd 		txTime = ((numSymbols * 18) + 4) / 5;   /* 3.6us */
419d054f3a8SAdrian Chadd 	else
420d054f3a8SAdrian Chadd 		txTime = numSymbols * 4;                /* 4us */
421d054f3a8SAdrian Chadd 	return txTime + HT_L_STF + HT_L_LTF +
422d054f3a8SAdrian Chadd 	    HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
423d054f3a8SAdrian Chadd }
424d054f3a8SAdrian Chadd 
42514779705SSam Leffler /*
42614779705SSam Leffler  * Compute the time to transmit a frame of length frameLen bytes
42714779705SSam Leffler  * using the specified rate, phy, and short preamble setting.
42814779705SSam Leffler  */
42914779705SSam Leffler uint16_t
ath_hal_computetxtime(struct ath_hal * ah,const HAL_RATE_TABLE * rates,uint32_t frameLen,uint16_t rateix,HAL_BOOL shortPreamble,HAL_BOOL includeSifs)43014779705SSam Leffler ath_hal_computetxtime(struct ath_hal *ah,
43114779705SSam Leffler 	const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix,
4327ff1939dSAdrian Chadd 	HAL_BOOL shortPreamble, HAL_BOOL includeSifs)
43314779705SSam Leffler {
43414779705SSam Leffler 	uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
43514779705SSam Leffler 	uint32_t kbps;
43614779705SSam Leffler 
4374a02016dSAdrian Chadd 	/* Warn if this function is called for 11n rates; it should not be! */
4384a02016dSAdrian Chadd 	if (IS_HT_RATE(rates->info[rateix].rateCode))
4394a02016dSAdrian Chadd 		ath_hal_printf(ah, "%s: MCS rate? (index %d; hwrate 0x%x)\n",
4404a02016dSAdrian Chadd 		    __func__, rateix, rates->info[rateix].rateCode);
4414a02016dSAdrian Chadd 
44214779705SSam Leffler 	kbps = rates->info[rateix].rateKbps;
44314779705SSam Leffler 	/*
444f6b6084bSPedro F. Giffuni 	 * index can be invalid during dynamic Turbo transitions.
44559efa8b5SSam Leffler 	 * XXX
44614779705SSam Leffler 	 */
44759efa8b5SSam Leffler 	if (kbps == 0)
44859efa8b5SSam Leffler 		return 0;
44914779705SSam Leffler 	switch (rates->info[rateix].phy) {
45014779705SSam Leffler 	case IEEE80211_T_CCK:
45114779705SSam Leffler 		phyTime		= CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
45214779705SSam Leffler 		if (shortPreamble && rates->info[rateix].shortPreamble)
45314779705SSam Leffler 			phyTime >>= 1;
45414779705SSam Leffler 		numBits		= frameLen << 3;
4557ff1939dSAdrian Chadd 		txTime		= phyTime
45614779705SSam Leffler 				+ ((numBits * 1000)/kbps);
4577ff1939dSAdrian Chadd 		if (includeSifs)
4587ff1939dSAdrian Chadd 			txTime	+= CCK_SIFS_TIME;
45914779705SSam Leffler 		break;
46014779705SSam Leffler 	case IEEE80211_T_OFDM:
46114779705SSam Leffler 		bitsPerSymbol	= (kbps * OFDM_SYMBOL_TIME) / 1000;
46214779705SSam Leffler 		HALASSERT(bitsPerSymbol != 0);
46314779705SSam Leffler 
46414779705SSam Leffler 		numBits		= OFDM_PLCP_BITS + (frameLen << 3);
46514779705SSam Leffler 		numSymbols	= howmany(numBits, bitsPerSymbol);
4667ff1939dSAdrian Chadd 		txTime		= OFDM_PREAMBLE_TIME
46714779705SSam Leffler 				+ (numSymbols * OFDM_SYMBOL_TIME);
4687ff1939dSAdrian Chadd 		if (includeSifs)
4697ff1939dSAdrian Chadd 			txTime	+= OFDM_SIFS_TIME;
47014779705SSam Leffler 		break;
47117e45e19SSam Leffler 	case IEEE80211_T_OFDM_HALF:
47217e45e19SSam Leffler 		bitsPerSymbol	= (kbps * OFDM_HALF_SYMBOL_TIME) / 1000;
47317e45e19SSam Leffler 		HALASSERT(bitsPerSymbol != 0);
47414779705SSam Leffler 
47517e45e19SSam Leffler 		numBits		= OFDM_HALF_PLCP_BITS + (frameLen << 3);
47617e45e19SSam Leffler 		numSymbols	= howmany(numBits, bitsPerSymbol);
4777ff1939dSAdrian Chadd 		txTime		= OFDM_HALF_PREAMBLE_TIME
47817e45e19SSam Leffler 				+ (numSymbols * OFDM_HALF_SYMBOL_TIME);
4797ff1939dSAdrian Chadd 		if (includeSifs)
4807ff1939dSAdrian Chadd 			txTime	+= OFDM_HALF_SIFS_TIME;
48117e45e19SSam Leffler 		break;
48217e45e19SSam Leffler 	case IEEE80211_T_OFDM_QUARTER:
48317e45e19SSam Leffler 		bitsPerSymbol	= (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000;
48417e45e19SSam Leffler 		HALASSERT(bitsPerSymbol != 0);
48514779705SSam Leffler 
48617e45e19SSam Leffler 		numBits		= OFDM_QUARTER_PLCP_BITS + (frameLen << 3);
48717e45e19SSam Leffler 		numSymbols	= howmany(numBits, bitsPerSymbol);
4887ff1939dSAdrian Chadd 		txTime		= OFDM_QUARTER_PREAMBLE_TIME
48917e45e19SSam Leffler 				+ (numSymbols * OFDM_QUARTER_SYMBOL_TIME);
4907ff1939dSAdrian Chadd 		if (includeSifs)
4917ff1939dSAdrian Chadd 			txTime	+= OFDM_QUARTER_SIFS_TIME;
49217e45e19SSam Leffler 		break;
49314779705SSam Leffler 	case IEEE80211_T_TURBO:
49419ea87c4SSam Leffler 		bitsPerSymbol	= (kbps * TURBO_SYMBOL_TIME) / 1000;
49514779705SSam Leffler 		HALASSERT(bitsPerSymbol != 0);
49614779705SSam Leffler 
49714779705SSam Leffler 		numBits		= TURBO_PLCP_BITS + (frameLen << 3);
49814779705SSam Leffler 		numSymbols	= howmany(numBits, bitsPerSymbol);
4997ff1939dSAdrian Chadd 		txTime		= TURBO_PREAMBLE_TIME
50014779705SSam Leffler 				+ (numSymbols * TURBO_SYMBOL_TIME);
5017ff1939dSAdrian Chadd 		if (includeSifs)
5027ff1939dSAdrian Chadd 			txTime	+= TURBO_SIFS_TIME;
50314779705SSam Leffler 		break;
50414779705SSam Leffler 	default:
50514779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_PHYIO,
50614779705SSam Leffler 		    "%s: unknown phy %u (rate ix %u)\n",
50714779705SSam Leffler 		    __func__, rates->info[rateix].phy, rateix);
50814779705SSam Leffler 		txTime = 0;
50914779705SSam Leffler 		break;
51014779705SSam Leffler 	}
51114779705SSam Leffler 	return txTime;
51214779705SSam Leffler }
51314779705SSam Leffler 
51485ca341aSAdrian Chadd int
ath_hal_get_curmode(struct ath_hal * ah,const struct ieee80211_channel * chan)51585ca341aSAdrian Chadd ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan)
51685ca341aSAdrian Chadd {
51785ca341aSAdrian Chadd 	/*
51885ca341aSAdrian Chadd 	 * Pick a default mode at bootup. A channel change is inevitable.
51985ca341aSAdrian Chadd 	 */
52085ca341aSAdrian Chadd 	if (!chan)
52185ca341aSAdrian Chadd 		return HAL_MODE_11NG_HT20;
52285ca341aSAdrian Chadd 
52385ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_TURBO(chan))
52485ca341aSAdrian Chadd 		return HAL_MODE_TURBO;
52585ca341aSAdrian Chadd 
52685ca341aSAdrian Chadd 	/* check for NA_HT before plain A, since IS_CHAN_A includes NA_HT */
52785ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
52885ca341aSAdrian Chadd 		return HAL_MODE_11NA_HT20;
52985ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
53085ca341aSAdrian Chadd 		return HAL_MODE_11NA_HT40PLUS;
53185ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
53285ca341aSAdrian Chadd 		return HAL_MODE_11NA_HT40MINUS;
53385ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_A(chan))
53485ca341aSAdrian Chadd 		return HAL_MODE_11A;
53585ca341aSAdrian Chadd 
53685ca341aSAdrian Chadd 	/* check for NG_HT before plain G, since IS_CHAN_G includes NG_HT */
53785ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
53885ca341aSAdrian Chadd 		return HAL_MODE_11NG_HT20;
53985ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
54085ca341aSAdrian Chadd 		return HAL_MODE_11NG_HT40PLUS;
54185ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
54285ca341aSAdrian Chadd 		return HAL_MODE_11NG_HT40MINUS;
54385ca341aSAdrian Chadd 
54485ca341aSAdrian Chadd 	/*
54585ca341aSAdrian Chadd 	 * XXX For FreeBSD, will this work correctly given the DYN
54685ca341aSAdrian Chadd 	 * chan mode (OFDM+CCK dynamic) ? We have pure-G versions DYN-BG..
54785ca341aSAdrian Chadd 	 */
54885ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_G(chan))
54985ca341aSAdrian Chadd 		return HAL_MODE_11G;
55085ca341aSAdrian Chadd 	if (IEEE80211_IS_CHAN_B(chan))
55185ca341aSAdrian Chadd 		return HAL_MODE_11B;
55285ca341aSAdrian Chadd 
55385ca341aSAdrian Chadd 	HALASSERT(0);
55485ca341aSAdrian Chadd 	return HAL_MODE_11NG_HT20;
55585ca341aSAdrian Chadd }
55685ca341aSAdrian Chadd 
55714779705SSam Leffler typedef enum {
55814779705SSam Leffler 	WIRELESS_MODE_11a   = 0,
55914779705SSam Leffler 	WIRELESS_MODE_TURBO = 1,
56014779705SSam Leffler 	WIRELESS_MODE_11b   = 2,
56114779705SSam Leffler 	WIRELESS_MODE_11g   = 3,
56214779705SSam Leffler 	WIRELESS_MODE_108g  = 4,
56314779705SSam Leffler 
56414779705SSam Leffler 	WIRELESS_MODE_MAX
56514779705SSam Leffler } WIRELESS_MODE;
56614779705SSam Leffler 
56790d3a30aSAdrian Chadd /*
56890d3a30aSAdrian Chadd  * XXX TODO: for some (?) chips, an 11b mode still runs at 11bg.
56990d3a30aSAdrian Chadd  * Maybe AR5211 has separate 11b and 11g only modes, so 11b is 22MHz
57090d3a30aSAdrian Chadd  * and 11g is 44MHz, but AR5416 and later run 11b in 11bg mode, right?
57190d3a30aSAdrian Chadd  */
57214779705SSam Leffler static WIRELESS_MODE
ath_hal_chan2wmode(struct ath_hal * ah,const struct ieee80211_channel * chan)57359efa8b5SSam Leffler ath_hal_chan2wmode(struct ath_hal *ah, const struct ieee80211_channel *chan)
57414779705SSam Leffler {
57559efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_B(chan))
57614779705SSam Leffler 		return WIRELESS_MODE_11b;
57759efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_G(chan))
57814779705SSam Leffler 		return WIRELESS_MODE_11g;
57959efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_108G(chan))
58014779705SSam Leffler 		return WIRELESS_MODE_108g;
58159efa8b5SSam Leffler 	if (IEEE80211_IS_CHAN_TURBO(chan))
58214779705SSam Leffler 		return WIRELESS_MODE_TURBO;
58314779705SSam Leffler 	return WIRELESS_MODE_11a;
58414779705SSam Leffler }
58514779705SSam Leffler 
58614779705SSam Leffler /*
58714779705SSam Leffler  * Convert between microseconds and core system clocks.
58814779705SSam Leffler  */
58914779705SSam Leffler                                      /* 11a Turbo  11b  11g  108g */
59014779705SSam Leffler static const uint8_t CLOCK_RATE[]  = { 40,  80,   22,  44,   88  };
59114779705SSam Leffler 
59297efbf40SAdrian Chadd #define	CLOCK_FAST_RATE_5GHZ_OFDM	44
59397efbf40SAdrian Chadd 
59414779705SSam Leffler u_int
ath_hal_mac_clks(struct ath_hal * ah,u_int usecs)59514779705SSam Leffler ath_hal_mac_clks(struct ath_hal *ah, u_int usecs)
59614779705SSam Leffler {
59759efa8b5SSam Leffler 	const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan;
59814779705SSam Leffler 	u_int clks;
59914779705SSam Leffler 
60014779705SSam Leffler 	/* NB: ah_curchan may be null when called attach time */
60197efbf40SAdrian Chadd 	/* XXX merlin and later specific workaround - 5ghz fast clock is 44 */
60297efbf40SAdrian Chadd 	if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) {
60397efbf40SAdrian Chadd 		clks = usecs * CLOCK_FAST_RATE_5GHZ_OFDM;
60497efbf40SAdrian Chadd 		if (IEEE80211_IS_CHAN_HT40(c))
60597efbf40SAdrian Chadd 			clks <<= 1;
60697efbf40SAdrian Chadd 	} else if (c != AH_NULL) {
60714779705SSam Leffler 		clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
60859efa8b5SSam Leffler 		if (IEEE80211_IS_CHAN_HT40(c))
60914779705SSam Leffler 			clks <<= 1;
61014779705SSam Leffler 	} else
61114779705SSam Leffler 		clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b];
6121690edb3SAdrian Chadd 
6131690edb3SAdrian Chadd 	/* Compensate for half/quarter rate */
6141690edb3SAdrian Chadd 	if (c != AH_NULL && IEEE80211_IS_CHAN_HALF(c))
6151690edb3SAdrian Chadd 		clks = clks / 2;
6161690edb3SAdrian Chadd 	else if (c != AH_NULL && IEEE80211_IS_CHAN_QUARTER(c))
6171690edb3SAdrian Chadd 		clks = clks / 4;
6181690edb3SAdrian Chadd 
61914779705SSam Leffler 	return clks;
62014779705SSam Leffler }
62114779705SSam Leffler 
62214779705SSam Leffler u_int
ath_hal_mac_usec(struct ath_hal * ah,u_int clks)62314779705SSam Leffler ath_hal_mac_usec(struct ath_hal *ah, u_int clks)
62414779705SSam Leffler {
62590d3a30aSAdrian Chadd 	uint64_t psec;
62690d3a30aSAdrian Chadd 
62790d3a30aSAdrian Chadd 	psec = ath_hal_mac_psec(ah, clks);
62890d3a30aSAdrian Chadd 	return (psec / 1000000);
62990d3a30aSAdrian Chadd }
63090d3a30aSAdrian Chadd 
63190d3a30aSAdrian Chadd /*
63290d3a30aSAdrian Chadd  * XXX TODO: half, quarter rates.
63390d3a30aSAdrian Chadd  */
63490d3a30aSAdrian Chadd uint64_t
ath_hal_mac_psec(struct ath_hal * ah,u_int clks)63590d3a30aSAdrian Chadd ath_hal_mac_psec(struct ath_hal *ah, u_int clks)
63690d3a30aSAdrian Chadd {
63759efa8b5SSam Leffler 	const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan;
63890d3a30aSAdrian Chadd 	uint64_t psec;
63914779705SSam Leffler 
64014779705SSam Leffler 	/* NB: ah_curchan may be null when called attach time */
64197efbf40SAdrian Chadd 	/* XXX merlin and later specific workaround - 5ghz fast clock is 44 */
64297efbf40SAdrian Chadd 	if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) {
64390d3a30aSAdrian Chadd 		psec = (clks * 1000000ULL) / CLOCK_FAST_RATE_5GHZ_OFDM;
64497efbf40SAdrian Chadd 		if (IEEE80211_IS_CHAN_HT40(c))
64590d3a30aSAdrian Chadd 			psec >>= 1;
64697efbf40SAdrian Chadd 	} else if (c != AH_NULL) {
64790d3a30aSAdrian Chadd 		psec = (clks * 1000000ULL) / CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
64859efa8b5SSam Leffler 		if (IEEE80211_IS_CHAN_HT40(c))
64990d3a30aSAdrian Chadd 			psec >>= 1;
65014779705SSam Leffler 	} else
65190d3a30aSAdrian Chadd 		psec = (clks * 1000000ULL) / CLOCK_RATE[WIRELESS_MODE_11b];
65290d3a30aSAdrian Chadd 	return psec;
65314779705SSam Leffler }
65414779705SSam Leffler 
65514779705SSam Leffler /*
65614779705SSam Leffler  * Setup a h/w rate table's reverse lookup table and
65714779705SSam Leffler  * fill in ack durations.  This routine is called for
65814779705SSam Leffler  * each rate table returned through the ah_getRateTable
65914779705SSam Leffler  * method.  The reverse lookup tables are assumed to be
66014779705SSam Leffler  * initialized to zero (or at least the first entry).
66114779705SSam Leffler  * We use this as a key that indicates whether or not
66214779705SSam Leffler  * we've previously setup the reverse lookup table.
66314779705SSam Leffler  *
66414779705SSam Leffler  * XXX not reentrant, but shouldn't matter
66514779705SSam Leffler  */
66614779705SSam Leffler void
ath_hal_setupratetable(struct ath_hal * ah,HAL_RATE_TABLE * rt)66714779705SSam Leffler ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt)
66814779705SSam Leffler {
66914779705SSam Leffler #define	N(a)	(sizeof(a)/sizeof(a[0]))
67014779705SSam Leffler 	int i;
67114779705SSam Leffler 
67214779705SSam Leffler 	if (rt->rateCodeToIndex[0] != 0)	/* already setup */
67314779705SSam Leffler 		return;
67414779705SSam Leffler 	for (i = 0; i < N(rt->rateCodeToIndex); i++)
67514779705SSam Leffler 		rt->rateCodeToIndex[i] = (uint8_t) -1;
67614779705SSam Leffler 	for (i = 0; i < rt->rateCount; i++) {
67714779705SSam Leffler 		uint8_t code = rt->info[i].rateCode;
67814779705SSam Leffler 		uint8_t cix = rt->info[i].controlRate;
67914779705SSam Leffler 
68014779705SSam Leffler 		HALASSERT(code < N(rt->rateCodeToIndex));
68114779705SSam Leffler 		rt->rateCodeToIndex[code] = i;
68214779705SSam Leffler 		HALASSERT((code | rt->info[i].shortPreamble) <
68314779705SSam Leffler 		    N(rt->rateCodeToIndex));
68414779705SSam Leffler 		rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i;
68514779705SSam Leffler 		/*
68614779705SSam Leffler 		 * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
68714779705SSam Leffler 		 *     depends on whether they are marked as basic rates;
68814779705SSam Leffler 		 *     the static tables are setup with an 11b-compatible
68914779705SSam Leffler 		 *     2Mb/s rate which will work but is suboptimal
69014779705SSam Leffler 		 */
69114779705SSam Leffler 		rt->info[i].lpAckDuration = ath_hal_computetxtime(ah, rt,
6927ff1939dSAdrian Chadd 			WLAN_CTRL_FRAME_SIZE, cix, AH_FALSE, AH_TRUE);
69314779705SSam Leffler 		rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt,
6947ff1939dSAdrian Chadd 			WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE, AH_TRUE);
69514779705SSam Leffler 	}
69614779705SSam Leffler #undef N
69714779705SSam Leffler }
69814779705SSam Leffler 
69914779705SSam Leffler HAL_STATUS
ath_hal_getcapability(struct ath_hal * ah,HAL_CAPABILITY_TYPE type,uint32_t capability,uint32_t * result)70014779705SSam Leffler ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
70114779705SSam Leffler 	uint32_t capability, uint32_t *result)
70214779705SSam Leffler {
70314779705SSam Leffler 	const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
70414779705SSam Leffler 
70514779705SSam Leffler 	switch (type) {
70614779705SSam Leffler 	case HAL_CAP_REG_DMN:		/* regulatory domain */
70714779705SSam Leffler 		*result = AH_PRIVATE(ah)->ah_currentRD;
70814779705SSam Leffler 		return HAL_OK;
7098db87e40SAdrian Chadd 	case HAL_CAP_DFS_DMN:		/* DFS Domain */
7108db87e40SAdrian Chadd 		*result = AH_PRIVATE(ah)->ah_dfsDomain;
7118db87e40SAdrian Chadd 		return HAL_OK;
71214779705SSam Leffler 	case HAL_CAP_CIPHER:		/* cipher handled in hardware */
71314779705SSam Leffler 	case HAL_CAP_TKIP_MIC:		/* handle TKIP MIC in hardware */
71414779705SSam Leffler 		return HAL_ENOTSUPP;
71514779705SSam Leffler 	case HAL_CAP_TKIP_SPLIT:	/* hardware TKIP uses split keys */
71614779705SSam Leffler 		return HAL_ENOTSUPP;
71714779705SSam Leffler 	case HAL_CAP_PHYCOUNTERS:	/* hardware PHY error counters */
71814779705SSam Leffler 		return pCap->halHwPhyCounterSupport ? HAL_OK : HAL_ENXIO;
71914779705SSam Leffler 	case HAL_CAP_WME_TKIPMIC:   /* hardware can do TKIP MIC when WMM is turned on */
72014779705SSam Leffler 		return HAL_ENOTSUPP;
72114779705SSam Leffler 	case HAL_CAP_DIVERSITY:		/* hardware supports fast diversity */
72214779705SSam Leffler 		return HAL_ENOTSUPP;
72314779705SSam Leffler 	case HAL_CAP_KEYCACHE_SIZE:	/* hardware key cache size */
72414779705SSam Leffler 		*result =  pCap->halKeyCacheSize;
72514779705SSam Leffler 		return HAL_OK;
72614779705SSam Leffler 	case HAL_CAP_NUM_TXQUEUES:	/* number of hardware tx queues */
72714779705SSam Leffler 		*result = pCap->halTotalQueues;
72814779705SSam Leffler 		return HAL_OK;
72914779705SSam Leffler 	case HAL_CAP_VEOL:		/* hardware supports virtual EOL */
73014779705SSam Leffler 		return pCap->halVEOLSupport ? HAL_OK : HAL_ENOTSUPP;
73114779705SSam Leffler 	case HAL_CAP_PSPOLL:		/* hardware PS-Poll support works */
73214779705SSam Leffler 		return pCap->halPSPollBroken ? HAL_ENOTSUPP : HAL_OK;
73314779705SSam Leffler 	case HAL_CAP_COMPRESSION:
73414779705SSam Leffler 		return pCap->halCompressSupport ? HAL_OK : HAL_ENOTSUPP;
73514779705SSam Leffler 	case HAL_CAP_BURST:
73614779705SSam Leffler 		return pCap->halBurstSupport ? HAL_OK : HAL_ENOTSUPP;
73714779705SSam Leffler 	case HAL_CAP_FASTFRAME:
73814779705SSam Leffler 		return pCap->halFastFramesSupport ? HAL_OK : HAL_ENOTSUPP;
73914779705SSam Leffler 	case HAL_CAP_DIAG:		/* hardware diagnostic support */
74014779705SSam Leffler 		*result = AH_PRIVATE(ah)->ah_diagreg;
74114779705SSam Leffler 		return HAL_OK;
74214779705SSam Leffler 	case HAL_CAP_TXPOW:		/* global tx power limit  */
74314779705SSam Leffler 		switch (capability) {
74414779705SSam Leffler 		case 0:			/* facility is supported */
74514779705SSam Leffler 			return HAL_OK;
74614779705SSam Leffler 		case 1:			/* current limit */
74714779705SSam Leffler 			*result = AH_PRIVATE(ah)->ah_powerLimit;
74814779705SSam Leffler 			return HAL_OK;
74914779705SSam Leffler 		case 2:			/* current max tx power */
75014779705SSam Leffler 			*result = AH_PRIVATE(ah)->ah_maxPowerLevel;
75114779705SSam Leffler 			return HAL_OK;
75214779705SSam Leffler 		case 3:			/* scale factor */
75314779705SSam Leffler 			*result = AH_PRIVATE(ah)->ah_tpScale;
75414779705SSam Leffler 			return HAL_OK;
75514779705SSam Leffler 		}
75614779705SSam Leffler 		return HAL_ENOTSUPP;
75714779705SSam Leffler 	case HAL_CAP_BSSIDMASK:		/* hardware supports bssid mask */
75814779705SSam Leffler 		return pCap->halBssIdMaskSupport ? HAL_OK : HAL_ENOTSUPP;
75914779705SSam Leffler 	case HAL_CAP_MCAST_KEYSRCH:	/* multicast frame keycache search */
76014779705SSam Leffler 		return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENOTSUPP;
76114779705SSam Leffler 	case HAL_CAP_TSF_ADJUST:	/* hardware has beacon tsf adjust */
76214779705SSam Leffler 		return HAL_ENOTSUPP;
76314779705SSam Leffler 	case HAL_CAP_RFSILENT:		/* rfsilent support  */
76414779705SSam Leffler 		switch (capability) {
76514779705SSam Leffler 		case 0:			/* facility is supported */
76614779705SSam Leffler 			return pCap->halRfSilentSupport ? HAL_OK : HAL_ENOTSUPP;
76714779705SSam Leffler 		case 1:			/* current setting */
76814779705SSam Leffler 			return AH_PRIVATE(ah)->ah_rfkillEnabled ?
76914779705SSam Leffler 				HAL_OK : HAL_ENOTSUPP;
77014779705SSam Leffler 		case 2:			/* rfsilent config */
77114779705SSam Leffler 			*result = AH_PRIVATE(ah)->ah_rfsilent;
77214779705SSam Leffler 			return HAL_OK;
77314779705SSam Leffler 		}
77414779705SSam Leffler 		return HAL_ENOTSUPP;
77514779705SSam Leffler 	case HAL_CAP_11D:
77614779705SSam Leffler 		return HAL_OK;
77726e8415dSAdrian Chadd 
77814779705SSam Leffler 	case HAL_CAP_HT:
77914779705SSam Leffler 		return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP;
78026e8415dSAdrian Chadd 	case HAL_CAP_GTXTO:
78126e8415dSAdrian Chadd 		return pCap->halGTTSupport ? HAL_OK : HAL_ENOTSUPP;
78226e8415dSAdrian Chadd 	case HAL_CAP_FAST_CC:
78326e8415dSAdrian Chadd 		return pCap->halFastCCSupport ? HAL_OK : HAL_ENOTSUPP;
78414779705SSam Leffler 	case HAL_CAP_TX_CHAINMASK:	/* mask of TX chains supported */
78514779705SSam Leffler 		*result = pCap->halTxChainMask;
78614779705SSam Leffler 		return HAL_OK;
78714779705SSam Leffler 	case HAL_CAP_RX_CHAINMASK:	/* mask of RX chains supported */
78814779705SSam Leffler 		*result = pCap->halRxChainMask;
78914779705SSam Leffler 		return HAL_OK;
79026e8415dSAdrian Chadd 	case HAL_CAP_NUM_GPIO_PINS:
79126e8415dSAdrian Chadd 		*result = pCap->halNumGpioPins;
79226e8415dSAdrian Chadd 		return HAL_OK;
79326e8415dSAdrian Chadd 	case HAL_CAP_CST:
79426e8415dSAdrian Chadd 		return pCap->halCSTSupport ? HAL_OK : HAL_ENOTSUPP;
79526e8415dSAdrian Chadd 	case HAL_CAP_RTS_AGGR_LIMIT:
79626e8415dSAdrian Chadd 		*result = pCap->halRtsAggrLimit;
79726e8415dSAdrian Chadd 		return HAL_OK;
79826e8415dSAdrian Chadd 	case HAL_CAP_4ADDR_AGGR:
79926e8415dSAdrian Chadd 		return pCap->hal4AddrAggrSupport ? HAL_OK : HAL_ENOTSUPP;
8002cb5233bSAdrian Chadd 	case HAL_CAP_EXT_CHAN_DFS:
8012cb5233bSAdrian Chadd 		return pCap->halExtChanDfsSupport ? HAL_OK : HAL_ENOTSUPP;
8026606ba81SAdrian Chadd 	case HAL_CAP_RX_STBC:
8036606ba81SAdrian Chadd 		return pCap->halRxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
8046606ba81SAdrian Chadd 	case HAL_CAP_TX_STBC:
8056606ba81SAdrian Chadd 		return pCap->halTxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
806f8649041SAdrian Chadd 	case HAL_CAP_COMBINED_RADAR_RSSI:
807f8649041SAdrian Chadd 		return pCap->halUseCombinedRadarRssi ? HAL_OK : HAL_ENOTSUPP;
808f8649041SAdrian Chadd 	case HAL_CAP_AUTO_SLEEP:
809f8649041SAdrian Chadd 		return pCap->halAutoSleepSupport ? HAL_OK : HAL_ENOTSUPP;
810f8649041SAdrian Chadd 	case HAL_CAP_MBSSID_AGGR_SUPPORT:
811f8649041SAdrian Chadd 		return pCap->halMbssidAggrSupport ? HAL_OK : HAL_ENOTSUPP;
812f8649041SAdrian Chadd 	case HAL_CAP_SPLIT_4KB_TRANS:	/* hardware handles descriptors straddling 4k page boundary */
813f8649041SAdrian Chadd 		return pCap->hal4kbSplitTransSupport ? HAL_OK : HAL_ENOTSUPP;
814f8649041SAdrian Chadd 	case HAL_CAP_REG_FLAG:
815f8649041SAdrian Chadd 		*result = AH_PRIVATE(ah)->ah_currentRDext;
816f8649041SAdrian Chadd 		return HAL_OK;
817f8649041SAdrian Chadd 	case HAL_CAP_ENHANCED_DMA_SUPPORT:
818f8649041SAdrian Chadd 		return pCap->halEnhancedDmaSupport ? HAL_OK : HAL_ENOTSUPP;
8190a6b6951SAdrian Chadd 	case HAL_CAP_NUM_TXMAPS:
8200a6b6951SAdrian Chadd 		*result = pCap->halNumTxMaps;
8210a6b6951SAdrian Chadd 		return HAL_OK;
8220a6b6951SAdrian Chadd 	case HAL_CAP_TXDESCLEN:
8230a6b6951SAdrian Chadd 		*result = pCap->halTxDescLen;
8240a6b6951SAdrian Chadd 		return HAL_OK;
8250a6b6951SAdrian Chadd 	case HAL_CAP_TXSTATUSLEN:
8260a6b6951SAdrian Chadd 		*result = pCap->halTxStatusLen;
8270a6b6951SAdrian Chadd 		return HAL_OK;
8280a6b6951SAdrian Chadd 	case HAL_CAP_RXSTATUSLEN:
8290a6b6951SAdrian Chadd 		*result = pCap->halRxStatusLen;
8300a6b6951SAdrian Chadd 		return HAL_OK;
8310a6b6951SAdrian Chadd 	case HAL_CAP_RXFIFODEPTH:
8320a6b6951SAdrian Chadd 		switch (capability) {
8330a6b6951SAdrian Chadd 		case HAL_RX_QUEUE_HP:
8340a6b6951SAdrian Chadd 			*result = pCap->halRxHpFifoDepth;
8350a6b6951SAdrian Chadd 			return HAL_OK;
8360a6b6951SAdrian Chadd 		case HAL_RX_QUEUE_LP:
8370a6b6951SAdrian Chadd 			*result = pCap->halRxLpFifoDepth;
8380a6b6951SAdrian Chadd 			return HAL_OK;
8390a6b6951SAdrian Chadd 		default:
8400a6b6951SAdrian Chadd 			return HAL_ENOTSUPP;
8410a6b6951SAdrian Chadd 	}
8420a6b6951SAdrian Chadd 	case HAL_CAP_RXBUFSIZE:
8430a6b6951SAdrian Chadd 	case HAL_CAP_NUM_MR_RETRIES:
844ee3e4df9SAdrian Chadd 		*result = pCap->halNumMRRetries;
845ee3e4df9SAdrian Chadd 		return HAL_OK;
84626e8415dSAdrian Chadd 	case HAL_CAP_BT_COEX:
84726e8415dSAdrian Chadd 		return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP;
84829dbc483SAdrian Chadd 	case HAL_CAP_SPECTRAL_SCAN:
84929dbc483SAdrian Chadd 		return pCap->halSpectralScanSupport ? HAL_OK : HAL_ENOTSUPP;
85026e8415dSAdrian Chadd 	case HAL_CAP_HT20_SGI:
85126e8415dSAdrian Chadd 		return pCap->halHTSGI20Support ? HAL_OK : HAL_ENOTSUPP;
85214779705SSam Leffler 	case HAL_CAP_RXTSTAMP_PREC:	/* rx desc tstamp precision (bits) */
85351558243SAdrian Chadd 		*result = pCap->halRxTstampPrecision;
85414779705SSam Leffler 		return HAL_OK;
855bc1af557SAdrian Chadd 	case HAL_CAP_ANT_DIV_COMB:	/* AR9285/AR9485 LNA diversity */
856bc1af557SAdrian Chadd 		return pCap->halAntDivCombSupport ? HAL_OK  : HAL_ENOTSUPP;
857bc1af557SAdrian Chadd 
8582cb5233bSAdrian Chadd 	case HAL_CAP_ENHANCED_DFS_SUPPORT:
8592cb5233bSAdrian Chadd 		return pCap->halEnhancedDfsSupport ? HAL_OK : HAL_ENOTSUPP;
86026e8415dSAdrian Chadd 
86126e8415dSAdrian Chadd 	/* FreeBSD-specific entries for now */
86226e8415dSAdrian Chadd 	case HAL_CAP_RXORN_FATAL:	/* HAL_INT_RXORN treated as fatal  */
86326e8415dSAdrian Chadd 		return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP;
864683f3134SSam Leffler 	case HAL_CAP_INTRMASK:		/* mask of supported interrupts */
865683f3134SSam Leffler 		*result = pCap->halIntrMask;
866683f3134SSam Leffler 		return HAL_OK;
8673c3e9d33SSam Leffler 	case HAL_CAP_BSSIDMATCH:	/* hardware has disable bssid match */
8683c3e9d33SSam Leffler 		return pCap->halBssidMatchSupport ? HAL_OK : HAL_ENOTSUPP;
86994d748d2SAdrian Chadd 	case HAL_CAP_STREAMS:		/* number of 11n spatial streams */
87094d748d2SAdrian Chadd 		switch (capability) {
87194d748d2SAdrian Chadd 		case 0:			/* TX */
87294d748d2SAdrian Chadd 			*result = pCap->halTxStreams;
87394d748d2SAdrian Chadd 			return HAL_OK;
87494d748d2SAdrian Chadd 		case 1:			/* RX */
87594d748d2SAdrian Chadd 			*result = pCap->halRxStreams;
87694d748d2SAdrian Chadd 			return HAL_OK;
87794d748d2SAdrian Chadd 		default:
87894d748d2SAdrian Chadd 			return HAL_ENOTSUPP;
87994d748d2SAdrian Chadd 		}
8808a2a6beeSAdrian Chadd 	case HAL_CAP_RXDESC_SELFLINK:	/* hardware supports self-linked final RX descriptors correctly */
8818a2a6beeSAdrian Chadd 		return pCap->halHasRxSelfLinkedTail ? HAL_OK : HAL_ENOTSUPP;
88246614948SAdrian Chadd 	case HAL_CAP_BB_READ_WAR:		/* Baseband read WAR */
88346614948SAdrian Chadd 		return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP;
884ddbe3036SAdrian Chadd 	case HAL_CAP_SERIALISE_WAR:		/* PCI register serialisation */
885ddbe3036SAdrian Chadd 		return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP;
886b042e6a3SAdrian Chadd 	case HAL_CAP_MFP:			/* Management frame protection setting */
887b042e6a3SAdrian Chadd 		*result = pCap->halMfpSupport;
888b042e6a3SAdrian Chadd 		return HAL_OK;
889d98a3d69SAdrian Chadd 	case HAL_CAP_RX_LNA_MIXING:	/* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */
890d98a3d69SAdrian Chadd 		return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP;
891dd7b232eSAdrian Chadd 	case HAL_CAP_DO_MYBEACON:	/* Hardware supports filtering my-beacons */
892dd7b232eSAdrian Chadd 		return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP;
89351558243SAdrian Chadd 	case HAL_CAP_TXTSTAMP_PREC:	/* tx desc tstamp precision (bits) */
89451558243SAdrian Chadd 		*result = pCap->halTxTstampPrecision;
89551558243SAdrian Chadd 		return HAL_OK;
89614779705SSam Leffler 	default:
89714779705SSam Leffler 		return HAL_EINVAL;
89814779705SSam Leffler 	}
89914779705SSam Leffler }
90014779705SSam Leffler 
90114779705SSam Leffler HAL_BOOL
ath_hal_setcapability(struct ath_hal * ah,HAL_CAPABILITY_TYPE type,uint32_t capability,uint32_t setting,HAL_STATUS * status)90214779705SSam Leffler ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
90314779705SSam Leffler 	uint32_t capability, uint32_t setting, HAL_STATUS *status)
90414779705SSam Leffler {
90514779705SSam Leffler 
90614779705SSam Leffler 	switch (type) {
90714779705SSam Leffler 	case HAL_CAP_TXPOW:
90814779705SSam Leffler 		switch (capability) {
90914779705SSam Leffler 		case 3:
91014779705SSam Leffler 			if (setting <= HAL_TP_SCALE_MIN) {
91114779705SSam Leffler 				AH_PRIVATE(ah)->ah_tpScale = setting;
91214779705SSam Leffler 				return AH_TRUE;
91314779705SSam Leffler 			}
91414779705SSam Leffler 			break;
91514779705SSam Leffler 		}
91614779705SSam Leffler 		break;
91714779705SSam Leffler 	case HAL_CAP_RFSILENT:		/* rfsilent support  */
91814779705SSam Leffler 		/*
91914779705SSam Leffler 		 * NB: allow even if halRfSilentSupport is false
92014779705SSam Leffler 		 *     in case the EEPROM is misprogrammed.
92114779705SSam Leffler 		 */
92214779705SSam Leffler 		switch (capability) {
92314779705SSam Leffler 		case 1:			/* current setting */
92414779705SSam Leffler 			AH_PRIVATE(ah)->ah_rfkillEnabled = (setting != 0);
92514779705SSam Leffler 			return AH_TRUE;
92614779705SSam Leffler 		case 2:			/* rfsilent config */
92714779705SSam Leffler 			/* XXX better done per-chip for validation? */
92814779705SSam Leffler 			AH_PRIVATE(ah)->ah_rfsilent = setting;
92914779705SSam Leffler 			return AH_TRUE;
93014779705SSam Leffler 		}
93114779705SSam Leffler 		break;
93214779705SSam Leffler 	case HAL_CAP_REG_DMN:		/* regulatory domain */
93314779705SSam Leffler 		AH_PRIVATE(ah)->ah_currentRD = setting;
93414779705SSam Leffler 		return AH_TRUE;
93514779705SSam Leffler 	case HAL_CAP_RXORN_FATAL:	/* HAL_INT_RXORN treated as fatal  */
93614779705SSam Leffler 		AH_PRIVATE(ah)->ah_rxornIsFatal = setting;
93714779705SSam Leffler 		return AH_TRUE;
93814779705SSam Leffler 	default:
93914779705SSam Leffler 		break;
94014779705SSam Leffler 	}
94114779705SSam Leffler 	if (status)
94214779705SSam Leffler 		*status = HAL_EINVAL;
94314779705SSam Leffler 	return AH_FALSE;
94414779705SSam Leffler }
94514779705SSam Leffler 
94614779705SSam Leffler /*
94714779705SSam Leffler  * Common support for getDiagState method.
94814779705SSam Leffler  */
94914779705SSam Leffler 
95014779705SSam Leffler static u_int
ath_hal_getregdump(struct ath_hal * ah,const HAL_REGRANGE * regs,void * dstbuf,int space)95114779705SSam Leffler ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs,
95214779705SSam Leffler 	void *dstbuf, int space)
95314779705SSam Leffler {
95414779705SSam Leffler 	uint32_t *dp = dstbuf;
95514779705SSam Leffler 	int i;
95614779705SSam Leffler 
95714779705SSam Leffler 	for (i = 0; space >= 2*sizeof(uint32_t); i++) {
958fad86101SAdrian Chadd 		uint32_t r = regs[i].start;
959fad86101SAdrian Chadd 		uint32_t e = regs[i].end;
960fad86101SAdrian Chadd 		*dp++ = r;
961fad86101SAdrian Chadd 		*dp++ = e;
962fad86101SAdrian Chadd 		space -= 2*sizeof(uint32_t);
96314779705SSam Leffler 		do {
96414779705SSam Leffler 			*dp++ = OS_REG_READ(ah, r);
96514779705SSam Leffler 			r += sizeof(uint32_t);
96614779705SSam Leffler 			space -= sizeof(uint32_t);
96714779705SSam Leffler 		} while (r <= e && space >= sizeof(uint32_t));
96814779705SSam Leffler 	}
96914779705SSam Leffler 	return (char *) dp - (char *) dstbuf;
97014779705SSam Leffler }
97114779705SSam Leffler 
9721a15e29aSSam Leffler static void
ath_hal_setregs(struct ath_hal * ah,const HAL_REGWRITE * regs,int space)9731a15e29aSSam Leffler ath_hal_setregs(struct ath_hal *ah, const HAL_REGWRITE *regs, int space)
9741a15e29aSSam Leffler {
9751a15e29aSSam Leffler 	while (space >= sizeof(HAL_REGWRITE)) {
9761a15e29aSSam Leffler 		OS_REG_WRITE(ah, regs->addr, regs->value);
9771a15e29aSSam Leffler 		regs++, space -= sizeof(HAL_REGWRITE);
9781a15e29aSSam Leffler 	}
9791a15e29aSSam Leffler }
9801a15e29aSSam Leffler 
98114779705SSam Leffler HAL_BOOL
ath_hal_getdiagstate(struct ath_hal * ah,int request,const void * args,uint32_t argsize,void ** result,uint32_t * resultsize)98214779705SSam Leffler ath_hal_getdiagstate(struct ath_hal *ah, int request,
98314779705SSam Leffler 	const void *args, uint32_t argsize,
98414779705SSam Leffler 	void **result, uint32_t *resultsize)
98514779705SSam Leffler {
986b0602becSAdrian Chadd 
98714779705SSam Leffler 	switch (request) {
98814779705SSam Leffler 	case HAL_DIAG_REVS:
98914779705SSam Leffler 		*result = &AH_PRIVATE(ah)->ah_devid;
99014779705SSam Leffler 		*resultsize = sizeof(HAL_REVS);
99114779705SSam Leffler 		return AH_TRUE;
99214779705SSam Leffler 	case HAL_DIAG_REGS:
99314779705SSam Leffler 		*resultsize = ath_hal_getregdump(ah, args, *result,*resultsize);
99414779705SSam Leffler 		return AH_TRUE;
9951a15e29aSSam Leffler 	case HAL_DIAG_SETREGS:
9961a15e29aSSam Leffler 		ath_hal_setregs(ah, args, argsize);
9971a15e29aSSam Leffler 		*resultsize = 0;
9981a15e29aSSam Leffler 		return AH_TRUE;
99914779705SSam Leffler 	case HAL_DIAG_FATALERR:
100014779705SSam Leffler 		*result = &AH_PRIVATE(ah)->ah_fatalState[0];
100114779705SSam Leffler 		*resultsize = sizeof(AH_PRIVATE(ah)->ah_fatalState);
100214779705SSam Leffler 		return AH_TRUE;
100314779705SSam Leffler 	case HAL_DIAG_EEREAD:
100414779705SSam Leffler 		if (argsize != sizeof(uint16_t))
100514779705SSam Leffler 			return AH_FALSE;
100614779705SSam Leffler 		if (!ath_hal_eepromRead(ah, *(const uint16_t *)args, *result))
100714779705SSam Leffler 			return AH_FALSE;
100814779705SSam Leffler 		*resultsize = sizeof(uint16_t);
100914779705SSam Leffler 		return AH_TRUE;
101014779705SSam Leffler #ifdef AH_PRIVATE_DIAG
101114779705SSam Leffler 	case HAL_DIAG_SETKEY: {
101214779705SSam Leffler 		const HAL_DIAG_KEYVAL *dk;
101314779705SSam Leffler 
101414779705SSam Leffler 		if (argsize != sizeof(HAL_DIAG_KEYVAL))
101514779705SSam Leffler 			return AH_FALSE;
101614779705SSam Leffler 		dk = (const HAL_DIAG_KEYVAL *)args;
101714779705SSam Leffler 		return ah->ah_setKeyCacheEntry(ah, dk->dk_keyix,
101814779705SSam Leffler 			&dk->dk_keyval, dk->dk_mac, dk->dk_xor);
101914779705SSam Leffler 	}
102014779705SSam Leffler 	case HAL_DIAG_RESETKEY:
102114779705SSam Leffler 		if (argsize != sizeof(uint16_t))
102214779705SSam Leffler 			return AH_FALSE;
102314779705SSam Leffler 		return ah->ah_resetKeyCacheEntry(ah, *(const uint16_t *)args);
102414779705SSam Leffler #ifdef AH_SUPPORT_WRITE_EEPROM
102514779705SSam Leffler 	case HAL_DIAG_EEWRITE: {
102614779705SSam Leffler 		const HAL_DIAG_EEVAL *ee;
102714779705SSam Leffler 		if (argsize != sizeof(HAL_DIAG_EEVAL))
102814779705SSam Leffler 			return AH_FALSE;
102914779705SSam Leffler 		ee = (const HAL_DIAG_EEVAL *)args;
103014779705SSam Leffler 		return ath_hal_eepromWrite(ah, ee->ee_off, ee->ee_data);
103114779705SSam Leffler 	}
103214779705SSam Leffler #endif /* AH_SUPPORT_WRITE_EEPROM */
103314779705SSam Leffler #endif /* AH_PRIVATE_DIAG */
103414779705SSam Leffler 	case HAL_DIAG_11NCOMPAT:
103514779705SSam Leffler 		if (argsize == 0) {
103614779705SSam Leffler 			*resultsize = sizeof(uint32_t);
103714779705SSam Leffler 			*((uint32_t *)(*result)) =
103814779705SSam Leffler 				AH_PRIVATE(ah)->ah_11nCompat;
103914779705SSam Leffler 		} else if (argsize == sizeof(uint32_t)) {
104014779705SSam Leffler 			AH_PRIVATE(ah)->ah_11nCompat = *(const uint32_t *)args;
104114779705SSam Leffler 		} else
104214779705SSam Leffler 			return AH_FALSE;
104314779705SSam Leffler 		return AH_TRUE;
1044b0602becSAdrian Chadd 	case HAL_DIAG_CHANSURVEY:
1045b0602becSAdrian Chadd 		*result = &AH_PRIVATE(ah)->ah_chansurvey;
1046b0602becSAdrian Chadd 		*resultsize = sizeof(HAL_CHANNEL_SURVEY);
1047b0602becSAdrian Chadd 		return AH_TRUE;
104814779705SSam Leffler 	}
104914779705SSam Leffler 	return AH_FALSE;
105014779705SSam Leffler }
105114779705SSam Leffler 
105214779705SSam Leffler /*
105314779705SSam Leffler  * Set the properties of the tx queue with the parameters
105414779705SSam Leffler  * from qInfo.
105514779705SSam Leffler  */
105614779705SSam Leffler HAL_BOOL
ath_hal_setTxQProps(struct ath_hal * ah,HAL_TX_QUEUE_INFO * qi,const HAL_TXQ_INFO * qInfo)105714779705SSam Leffler ath_hal_setTxQProps(struct ath_hal *ah,
105814779705SSam Leffler 	HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo)
105914779705SSam Leffler {
106014779705SSam Leffler 	uint32_t cw;
106114779705SSam Leffler 
106214779705SSam Leffler 	if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
106314779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
106414779705SSam Leffler 		    "%s: inactive queue\n", __func__);
106514779705SSam Leffler 		return AH_FALSE;
106614779705SSam Leffler 	}
106714779705SSam Leffler 	/* XXX validate parameters */
106814779705SSam Leffler 	qi->tqi_ver = qInfo->tqi_ver;
106914779705SSam Leffler 	qi->tqi_subtype = qInfo->tqi_subtype;
107014779705SSam Leffler 	qi->tqi_qflags = qInfo->tqi_qflags;
107114779705SSam Leffler 	qi->tqi_priority = qInfo->tqi_priority;
107214779705SSam Leffler 	if (qInfo->tqi_aifs != HAL_TXQ_USEDEFAULT)
107314779705SSam Leffler 		qi->tqi_aifs = AH_MIN(qInfo->tqi_aifs, 255);
107414779705SSam Leffler 	else
107514779705SSam Leffler 		qi->tqi_aifs = INIT_AIFS;
107614779705SSam Leffler 	if (qInfo->tqi_cwmin != HAL_TXQ_USEDEFAULT) {
107714779705SSam Leffler 		cw = AH_MIN(qInfo->tqi_cwmin, 1024);
107814779705SSam Leffler 		/* make sure that the CWmin is of the form (2^n - 1) */
107914779705SSam Leffler 		qi->tqi_cwmin = 1;
108014779705SSam Leffler 		while (qi->tqi_cwmin < cw)
108114779705SSam Leffler 			qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1;
108214779705SSam Leffler 	} else
108314779705SSam Leffler 		qi->tqi_cwmin = qInfo->tqi_cwmin;
108414779705SSam Leffler 	if (qInfo->tqi_cwmax != HAL_TXQ_USEDEFAULT) {
108514779705SSam Leffler 		cw = AH_MIN(qInfo->tqi_cwmax, 1024);
108614779705SSam Leffler 		/* make sure that the CWmax is of the form (2^n - 1) */
108714779705SSam Leffler 		qi->tqi_cwmax = 1;
108814779705SSam Leffler 		while (qi->tqi_cwmax < cw)
108914779705SSam Leffler 			qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1;
109014779705SSam Leffler 	} else
109114779705SSam Leffler 		qi->tqi_cwmax = INIT_CWMAX;
109214779705SSam Leffler 	/* Set retry limit values */
109314779705SSam Leffler 	if (qInfo->tqi_shretry != 0)
109414779705SSam Leffler 		qi->tqi_shretry = AH_MIN(qInfo->tqi_shretry, 15);
109514779705SSam Leffler 	else
109614779705SSam Leffler 		qi->tqi_shretry = INIT_SH_RETRY;
109714779705SSam Leffler 	if (qInfo->tqi_lgretry != 0)
109814779705SSam Leffler 		qi->tqi_lgretry = AH_MIN(qInfo->tqi_lgretry, 15);
109914779705SSam Leffler 	else
110014779705SSam Leffler 		qi->tqi_lgretry = INIT_LG_RETRY;
110114779705SSam Leffler 	qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod;
110214779705SSam Leffler 	qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit;
110314779705SSam Leffler 	qi->tqi_burstTime = qInfo->tqi_burstTime;
110414779705SSam Leffler 	qi->tqi_readyTime = qInfo->tqi_readyTime;
110514779705SSam Leffler 
110614779705SSam Leffler 	switch (qInfo->tqi_subtype) {
110714779705SSam Leffler 	case HAL_WME_UPSD:
110814779705SSam Leffler 		if (qi->tqi_type == HAL_TX_QUEUE_DATA)
110914779705SSam Leffler 			qi->tqi_intFlags = HAL_TXQ_USE_LOCKOUT_BKOFF_DIS;
111014779705SSam Leffler 		break;
111114779705SSam Leffler 	default:
111214779705SSam Leffler 		break;		/* NB: silence compiler */
111314779705SSam Leffler 	}
111414779705SSam Leffler 	return AH_TRUE;
111514779705SSam Leffler }
111614779705SSam Leffler 
111714779705SSam Leffler HAL_BOOL
ath_hal_getTxQProps(struct ath_hal * ah,HAL_TXQ_INFO * qInfo,const HAL_TX_QUEUE_INFO * qi)111814779705SSam Leffler ath_hal_getTxQProps(struct ath_hal *ah,
111914779705SSam Leffler 	HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi)
112014779705SSam Leffler {
112114779705SSam Leffler 	if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
112214779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
112314779705SSam Leffler 		    "%s: inactive queue\n", __func__);
112414779705SSam Leffler 		return AH_FALSE;
112514779705SSam Leffler 	}
112614779705SSam Leffler 
112714779705SSam Leffler 	qInfo->tqi_ver = qi->tqi_ver;
112814779705SSam Leffler 	qInfo->tqi_subtype = qi->tqi_subtype;
112914779705SSam Leffler 	qInfo->tqi_qflags = qi->tqi_qflags;
113014779705SSam Leffler 	qInfo->tqi_priority = qi->tqi_priority;
113114779705SSam Leffler 	qInfo->tqi_aifs = qi->tqi_aifs;
113214779705SSam Leffler 	qInfo->tqi_cwmin = qi->tqi_cwmin;
113314779705SSam Leffler 	qInfo->tqi_cwmax = qi->tqi_cwmax;
113414779705SSam Leffler 	qInfo->tqi_shretry = qi->tqi_shretry;
113514779705SSam Leffler 	qInfo->tqi_lgretry = qi->tqi_lgretry;
113614779705SSam Leffler 	qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod;
113714779705SSam Leffler 	qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit;
113814779705SSam Leffler 	qInfo->tqi_burstTime = qi->tqi_burstTime;
113914779705SSam Leffler 	qInfo->tqi_readyTime = qi->tqi_readyTime;
1140*1bae1560SAdrian Chadd 	qInfo->tqi_compBuf = qi->tqi_physCompBuf;
114114779705SSam Leffler 	return AH_TRUE;
114214779705SSam Leffler }
114314779705SSam Leffler 
114414779705SSam Leffler                                      /* 11a Turbo  11b  11g  108g */
114514779705SSam Leffler static const int16_t NOISE_FLOOR[] = { -96, -93,  -98, -96,  -93 };
114614779705SSam Leffler 
114714779705SSam Leffler /*
114814779705SSam Leffler  * Read the current channel noise floor and return.
114914779705SSam Leffler  * If nf cal hasn't finished, channel noise floor should be 0
115014779705SSam Leffler  * and we return a nominal value based on band and frequency.
115114779705SSam Leffler  *
115214779705SSam Leffler  * NB: This is a private routine used by per-chip code to
115314779705SSam Leffler  *     implement the ah_getChanNoise method.
115414779705SSam Leffler  */
115514779705SSam Leffler int16_t
ath_hal_getChanNoise(struct ath_hal * ah,const struct ieee80211_channel * chan)115659efa8b5SSam Leffler ath_hal_getChanNoise(struct ath_hal *ah, const struct ieee80211_channel *chan)
115714779705SSam Leffler {
115814779705SSam Leffler 	HAL_CHANNEL_INTERNAL *ichan;
115914779705SSam Leffler 
116014779705SSam Leffler 	ichan = ath_hal_checkchannel(ah, chan);
116114779705SSam Leffler 	if (ichan == AH_NULL) {
116214779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_NFCAL,
116314779705SSam Leffler 		    "%s: invalid channel %u/0x%x; no mapping\n",
116459efa8b5SSam Leffler 		    __func__, chan->ic_freq, chan->ic_flags);
116514779705SSam Leffler 		return 0;
116614779705SSam Leffler 	}
116714779705SSam Leffler 	if (ichan->rawNoiseFloor == 0) {
116814779705SSam Leffler 		WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan);
116914779705SSam Leffler 
117014779705SSam Leffler 		HALASSERT(mode < WIRELESS_MODE_MAX);
117114779705SSam Leffler 		return NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan);
117214779705SSam Leffler 	} else
117314779705SSam Leffler 		return ichan->rawNoiseFloor + ichan->noiseFloorAdjust;
117414779705SSam Leffler }
117514779705SSam Leffler 
117614779705SSam Leffler /*
117782c30dc4SAdrian Chadd  * Fetch the current setup of ctl/ext noise floor values.
117882c30dc4SAdrian Chadd  *
117982c30dc4SAdrian Chadd  * If the CHANNEL_MIMO_NF_VALID flag isn't set, the array is simply
118082c30dc4SAdrian Chadd  * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust().
118182c30dc4SAdrian Chadd  *
118282c30dc4SAdrian Chadd  * The caller must supply ctl/ext NF arrays which are at least
1183d94f2d7fSAdrian Chadd  * AH_MAX_CHAINS entries long.
118482c30dc4SAdrian Chadd  */
118582c30dc4SAdrian Chadd int
ath_hal_get_mimo_chan_noise(struct ath_hal * ah,const struct ieee80211_channel * chan,int16_t * nf_ctl,int16_t * nf_ext)118682c30dc4SAdrian Chadd ath_hal_get_mimo_chan_noise(struct ath_hal *ah,
11877ab2ab91SAdrian Chadd     const struct ieee80211_channel *chan, int16_t *nf_ctl,
11887ab2ab91SAdrian Chadd     int16_t *nf_ext)
118982c30dc4SAdrian Chadd {
119082c30dc4SAdrian Chadd 	HAL_CHANNEL_INTERNAL *ichan;
119182c30dc4SAdrian Chadd 	int i;
119282c30dc4SAdrian Chadd 
119382c30dc4SAdrian Chadd 	ichan = ath_hal_checkchannel(ah, chan);
119482c30dc4SAdrian Chadd 	if (ichan == AH_NULL) {
119582c30dc4SAdrian Chadd 		HALDEBUG(ah, HAL_DEBUG_NFCAL,
119682c30dc4SAdrian Chadd 		    "%s: invalid channel %u/0x%x; no mapping\n",
119782c30dc4SAdrian Chadd 		    __func__, chan->ic_freq, chan->ic_flags);
1198d94f2d7fSAdrian Chadd 		for (i = 0; i < AH_MAX_CHAINS; i++) {
119982c30dc4SAdrian Chadd 			nf_ctl[i] = nf_ext[i] = 0;
120082c30dc4SAdrian Chadd 		}
120182c30dc4SAdrian Chadd 		return 0;
120282c30dc4SAdrian Chadd 	}
120382c30dc4SAdrian Chadd 
120482c30dc4SAdrian Chadd 	/* Return 0 if there's no valid MIMO values (yet) */
120582c30dc4SAdrian Chadd 	if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) {
1206d94f2d7fSAdrian Chadd 		for (i = 0; i < AH_MAX_CHAINS; i++) {
120782c30dc4SAdrian Chadd 			nf_ctl[i] = nf_ext[i] = 0;
120882c30dc4SAdrian Chadd 		}
120982c30dc4SAdrian Chadd 		return 0;
121082c30dc4SAdrian Chadd 	}
121182c30dc4SAdrian Chadd 	if (ichan->rawNoiseFloor == 0) {
121282c30dc4SAdrian Chadd 		WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan);
121382c30dc4SAdrian Chadd 		HALASSERT(mode < WIRELESS_MODE_MAX);
121482c30dc4SAdrian Chadd 		/*
121582c30dc4SAdrian Chadd 		 * See the comment below - this could cause issues for
121682c30dc4SAdrian Chadd 		 * stations which have a very low RSSI, below the
121782c30dc4SAdrian Chadd 		 * 'normalised' NF values in NOISE_FLOOR[].
121882c30dc4SAdrian Chadd 		 */
1219d94f2d7fSAdrian Chadd 		for (i = 0; i < AH_MAX_CHAINS; i++) {
122082c30dc4SAdrian Chadd 			nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] +
122182c30dc4SAdrian Chadd 			    ath_hal_getNfAdjust(ah, ichan);
122282c30dc4SAdrian Chadd 		}
122382c30dc4SAdrian Chadd 		return 1;
122482c30dc4SAdrian Chadd 	} else {
122582c30dc4SAdrian Chadd 		/*
122682c30dc4SAdrian Chadd 		 * The value returned here from a MIMO radio is presumed to be
122782c30dc4SAdrian Chadd 		 * "good enough" as a NF calculation. As RSSI values are calculated
122882c30dc4SAdrian Chadd 		 * against this, an adjusted NF may be higher than the RSSI value
122982c30dc4SAdrian Chadd 		 * returned from a vary weak station, resulting in an obscenely
123082c30dc4SAdrian Chadd 		 * high signal strength calculation being returned.
123182c30dc4SAdrian Chadd 		 *
123282c30dc4SAdrian Chadd 		 * This should be re-evaluated at a later date, along with any
123382c30dc4SAdrian Chadd 		 * signal strength calculations which are made. Quite likely the
123482c30dc4SAdrian Chadd 		 * RSSI values will need to be adjusted to ensure the calculations
123582c30dc4SAdrian Chadd 		 * don't "wrap" when RSSI is less than the "adjusted" NF value.
123682c30dc4SAdrian Chadd 		 * ("Adjust" here is via ichan->noiseFloorAdjust.)
123782c30dc4SAdrian Chadd 		 */
1238d94f2d7fSAdrian Chadd 		for (i = 0; i < AH_MAX_CHAINS; i++) {
123982c30dc4SAdrian Chadd 			nf_ctl[i] = ichan->noiseFloorCtl[i] + ath_hal_getNfAdjust(ah, ichan);
124082c30dc4SAdrian Chadd 			nf_ext[i] = ichan->noiseFloorExt[i] + ath_hal_getNfAdjust(ah, ichan);
124182c30dc4SAdrian Chadd 		}
124282c30dc4SAdrian Chadd 		return 1;
124382c30dc4SAdrian Chadd 	}
124482c30dc4SAdrian Chadd }
124582c30dc4SAdrian Chadd 
124682c30dc4SAdrian Chadd /*
124714779705SSam Leffler  * Process all valid raw noise floors into the dBm noise floor values.
124814779705SSam Leffler  * Though our device has no reference for a dBm noise floor, we perform
124914779705SSam Leffler  * a relative minimization of NF's based on the lowest NF found across a
125014779705SSam Leffler  * channel scan.
125114779705SSam Leffler  */
125214779705SSam Leffler void
ath_hal_process_noisefloor(struct ath_hal * ah)125314779705SSam Leffler ath_hal_process_noisefloor(struct ath_hal *ah)
125414779705SSam Leffler {
125514779705SSam Leffler 	HAL_CHANNEL_INTERNAL *c;
125614779705SSam Leffler 	int16_t correct2, correct5;
125714779705SSam Leffler 	int16_t lowest2, lowest5;
125814779705SSam Leffler 	int i;
125914779705SSam Leffler 
126014779705SSam Leffler 	/*
126114779705SSam Leffler 	 * Find the lowest 2GHz and 5GHz noise floor values after adjusting
126214779705SSam Leffler 	 * for statistically recorded NF/channel deviation.
126314779705SSam Leffler 	 */
126414779705SSam Leffler 	correct2 = lowest2 = 0;
126514779705SSam Leffler 	correct5 = lowest5 = 0;
126614779705SSam Leffler 	for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) {
126714779705SSam Leffler 		WIRELESS_MODE mode;
126814779705SSam Leffler 		int16_t nf;
126914779705SSam Leffler 
127014779705SSam Leffler 		c = &AH_PRIVATE(ah)->ah_channels[i];
127114779705SSam Leffler 		if (c->rawNoiseFloor >= 0)
127214779705SSam Leffler 			continue;
127359efa8b5SSam Leffler 		/* XXX can't identify proper mode */
127459efa8b5SSam Leffler 		mode = IS_CHAN_5GHZ(c) ? WIRELESS_MODE_11a : WIRELESS_MODE_11g;
127514779705SSam Leffler 		nf = c->rawNoiseFloor + NOISE_FLOOR[mode] +
127614779705SSam Leffler 			ath_hal_getNfAdjust(ah, c);
127714779705SSam Leffler 		if (IS_CHAN_5GHZ(c)) {
127814779705SSam Leffler 			if (nf < lowest5) {
127914779705SSam Leffler 				lowest5 = nf;
128014779705SSam Leffler 				correct5 = NOISE_FLOOR[mode] -
128114779705SSam Leffler 				    (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c));
128214779705SSam Leffler 			}
128314779705SSam Leffler 		} else {
128414779705SSam Leffler 			if (nf < lowest2) {
128514779705SSam Leffler 				lowest2 = nf;
128614779705SSam Leffler 				correct2 = NOISE_FLOOR[mode] -
128714779705SSam Leffler 				    (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c));
128814779705SSam Leffler 			}
128914779705SSam Leffler 		}
129014779705SSam Leffler 	}
129114779705SSam Leffler 
129214779705SSam Leffler 	/* Correct the channels to reach the expected NF value */
129314779705SSam Leffler 	for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) {
129414779705SSam Leffler 		c = &AH_PRIVATE(ah)->ah_channels[i];
129514779705SSam Leffler 		if (c->rawNoiseFloor >= 0)
129614779705SSam Leffler 			continue;
129714779705SSam Leffler 		/* Apply correction factor */
129814779705SSam Leffler 		c->noiseFloorAdjust = ath_hal_getNfAdjust(ah, c) +
129914779705SSam Leffler 			(IS_CHAN_5GHZ(c) ? correct5 : correct2);
130059efa8b5SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_NFCAL, "%u raw nf %d adjust %d\n",
130159efa8b5SSam Leffler 		    c->channel, c->rawNoiseFloor, c->noiseFloorAdjust);
130214779705SSam Leffler 	}
130314779705SSam Leffler }
130414779705SSam Leffler 
130514779705SSam Leffler /*
130614779705SSam Leffler  * INI support routines.
130714779705SSam Leffler  */
130814779705SSam Leffler 
130914779705SSam Leffler int
ath_hal_ini_write(struct ath_hal * ah,const HAL_INI_ARRAY * ia,int col,int regWr)131014779705SSam Leffler ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia,
131114779705SSam Leffler 	int col, int regWr)
131214779705SSam Leffler {
131314779705SSam Leffler 	int r;
131414779705SSam Leffler 
1315d9bde686SSam Leffler 	HALASSERT(col < ia->cols);
131614779705SSam Leffler 	for (r = 0; r < ia->rows; r++) {
131714779705SSam Leffler 		OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0),
131814779705SSam Leffler 		    HAL_INI_VAL(ia, r, col));
13196ef699a0SAdrian Chadd 
13206ef699a0SAdrian Chadd 		/* Analog shift register delay seems needed for Merlin - PR kern/154220 */
1321cd50bf42SAdrian Chadd 		if (HAL_INI_VAL(ia, r, 0) >= 0x7800 && HAL_INI_VAL(ia, r, 0) < 0x7900)
13226ef699a0SAdrian Chadd 			OS_DELAY(100);
13236ef699a0SAdrian Chadd 
132414779705SSam Leffler 		DMA_YIELD(regWr);
132514779705SSam Leffler 	}
132614779705SSam Leffler 	return regWr;
132714779705SSam Leffler }
132814779705SSam Leffler 
132914779705SSam Leffler void
ath_hal_ini_bank_setup(uint32_t data[],const HAL_INI_ARRAY * ia,int col)133014779705SSam Leffler ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col)
133114779705SSam Leffler {
133214779705SSam Leffler 	int r;
133314779705SSam Leffler 
1334d9bde686SSam Leffler 	HALASSERT(col < ia->cols);
133514779705SSam Leffler 	for (r = 0; r < ia->rows; r++)
133614779705SSam Leffler 		data[r] = HAL_INI_VAL(ia, r, col);
133714779705SSam Leffler }
133814779705SSam Leffler 
133914779705SSam Leffler int
ath_hal_ini_bank_write(struct ath_hal * ah,const HAL_INI_ARRAY * ia,const uint32_t data[],int regWr)134014779705SSam Leffler ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia,
134114779705SSam Leffler 	const uint32_t data[], int regWr)
134214779705SSam Leffler {
134314779705SSam Leffler 	int r;
134414779705SSam Leffler 
134514779705SSam Leffler 	for (r = 0; r < ia->rows; r++) {
134614779705SSam Leffler 		OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), data[r]);
134714779705SSam Leffler 		DMA_YIELD(regWr);
134814779705SSam Leffler 	}
134914779705SSam Leffler 	return regWr;
135014779705SSam Leffler }
13516ff1b2bdSAdrian Chadd 
13526ff1b2bdSAdrian Chadd /*
13536ff1b2bdSAdrian Chadd  * These are EEPROM board related routines which should likely live in
13546ff1b2bdSAdrian Chadd  * a helper library of some sort.
13556ff1b2bdSAdrian Chadd  */
13566ff1b2bdSAdrian Chadd 
13576ff1b2bdSAdrian Chadd /**************************************************************
13586ff1b2bdSAdrian Chadd  * ath_ee_getLowerUppderIndex
13596ff1b2bdSAdrian Chadd  *
13606ff1b2bdSAdrian Chadd  * Return indices surrounding the value in sorted integer lists.
13616ff1b2bdSAdrian Chadd  * Requirement: the input list must be monotonically increasing
13626ff1b2bdSAdrian Chadd  *     and populated up to the list size
13636ff1b2bdSAdrian Chadd  * Returns: match is set if an index in the array matches exactly
13646ff1b2bdSAdrian Chadd  *     or a the target is before or after the range of the array.
13656ff1b2bdSAdrian Chadd  */
13666ff1b2bdSAdrian Chadd HAL_BOOL
ath_ee_getLowerUpperIndex(uint8_t target,uint8_t * pList,uint16_t listSize,uint16_t * indexL,uint16_t * indexR)13676ff1b2bdSAdrian Chadd ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize,
13686ff1b2bdSAdrian Chadd                    uint16_t *indexL, uint16_t *indexR)
13696ff1b2bdSAdrian Chadd {
13706ff1b2bdSAdrian Chadd     uint16_t i;
13716ff1b2bdSAdrian Chadd 
13726ff1b2bdSAdrian Chadd     /*
13736ff1b2bdSAdrian Chadd      * Check first and last elements for beyond ordered array cases.
13746ff1b2bdSAdrian Chadd      */
13756ff1b2bdSAdrian Chadd     if (target <= pList[0]) {
13766ff1b2bdSAdrian Chadd         *indexL = *indexR = 0;
13776ff1b2bdSAdrian Chadd         return AH_TRUE;
13786ff1b2bdSAdrian Chadd     }
13796ff1b2bdSAdrian Chadd     if (target >= pList[listSize-1]) {
13806ff1b2bdSAdrian Chadd         *indexL = *indexR = (uint16_t)(listSize - 1);
13816ff1b2bdSAdrian Chadd         return AH_TRUE;
13826ff1b2bdSAdrian Chadd     }
13836ff1b2bdSAdrian Chadd 
13846ff1b2bdSAdrian Chadd     /* look for value being near or between 2 values in list */
13856ff1b2bdSAdrian Chadd     for (i = 0; i < listSize - 1; i++) {
13866ff1b2bdSAdrian Chadd         /*
13876ff1b2bdSAdrian Chadd          * If value is close to the current value of the list
13886ff1b2bdSAdrian Chadd          * then target is not between values, it is one of the values
13896ff1b2bdSAdrian Chadd          */
13906ff1b2bdSAdrian Chadd         if (pList[i] == target) {
13916ff1b2bdSAdrian Chadd             *indexL = *indexR = i;
13926ff1b2bdSAdrian Chadd             return AH_TRUE;
13936ff1b2bdSAdrian Chadd         }
13946ff1b2bdSAdrian Chadd         /*
13956ff1b2bdSAdrian Chadd          * Look for value being between current value and next value
13966ff1b2bdSAdrian Chadd          * if so return these 2 values
13976ff1b2bdSAdrian Chadd          */
13986ff1b2bdSAdrian Chadd         if (target < pList[i + 1]) {
13996ff1b2bdSAdrian Chadd             *indexL = i;
14006ff1b2bdSAdrian Chadd             *indexR = (uint16_t)(i + 1);
14016ff1b2bdSAdrian Chadd             return AH_FALSE;
14026ff1b2bdSAdrian Chadd         }
14036ff1b2bdSAdrian Chadd     }
14046ff1b2bdSAdrian Chadd     HALASSERT(0);
14056ff1b2bdSAdrian Chadd     *indexL = *indexR = 0;
14066ff1b2bdSAdrian Chadd     return AH_FALSE;
14076ff1b2bdSAdrian Chadd }
14086ff1b2bdSAdrian Chadd 
14096ff1b2bdSAdrian Chadd /**************************************************************
14106ff1b2bdSAdrian Chadd  * ath_ee_FillVpdTable
14116ff1b2bdSAdrian Chadd  *
14126ff1b2bdSAdrian Chadd  * Fill the Vpdlist for indices Pmax-Pmin
14136ff1b2bdSAdrian Chadd  * Note: pwrMin, pwrMax and Vpdlist are all in dBm * 4
14146ff1b2bdSAdrian Chadd  */
14156ff1b2bdSAdrian Chadd HAL_BOOL
ath_ee_FillVpdTable(uint8_t pwrMin,uint8_t pwrMax,uint8_t * pPwrList,uint8_t * pVpdList,uint16_t numIntercepts,uint8_t * pRetVpdList)14166ff1b2bdSAdrian Chadd ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList,
14176ff1b2bdSAdrian Chadd                    uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList)
14186ff1b2bdSAdrian Chadd {
14196ff1b2bdSAdrian Chadd     uint16_t  i, k;
14206ff1b2bdSAdrian Chadd     uint8_t   currPwr = pwrMin;
14216ff1b2bdSAdrian Chadd     uint16_t  idxL, idxR;
14226ff1b2bdSAdrian Chadd 
14236ff1b2bdSAdrian Chadd     HALASSERT(pwrMax > pwrMin);
14246ff1b2bdSAdrian Chadd     for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
14256ff1b2bdSAdrian Chadd         ath_ee_getLowerUpperIndex(currPwr, pPwrList, numIntercepts,
14266ff1b2bdSAdrian Chadd                            &(idxL), &(idxR));
14276ff1b2bdSAdrian Chadd         if (idxR < 1)
14286ff1b2bdSAdrian Chadd             idxR = 1;           /* extrapolate below */
14296ff1b2bdSAdrian Chadd         if (idxL == numIntercepts - 1)
14306ff1b2bdSAdrian Chadd             idxL = (uint16_t)(numIntercepts - 2);   /* extrapolate above */
14316ff1b2bdSAdrian Chadd         if (pPwrList[idxL] == pPwrList[idxR])
14326ff1b2bdSAdrian Chadd             k = pVpdList[idxL];
14336ff1b2bdSAdrian Chadd         else
14346ff1b2bdSAdrian Chadd             k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) /
14356ff1b2bdSAdrian Chadd                   (pPwrList[idxR] - pPwrList[idxL]) );
14366ff1b2bdSAdrian Chadd         HALASSERT(k < 256);
14376ff1b2bdSAdrian Chadd         pRetVpdList[i] = (uint8_t)k;
14386ff1b2bdSAdrian Chadd         currPwr += 2;               /* half dB steps */
14396ff1b2bdSAdrian Chadd     }
14406ff1b2bdSAdrian Chadd 
14416ff1b2bdSAdrian Chadd     return AH_TRUE;
14426ff1b2bdSAdrian Chadd }
14436ff1b2bdSAdrian Chadd 
14446ff1b2bdSAdrian Chadd /**************************************************************************
14456ff1b2bdSAdrian Chadd  * ath_ee_interpolate
14466ff1b2bdSAdrian Chadd  *
14476ff1b2bdSAdrian Chadd  * Returns signed interpolated or the scaled up interpolated value
14486ff1b2bdSAdrian Chadd  */
14496ff1b2bdSAdrian Chadd int16_t
ath_ee_interpolate(uint16_t target,uint16_t srcLeft,uint16_t srcRight,int16_t targetLeft,int16_t targetRight)14506ff1b2bdSAdrian Chadd ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight,
14516ff1b2bdSAdrian Chadd             int16_t targetLeft, int16_t targetRight)
14526ff1b2bdSAdrian Chadd {
14536ff1b2bdSAdrian Chadd     int16_t rv;
14546ff1b2bdSAdrian Chadd 
14556ff1b2bdSAdrian Chadd     if (srcRight == srcLeft) {
14566ff1b2bdSAdrian Chadd         rv = targetLeft;
14576ff1b2bdSAdrian Chadd     } else {
14586ff1b2bdSAdrian Chadd         rv = (int16_t)( ((target - srcLeft) * targetRight +
14596ff1b2bdSAdrian Chadd               (srcRight - target) * targetLeft) / (srcRight - srcLeft) );
14606ff1b2bdSAdrian Chadd     }
14616ff1b2bdSAdrian Chadd     return rv;
14626ff1b2bdSAdrian Chadd }
1463fc4de9b7SAdrian Chadd 
1464fc4de9b7SAdrian Chadd /*
1465fc4de9b7SAdrian Chadd  * Adjust the TSF.
1466fc4de9b7SAdrian Chadd  */
1467fc4de9b7SAdrian Chadd void
ath_hal_adjusttsf(struct ath_hal * ah,int32_t tsfdelta)1468fc4de9b7SAdrian Chadd ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta)
1469fc4de9b7SAdrian Chadd {
1470fc4de9b7SAdrian Chadd 	/* XXX handle wrap/overflow */
1471fc4de9b7SAdrian Chadd 	OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta);
1472fc4de9b7SAdrian Chadd }
1473fc4de9b7SAdrian Chadd 
1474fc4de9b7SAdrian Chadd /*
1475fc4de9b7SAdrian Chadd  * Enable or disable CCA.
1476fc4de9b7SAdrian Chadd  */
1477fc4de9b7SAdrian Chadd void
ath_hal_setcca(struct ath_hal * ah,int ena)1478fc4de9b7SAdrian Chadd ath_hal_setcca(struct ath_hal *ah, int ena)
1479fc4de9b7SAdrian Chadd {
1480fc4de9b7SAdrian Chadd 	/*
1481fc4de9b7SAdrian Chadd 	 * NB: fill me in; this is not provided by default because disabling
1482fc4de9b7SAdrian Chadd 	 *     CCA in most locales violates regulatory.
1483fc4de9b7SAdrian Chadd 	 */
1484fc4de9b7SAdrian Chadd }
1485fc4de9b7SAdrian Chadd 
1486fc4de9b7SAdrian Chadd /*
1487fc4de9b7SAdrian Chadd  * Get CCA setting.
14883557b26aSAdrian Chadd  *
14893557b26aSAdrian Chadd  * XXX TODO: turn this and the above function into methods
14903557b26aSAdrian Chadd  * in case there are chipset differences in handling CCA.
1491fc4de9b7SAdrian Chadd  */
1492fc4de9b7SAdrian Chadd int
ath_hal_getcca(struct ath_hal * ah)1493fc4de9b7SAdrian Chadd ath_hal_getcca(struct ath_hal *ah)
1494fc4de9b7SAdrian Chadd {
1495fc4de9b7SAdrian Chadd 	u_int32_t diag;
1496fc4de9b7SAdrian Chadd 	if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK)
1497fc4de9b7SAdrian Chadd 		return 1;
1498fc4de9b7SAdrian Chadd 	return ((diag & 0x500000) == 0);
1499fc4de9b7SAdrian Chadd }
1500a86e181bSAdrian Chadd 
1501a86e181bSAdrian Chadd /*
15022bc158cfSAdrian Chadd  * Set the current state of self-generated ACK and RTS/CTS frames.
15032bc158cfSAdrian Chadd  *
15042bc158cfSAdrian Chadd  * For correct DFS operation, the device should not even /ACK/ frames
15052bc158cfSAdrian Chadd  * that are sent to it during CAC or CSA.
15062bc158cfSAdrian Chadd  */
15072bc158cfSAdrian Chadd void
ath_hal_set_dfs_cac_tx_quiet(struct ath_hal * ah,HAL_BOOL ena)15082bc158cfSAdrian Chadd ath_hal_set_dfs_cac_tx_quiet(struct ath_hal *ah, HAL_BOOL ena)
15092bc158cfSAdrian Chadd {
15102bc158cfSAdrian Chadd 
15112bc158cfSAdrian Chadd 	if (ah->ah_setDfsCacTxQuiet == NULL)
15122bc158cfSAdrian Chadd 		return;
15132bc158cfSAdrian Chadd 	ah->ah_setDfsCacTxQuiet(ah, ena);
15142bc158cfSAdrian Chadd }
15152bc158cfSAdrian Chadd 
15162bc158cfSAdrian Chadd /*
1517a86e181bSAdrian Chadd  * This routine is only needed when supporting EEPROM-in-RAM setups
1518a86e181bSAdrian Chadd  * (eg embedded SoCs and on-board PCI/PCIe devices.)
1519a86e181bSAdrian Chadd  */
1520a86e181bSAdrian Chadd /* NB: This is in 16 bit words; not bytes */
1521a86e181bSAdrian Chadd /* XXX This doesn't belong here!  */
1522a86e181bSAdrian Chadd #define ATH_DATA_EEPROM_SIZE    2048
1523a86e181bSAdrian Chadd 
1524a86e181bSAdrian Chadd HAL_BOOL
ath_hal_EepromDataRead(struct ath_hal * ah,u_int off,uint16_t * data)1525a86e181bSAdrian Chadd ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data)
1526a86e181bSAdrian Chadd {
1527a86e181bSAdrian Chadd 	if (ah->ah_eepromdata == AH_NULL) {
1528a86e181bSAdrian Chadd 		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__);
1529a86e181bSAdrian Chadd 		return AH_FALSE;
1530a86e181bSAdrian Chadd 	}
1531a86e181bSAdrian Chadd 	if (off > ATH_DATA_EEPROM_SIZE) {
1532a86e181bSAdrian Chadd 		HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset %x > %x\n",
1533a86e181bSAdrian Chadd 		    __func__, off, ATH_DATA_EEPROM_SIZE);
1534a86e181bSAdrian Chadd 		return AH_FALSE;
1535a86e181bSAdrian Chadd 	}
1536a86e181bSAdrian Chadd 	(*data) = ah->ah_eepromdata[off];
1537a86e181bSAdrian Chadd 	return AH_TRUE;
1538a86e181bSAdrian Chadd }
153903f26656SAdrian Chadd 
154003f26656SAdrian Chadd /*
154103f26656SAdrian Chadd  * Do a 2GHz specific MHz->IEEE based on the hardware
154203f26656SAdrian Chadd  * frequency.
154303f26656SAdrian Chadd  *
154403f26656SAdrian Chadd  * This is the unmapped frequency which is programmed into the hardware.
154503f26656SAdrian Chadd  */
154603f26656SAdrian Chadd int
ath_hal_mhz2ieee_2ghz(struct ath_hal * ah,int freq)1547ff066b54SAdrian Chadd ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, int freq)
154803f26656SAdrian Chadd {
154903f26656SAdrian Chadd 
1550ff066b54SAdrian Chadd 	if (freq == 2484)
155103f26656SAdrian Chadd 		return 14;
1552ff066b54SAdrian Chadd 	if (freq < 2484)
1553ff066b54SAdrian Chadd 		return ((int) freq - 2407) / 5;
155403f26656SAdrian Chadd 	else
1555ff066b54SAdrian Chadd 		return 15 + ((freq - 2512) / 20);
155603f26656SAdrian Chadd }
1557b0602becSAdrian Chadd 
1558b0602becSAdrian Chadd /*
1559b0602becSAdrian Chadd  * Clear the current survey data.
1560b0602becSAdrian Chadd  *
1561b0602becSAdrian Chadd  * This should be done during a channel change.
1562b0602becSAdrian Chadd  */
1563b0602becSAdrian Chadd void
ath_hal_survey_clear(struct ath_hal * ah)1564b0602becSAdrian Chadd ath_hal_survey_clear(struct ath_hal *ah)
1565b0602becSAdrian Chadd {
1566b0602becSAdrian Chadd 
1567b0602becSAdrian Chadd 	OS_MEMZERO(&AH_PRIVATE(ah)->ah_chansurvey,
1568b0602becSAdrian Chadd 	    sizeof(AH_PRIVATE(ah)->ah_chansurvey));
1569b0602becSAdrian Chadd }
1570b0602becSAdrian Chadd 
1571b0602becSAdrian Chadd /*
1572b0602becSAdrian Chadd  * Add a sample to the channel survey.
1573b0602becSAdrian Chadd  */
1574b0602becSAdrian Chadd void
ath_hal_survey_add_sample(struct ath_hal * ah,HAL_SURVEY_SAMPLE * hs)1575b0602becSAdrian Chadd ath_hal_survey_add_sample(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs)
1576b0602becSAdrian Chadd {
1577b0602becSAdrian Chadd 	HAL_CHANNEL_SURVEY *cs;
1578b0602becSAdrian Chadd 
1579b0602becSAdrian Chadd 	cs = &AH_PRIVATE(ah)->ah_chansurvey;
1580b0602becSAdrian Chadd 
1581b0602becSAdrian Chadd 	OS_MEMCPY(&cs->samples[cs->cur_sample], hs, sizeof(*hs));
1582b0602becSAdrian Chadd 	cs->samples[cs->cur_sample].seq_num = cs->cur_seq;
1583b0602becSAdrian Chadd 	cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT;
1584b0602becSAdrian Chadd 	cs->cur_seq++;
1585b0602becSAdrian Chadd }
1586