1572ff6f6SMatthew Dillon /*
2572ff6f6SMatthew Dillon * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3572ff6f6SMatthew Dillon * Copyright (c) 2002-2008 Atheros Communications, Inc.
4572ff6f6SMatthew Dillon *
5572ff6f6SMatthew Dillon * Permission to use, copy, modify, and/or distribute this software for any
6572ff6f6SMatthew Dillon * purpose with or without fee is hereby granted, provided that the above
7572ff6f6SMatthew Dillon * copyright notice and this permission notice appear in all copies.
8572ff6f6SMatthew Dillon *
9572ff6f6SMatthew Dillon * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10572ff6f6SMatthew Dillon * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11572ff6f6SMatthew Dillon * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12572ff6f6SMatthew Dillon * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13572ff6f6SMatthew Dillon * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14572ff6f6SMatthew Dillon * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15572ff6f6SMatthew Dillon * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16572ff6f6SMatthew Dillon *
17572ff6f6SMatthew Dillon * $FreeBSD$
18572ff6f6SMatthew Dillon */
19572ff6f6SMatthew Dillon #include "opt_ah.h"
20572ff6f6SMatthew Dillon
21572ff6f6SMatthew Dillon #include "ah.h"
22572ff6f6SMatthew Dillon #include "ah_internal.h"
23572ff6f6SMatthew Dillon #include "ah_devid.h"
24572ff6f6SMatthew Dillon #include "ah_eeprom.h" /* for 5ghz fast clock flag */
25572ff6f6SMatthew Dillon
26572ff6f6SMatthew Dillon #include "ar5416/ar5416reg.h" /* NB: includes ar5212reg.h */
27572ff6f6SMatthew Dillon #include "ar9003/ar9300_devid.h"
28572ff6f6SMatthew Dillon
29572ff6f6SMatthew Dillon /* linker set of registered chips */
30572ff6f6SMatthew Dillon OS_SET_DECLARE(ah_chips, struct ath_hal_chip);
31572ff6f6SMatthew Dillon
32572ff6f6SMatthew Dillon /*
33572ff6f6SMatthew Dillon * Check the set of registered chips to see if any recognize
34572ff6f6SMatthew Dillon * the device as one they can support.
35572ff6f6SMatthew Dillon */
36572ff6f6SMatthew Dillon const char*
ath_hal_probe(uint16_t vendorid,uint16_t devid)37572ff6f6SMatthew Dillon ath_hal_probe(uint16_t vendorid, uint16_t devid)
38572ff6f6SMatthew Dillon {
39572ff6f6SMatthew Dillon struct ath_hal_chip * const *pchip;
40572ff6f6SMatthew Dillon
41572ff6f6SMatthew Dillon OS_SET_FOREACH(pchip, ah_chips) {
42572ff6f6SMatthew Dillon const char *name = (*pchip)->probe(vendorid, devid);
43572ff6f6SMatthew Dillon if (name != AH_NULL)
44572ff6f6SMatthew Dillon return name;
45572ff6f6SMatthew Dillon }
46572ff6f6SMatthew Dillon return AH_NULL;
47572ff6f6SMatthew Dillon }
48572ff6f6SMatthew Dillon
49572ff6f6SMatthew Dillon /*
50572ff6f6SMatthew Dillon * Attach detects device chip revisions, initializes the hwLayer
51572ff6f6SMatthew Dillon * function list, reads EEPROM information,
52572ff6f6SMatthew Dillon * selects reset vectors, and performs a short self test.
53572ff6f6SMatthew Dillon * Any failures will return an error that should cause a hardware
54572ff6f6SMatthew Dillon * disable.
55572ff6f6SMatthew Dillon */
56572ff6f6SMatthew Dillon 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)57572ff6f6SMatthew Dillon ath_hal_attach(uint16_t devid, HAL_SOFTC sc,
58848b370cSMatthew Dillon HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata,
59848b370cSMatthew Dillon HAL_OPS_CONFIG *ah_config,
60848b370cSMatthew Dillon HAL_STATUS *error)
61572ff6f6SMatthew Dillon {
62572ff6f6SMatthew Dillon struct ath_hal_chip * const *pchip;
63572ff6f6SMatthew Dillon
64572ff6f6SMatthew Dillon OS_SET_FOREACH(pchip, ah_chips) {
65572ff6f6SMatthew Dillon struct ath_hal_chip *chip = *pchip;
66572ff6f6SMatthew Dillon struct ath_hal *ah;
67572ff6f6SMatthew Dillon
68572ff6f6SMatthew Dillon /* XXX don't have vendorid, assume atheros one works */
69572ff6f6SMatthew Dillon if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL)
70572ff6f6SMatthew Dillon continue;
71848b370cSMatthew Dillon ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config,
72848b370cSMatthew Dillon error);
73572ff6f6SMatthew Dillon if (ah != AH_NULL) {
74572ff6f6SMatthew Dillon /* copy back private state to public area */
75572ff6f6SMatthew Dillon ah->ah_devid = AH_PRIVATE(ah)->ah_devid;
76572ff6f6SMatthew Dillon ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid;
77572ff6f6SMatthew Dillon ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion;
78572ff6f6SMatthew Dillon ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev;
79572ff6f6SMatthew Dillon ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev;
80572ff6f6SMatthew Dillon ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev;
81572ff6f6SMatthew Dillon ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev;
82572ff6f6SMatthew Dillon return ah;
83572ff6f6SMatthew Dillon }
84572ff6f6SMatthew Dillon }
85572ff6f6SMatthew Dillon return AH_NULL;
86572ff6f6SMatthew Dillon }
87572ff6f6SMatthew Dillon
88572ff6f6SMatthew Dillon const char *
ath_hal_mac_name(struct ath_hal * ah)89572ff6f6SMatthew Dillon ath_hal_mac_name(struct ath_hal *ah)
90572ff6f6SMatthew Dillon {
91572ff6f6SMatthew Dillon switch (ah->ah_macVersion) {
92572ff6f6SMatthew Dillon case AR_SREV_VERSION_CRETE:
93572ff6f6SMatthew Dillon case AR_SREV_VERSION_MAUI_1:
94*b14ca477SMatthew Dillon return "AR5210";
95572ff6f6SMatthew Dillon case AR_SREV_VERSION_MAUI_2:
96572ff6f6SMatthew Dillon case AR_SREV_VERSION_OAHU:
97*b14ca477SMatthew Dillon return "AR5211";
98572ff6f6SMatthew Dillon case AR_SREV_VERSION_VENICE:
99*b14ca477SMatthew Dillon return "AR5212";
100572ff6f6SMatthew Dillon case AR_SREV_VERSION_GRIFFIN:
101*b14ca477SMatthew Dillon return "AR2413";
102572ff6f6SMatthew Dillon case AR_SREV_VERSION_CONDOR:
103*b14ca477SMatthew Dillon return "AR5424";
104572ff6f6SMatthew Dillon case AR_SREV_VERSION_EAGLE:
105*b14ca477SMatthew Dillon return "AR5413";
106572ff6f6SMatthew Dillon case AR_SREV_VERSION_COBRA:
107*b14ca477SMatthew Dillon return "AR2415";
108572ff6f6SMatthew Dillon case AR_SREV_2425: /* Swan */
109*b14ca477SMatthew Dillon return "AR2425";
110572ff6f6SMatthew Dillon case AR_SREV_2417: /* Nala */
111*b14ca477SMatthew Dillon return "AR2417";
112572ff6f6SMatthew Dillon case AR_XSREV_VERSION_OWL_PCI:
113*b14ca477SMatthew Dillon return "AR5416";
114572ff6f6SMatthew Dillon case AR_XSREV_VERSION_OWL_PCIE:
115*b14ca477SMatthew Dillon return "AR5418";
116572ff6f6SMatthew Dillon case AR_XSREV_VERSION_HOWL:
117*b14ca477SMatthew Dillon return "AR9130";
118572ff6f6SMatthew Dillon case AR_XSREV_VERSION_SOWL:
119*b14ca477SMatthew Dillon return "AR9160";
120572ff6f6SMatthew Dillon case AR_XSREV_VERSION_MERLIN:
121572ff6f6SMatthew Dillon if (AH_PRIVATE(ah)->ah_ispcie)
122*b14ca477SMatthew Dillon return "AR9280";
123*b14ca477SMatthew Dillon return "AR9220";
124572ff6f6SMatthew Dillon case AR_XSREV_VERSION_KITE:
125*b14ca477SMatthew Dillon return "AR9285";
126572ff6f6SMatthew Dillon case AR_XSREV_VERSION_KIWI:
127572ff6f6SMatthew Dillon if (AH_PRIVATE(ah)->ah_ispcie)
128*b14ca477SMatthew Dillon return "AR9287";
129*b14ca477SMatthew Dillon return "AR9227";
130572ff6f6SMatthew Dillon case AR_SREV_VERSION_AR9380:
131572ff6f6SMatthew Dillon if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10)
132*b14ca477SMatthew Dillon return "AR9580";
133*b14ca477SMatthew Dillon return "AR9380";
134572ff6f6SMatthew Dillon case AR_SREV_VERSION_AR9460:
135*b14ca477SMatthew Dillon return "AR9460";
136572ff6f6SMatthew Dillon case AR_SREV_VERSION_AR9330:
137*b14ca477SMatthew Dillon return "AR9330";
138572ff6f6SMatthew Dillon case AR_SREV_VERSION_AR9340:
139*b14ca477SMatthew Dillon return "AR9340";
140572ff6f6SMatthew Dillon case AR_SREV_VERSION_QCA9550:
141*b14ca477SMatthew Dillon return "QCA9550";
142572ff6f6SMatthew Dillon case AR_SREV_VERSION_AR9485:
143*b14ca477SMatthew Dillon return "AR9485";
144572ff6f6SMatthew Dillon case AR_SREV_VERSION_QCA9565:
145*b14ca477SMatthew Dillon return "QCA9565";
146*b14ca477SMatthew Dillon case AR_SREV_VERSION_QCA9530:
147*b14ca477SMatthew Dillon return "QCA9530";
148572ff6f6SMatthew Dillon }
149572ff6f6SMatthew Dillon return "????";
150572ff6f6SMatthew Dillon }
151572ff6f6SMatthew Dillon
152572ff6f6SMatthew Dillon /*
153572ff6f6SMatthew Dillon * Return the mask of available modes based on the hardware capabilities.
154572ff6f6SMatthew Dillon */
155572ff6f6SMatthew Dillon u_int
ath_hal_getwirelessmodes(struct ath_hal * ah)156572ff6f6SMatthew Dillon ath_hal_getwirelessmodes(struct ath_hal*ah)
157572ff6f6SMatthew Dillon {
158572ff6f6SMatthew Dillon return ath_hal_getWirelessModes(ah);
159572ff6f6SMatthew Dillon }
160572ff6f6SMatthew Dillon
161572ff6f6SMatthew Dillon /* linker set of registered RF backends */
162572ff6f6SMatthew Dillon OS_SET_DECLARE(ah_rfs, struct ath_hal_rf);
163572ff6f6SMatthew Dillon
164572ff6f6SMatthew Dillon /*
165572ff6f6SMatthew Dillon * Check the set of registered RF backends to see if
166572ff6f6SMatthew Dillon * any recognize the device as one they can support.
167572ff6f6SMatthew Dillon */
168572ff6f6SMatthew Dillon struct ath_hal_rf *
ath_hal_rfprobe(struct ath_hal * ah,HAL_STATUS * ecode)169572ff6f6SMatthew Dillon ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode)
170572ff6f6SMatthew Dillon {
171572ff6f6SMatthew Dillon struct ath_hal_rf * const *prf;
172572ff6f6SMatthew Dillon
173572ff6f6SMatthew Dillon OS_SET_FOREACH(prf, ah_rfs) {
174572ff6f6SMatthew Dillon struct ath_hal_rf *rf = *prf;
175572ff6f6SMatthew Dillon if (rf->probe(ah))
176572ff6f6SMatthew Dillon return rf;
177572ff6f6SMatthew Dillon }
178572ff6f6SMatthew Dillon *ecode = HAL_ENOTSUPP;
179572ff6f6SMatthew Dillon return AH_NULL;
180572ff6f6SMatthew Dillon }
181572ff6f6SMatthew Dillon
182572ff6f6SMatthew Dillon const char *
ath_hal_rf_name(struct ath_hal * ah)183572ff6f6SMatthew Dillon ath_hal_rf_name(struct ath_hal *ah)
184572ff6f6SMatthew Dillon {
185572ff6f6SMatthew Dillon switch (ah->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) {
186572ff6f6SMatthew Dillon case 0: /* 5210 */
187572ff6f6SMatthew Dillon return "5110"; /* NB: made up */
188572ff6f6SMatthew Dillon case AR_RAD5111_SREV_MAJOR:
189572ff6f6SMatthew Dillon case AR_RAD5111_SREV_PROD:
190572ff6f6SMatthew Dillon return "5111";
191572ff6f6SMatthew Dillon case AR_RAD2111_SREV_MAJOR:
192572ff6f6SMatthew Dillon return "2111";
193572ff6f6SMatthew Dillon case AR_RAD5112_SREV_MAJOR:
194572ff6f6SMatthew Dillon case AR_RAD5112_SREV_2_0:
195572ff6f6SMatthew Dillon case AR_RAD5112_SREV_2_1:
196572ff6f6SMatthew Dillon return "5112";
197572ff6f6SMatthew Dillon case AR_RAD2112_SREV_MAJOR:
198572ff6f6SMatthew Dillon case AR_RAD2112_SREV_2_0:
199572ff6f6SMatthew Dillon case AR_RAD2112_SREV_2_1:
200572ff6f6SMatthew Dillon return "2112";
201572ff6f6SMatthew Dillon case AR_RAD2413_SREV_MAJOR:
202572ff6f6SMatthew Dillon return "2413";
203572ff6f6SMatthew Dillon case AR_RAD5413_SREV_MAJOR:
204572ff6f6SMatthew Dillon return "5413";
205572ff6f6SMatthew Dillon case AR_RAD2316_SREV_MAJOR:
206572ff6f6SMatthew Dillon return "2316";
207572ff6f6SMatthew Dillon case AR_RAD2317_SREV_MAJOR:
208572ff6f6SMatthew Dillon return "2317";
209572ff6f6SMatthew Dillon case AR_RAD5424_SREV_MAJOR:
210572ff6f6SMatthew Dillon return "5424";
211572ff6f6SMatthew Dillon
212572ff6f6SMatthew Dillon case AR_RAD5133_SREV_MAJOR:
213572ff6f6SMatthew Dillon return "5133";
214572ff6f6SMatthew Dillon case AR_RAD2133_SREV_MAJOR:
215572ff6f6SMatthew Dillon return "2133";
216572ff6f6SMatthew Dillon case AR_RAD5122_SREV_MAJOR:
217572ff6f6SMatthew Dillon return "5122";
218572ff6f6SMatthew Dillon case AR_RAD2122_SREV_MAJOR:
219572ff6f6SMatthew Dillon return "2122";
220572ff6f6SMatthew Dillon }
221572ff6f6SMatthew Dillon return "????";
222572ff6f6SMatthew Dillon }
223572ff6f6SMatthew Dillon
224572ff6f6SMatthew Dillon /*
225572ff6f6SMatthew Dillon * Poll the register looking for a specific value.
226572ff6f6SMatthew Dillon */
227572ff6f6SMatthew Dillon HAL_BOOL
ath_hal_wait(struct ath_hal * ah,u_int reg,uint32_t mask,uint32_t val)228572ff6f6SMatthew Dillon ath_hal_wait(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val)
229572ff6f6SMatthew Dillon {
230572ff6f6SMatthew Dillon #define AH_TIMEOUT 1000
231572ff6f6SMatthew Dillon return ath_hal_waitfor(ah, reg, mask, val, AH_TIMEOUT);
232572ff6f6SMatthew Dillon #undef AH_TIMEOUT
233572ff6f6SMatthew Dillon }
234572ff6f6SMatthew Dillon
235572ff6f6SMatthew Dillon HAL_BOOL
ath_hal_waitfor(struct ath_hal * ah,u_int reg,uint32_t mask,uint32_t val,uint32_t timeout)236572ff6f6SMatthew Dillon ath_hal_waitfor(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val, uint32_t timeout)
237572ff6f6SMatthew Dillon {
238572ff6f6SMatthew Dillon int i;
239572ff6f6SMatthew Dillon
240572ff6f6SMatthew Dillon for (i = 0; i < timeout; i++) {
241572ff6f6SMatthew Dillon if ((OS_REG_READ(ah, reg) & mask) == val)
242572ff6f6SMatthew Dillon return AH_TRUE;
243572ff6f6SMatthew Dillon OS_DELAY(10);
244572ff6f6SMatthew Dillon }
245572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_REGIO | HAL_DEBUG_PHYIO,
246572ff6f6SMatthew Dillon "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n",
247572ff6f6SMatthew Dillon __func__, reg, OS_REG_READ(ah, reg), mask, val);
248572ff6f6SMatthew Dillon return AH_FALSE;
249572ff6f6SMatthew Dillon }
250572ff6f6SMatthew Dillon
251572ff6f6SMatthew Dillon /*
252572ff6f6SMatthew Dillon * Reverse the bits starting at the low bit for a value of
253572ff6f6SMatthew Dillon * bit_count in size
254572ff6f6SMatthew Dillon */
255572ff6f6SMatthew Dillon uint32_t
ath_hal_reverseBits(uint32_t val,uint32_t n)256572ff6f6SMatthew Dillon ath_hal_reverseBits(uint32_t val, uint32_t n)
257572ff6f6SMatthew Dillon {
258572ff6f6SMatthew Dillon uint32_t retval;
259572ff6f6SMatthew Dillon int i;
260572ff6f6SMatthew Dillon
261572ff6f6SMatthew Dillon for (i = 0, retval = 0; i < n; i++) {
262572ff6f6SMatthew Dillon retval = (retval << 1) | (val & 1);
263572ff6f6SMatthew Dillon val >>= 1;
264572ff6f6SMatthew Dillon }
265572ff6f6SMatthew Dillon return retval;
266572ff6f6SMatthew Dillon }
267572ff6f6SMatthew Dillon
268572ff6f6SMatthew Dillon /* 802.11n related timing definitions */
269572ff6f6SMatthew Dillon
270572ff6f6SMatthew Dillon #define OFDM_PLCP_BITS 22
271572ff6f6SMatthew Dillon #define HT_L_STF 8
272572ff6f6SMatthew Dillon #define HT_L_LTF 8
273572ff6f6SMatthew Dillon #define HT_L_SIG 4
274572ff6f6SMatthew Dillon #define HT_SIG 8
275572ff6f6SMatthew Dillon #define HT_STF 4
276572ff6f6SMatthew Dillon #define HT_LTF(n) ((n) * 4)
277572ff6f6SMatthew Dillon
278572ff6f6SMatthew Dillon #define HT_RC_2_MCS(_rc) ((_rc) & 0xf)
279572ff6f6SMatthew Dillon #define HT_RC_2_STREAMS(_rc) ((((_rc) & 0x78) >> 3) + 1)
280572ff6f6SMatthew Dillon #define IS_HT_RATE(_rc) ( (_rc) & IEEE80211_RATE_MCS)
281572ff6f6SMatthew Dillon
282572ff6f6SMatthew Dillon /*
283572ff6f6SMatthew Dillon * Calculate the duration of a packet whether it is 11n or legacy.
284572ff6f6SMatthew Dillon */
285572ff6f6SMatthew Dillon 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)286572ff6f6SMatthew Dillon ath_hal_pkt_txtime(struct ath_hal *ah, const HAL_RATE_TABLE *rates, uint32_t frameLen,
287572ff6f6SMatthew Dillon uint16_t rateix, HAL_BOOL isht40, HAL_BOOL shortPreamble)
288572ff6f6SMatthew Dillon {
289572ff6f6SMatthew Dillon uint8_t rc;
290572ff6f6SMatthew Dillon int numStreams;
291572ff6f6SMatthew Dillon
292572ff6f6SMatthew Dillon rc = rates->info[rateix].rateCode;
293572ff6f6SMatthew Dillon
294572ff6f6SMatthew Dillon /* Legacy rate? Return the old way */
295572ff6f6SMatthew Dillon if (! IS_HT_RATE(rc))
296572ff6f6SMatthew Dillon return ath_hal_computetxtime(ah, rates, frameLen, rateix, shortPreamble);
297572ff6f6SMatthew Dillon
298572ff6f6SMatthew Dillon /* 11n frame - extract out the number of spatial streams */
299572ff6f6SMatthew Dillon numStreams = HT_RC_2_STREAMS(rc);
300572ff6f6SMatthew Dillon KASSERT(numStreams > 0 && numStreams <= 4,
301572ff6f6SMatthew Dillon ("number of spatial streams needs to be 1..3: MCS rate 0x%x!",
302572ff6f6SMatthew Dillon rateix));
303572ff6f6SMatthew Dillon
304572ff6f6SMatthew Dillon return ath_computedur_ht(frameLen, rc, numStreams, isht40, shortPreamble);
305572ff6f6SMatthew Dillon }
306572ff6f6SMatthew Dillon
307572ff6f6SMatthew Dillon static const uint16_t ht20_bps[32] = {
308572ff6f6SMatthew Dillon 26, 52, 78, 104, 156, 208, 234, 260,
309572ff6f6SMatthew Dillon 52, 104, 156, 208, 312, 416, 468, 520,
310572ff6f6SMatthew Dillon 78, 156, 234, 312, 468, 624, 702, 780,
311572ff6f6SMatthew Dillon 104, 208, 312, 416, 624, 832, 936, 1040
312572ff6f6SMatthew Dillon };
313572ff6f6SMatthew Dillon static const uint16_t ht40_bps[32] = {
314572ff6f6SMatthew Dillon 54, 108, 162, 216, 324, 432, 486, 540,
315572ff6f6SMatthew Dillon 108, 216, 324, 432, 648, 864, 972, 1080,
316572ff6f6SMatthew Dillon 162, 324, 486, 648, 972, 1296, 1458, 1620,
317572ff6f6SMatthew Dillon 216, 432, 648, 864, 1296, 1728, 1944, 2160
318572ff6f6SMatthew Dillon };
319572ff6f6SMatthew Dillon
320572ff6f6SMatthew Dillon /*
321572ff6f6SMatthew Dillon * Calculate the transmit duration of an 11n frame.
322572ff6f6SMatthew Dillon */
323572ff6f6SMatthew Dillon uint32_t
ath_computedur_ht(uint32_t frameLen,uint16_t rate,int streams,HAL_BOOL isht40,HAL_BOOL isShortGI)324572ff6f6SMatthew Dillon ath_computedur_ht(uint32_t frameLen, uint16_t rate, int streams,
325572ff6f6SMatthew Dillon HAL_BOOL isht40, HAL_BOOL isShortGI)
326572ff6f6SMatthew Dillon {
327572ff6f6SMatthew Dillon uint32_t bitsPerSymbol, numBits, numSymbols, txTime;
328572ff6f6SMatthew Dillon
329572ff6f6SMatthew Dillon KASSERT(rate & IEEE80211_RATE_MCS, ("not mcs %d", rate));
330572ff6f6SMatthew Dillon KASSERT((rate &~ IEEE80211_RATE_MCS) < 31, ("bad mcs 0x%x", rate));
331572ff6f6SMatthew Dillon
332572ff6f6SMatthew Dillon if (isht40)
333572ff6f6SMatthew Dillon bitsPerSymbol = ht40_bps[rate & 0x1f];
334572ff6f6SMatthew Dillon else
335572ff6f6SMatthew Dillon bitsPerSymbol = ht20_bps[rate & 0x1f];
336572ff6f6SMatthew Dillon numBits = OFDM_PLCP_BITS + (frameLen << 3);
337572ff6f6SMatthew Dillon numSymbols = howmany(numBits, bitsPerSymbol);
338572ff6f6SMatthew Dillon if (isShortGI)
339572ff6f6SMatthew Dillon txTime = ((numSymbols * 18) + 4) / 5; /* 3.6us */
340572ff6f6SMatthew Dillon else
341572ff6f6SMatthew Dillon txTime = numSymbols * 4; /* 4us */
342572ff6f6SMatthew Dillon return txTime + HT_L_STF + HT_L_LTF +
343572ff6f6SMatthew Dillon HT_L_SIG + HT_SIG + HT_STF + HT_LTF(streams);
344572ff6f6SMatthew Dillon }
345572ff6f6SMatthew Dillon
346572ff6f6SMatthew Dillon /*
347572ff6f6SMatthew Dillon * Compute the time to transmit a frame of length frameLen bytes
348572ff6f6SMatthew Dillon * using the specified rate, phy, and short preamble setting.
349572ff6f6SMatthew Dillon */
350572ff6f6SMatthew Dillon uint16_t
ath_hal_computetxtime(struct ath_hal * ah,const HAL_RATE_TABLE * rates,uint32_t frameLen,uint16_t rateix,HAL_BOOL shortPreamble)351572ff6f6SMatthew Dillon ath_hal_computetxtime(struct ath_hal *ah,
352572ff6f6SMatthew Dillon const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix,
353572ff6f6SMatthew Dillon HAL_BOOL shortPreamble)
354572ff6f6SMatthew Dillon {
355572ff6f6SMatthew Dillon uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime;
356572ff6f6SMatthew Dillon uint32_t kbps;
357572ff6f6SMatthew Dillon
358572ff6f6SMatthew Dillon /* Warn if this function is called for 11n rates; it should not be! */
359572ff6f6SMatthew Dillon if (IS_HT_RATE(rates->info[rateix].rateCode))
360572ff6f6SMatthew Dillon ath_hal_printf(ah, "%s: MCS rate? (index %d; hwrate 0x%x)\n",
361572ff6f6SMatthew Dillon __func__, rateix, rates->info[rateix].rateCode);
362572ff6f6SMatthew Dillon
363572ff6f6SMatthew Dillon kbps = rates->info[rateix].rateKbps;
364572ff6f6SMatthew Dillon /*
365*b14ca477SMatthew Dillon * index can be invalid during dynamic Turbo transitions.
366572ff6f6SMatthew Dillon * XXX
367572ff6f6SMatthew Dillon */
368572ff6f6SMatthew Dillon if (kbps == 0)
369572ff6f6SMatthew Dillon return 0;
370572ff6f6SMatthew Dillon switch (rates->info[rateix].phy) {
371572ff6f6SMatthew Dillon case IEEE80211_T_CCK:
372572ff6f6SMatthew Dillon phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
373572ff6f6SMatthew Dillon if (shortPreamble && rates->info[rateix].shortPreamble)
374572ff6f6SMatthew Dillon phyTime >>= 1;
375572ff6f6SMatthew Dillon numBits = frameLen << 3;
376572ff6f6SMatthew Dillon txTime = CCK_SIFS_TIME + phyTime
377572ff6f6SMatthew Dillon + ((numBits * 1000)/kbps);
378572ff6f6SMatthew Dillon break;
379572ff6f6SMatthew Dillon case IEEE80211_T_OFDM:
380572ff6f6SMatthew Dillon bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000;
381572ff6f6SMatthew Dillon HALASSERT(bitsPerSymbol != 0);
382572ff6f6SMatthew Dillon
383572ff6f6SMatthew Dillon numBits = OFDM_PLCP_BITS + (frameLen << 3);
384572ff6f6SMatthew Dillon numSymbols = howmany(numBits, bitsPerSymbol);
385572ff6f6SMatthew Dillon txTime = OFDM_SIFS_TIME
386572ff6f6SMatthew Dillon + OFDM_PREAMBLE_TIME
387572ff6f6SMatthew Dillon + (numSymbols * OFDM_SYMBOL_TIME);
388572ff6f6SMatthew Dillon break;
389572ff6f6SMatthew Dillon case IEEE80211_T_OFDM_HALF:
390572ff6f6SMatthew Dillon bitsPerSymbol = (kbps * OFDM_HALF_SYMBOL_TIME) / 1000;
391572ff6f6SMatthew Dillon HALASSERT(bitsPerSymbol != 0);
392572ff6f6SMatthew Dillon
393572ff6f6SMatthew Dillon numBits = OFDM_HALF_PLCP_BITS + (frameLen << 3);
394572ff6f6SMatthew Dillon numSymbols = howmany(numBits, bitsPerSymbol);
395572ff6f6SMatthew Dillon txTime = OFDM_HALF_SIFS_TIME
396572ff6f6SMatthew Dillon + OFDM_HALF_PREAMBLE_TIME
397572ff6f6SMatthew Dillon + (numSymbols * OFDM_HALF_SYMBOL_TIME);
398572ff6f6SMatthew Dillon break;
399572ff6f6SMatthew Dillon case IEEE80211_T_OFDM_QUARTER:
400572ff6f6SMatthew Dillon bitsPerSymbol = (kbps * OFDM_QUARTER_SYMBOL_TIME) / 1000;
401572ff6f6SMatthew Dillon HALASSERT(bitsPerSymbol != 0);
402572ff6f6SMatthew Dillon
403572ff6f6SMatthew Dillon numBits = OFDM_QUARTER_PLCP_BITS + (frameLen << 3);
404572ff6f6SMatthew Dillon numSymbols = howmany(numBits, bitsPerSymbol);
405572ff6f6SMatthew Dillon txTime = OFDM_QUARTER_SIFS_TIME
406572ff6f6SMatthew Dillon + OFDM_QUARTER_PREAMBLE_TIME
407572ff6f6SMatthew Dillon + (numSymbols * OFDM_QUARTER_SYMBOL_TIME);
408572ff6f6SMatthew Dillon break;
409572ff6f6SMatthew Dillon case IEEE80211_T_TURBO:
410572ff6f6SMatthew Dillon bitsPerSymbol = (kbps * TURBO_SYMBOL_TIME) / 1000;
411572ff6f6SMatthew Dillon HALASSERT(bitsPerSymbol != 0);
412572ff6f6SMatthew Dillon
413572ff6f6SMatthew Dillon numBits = TURBO_PLCP_BITS + (frameLen << 3);
414572ff6f6SMatthew Dillon numSymbols = howmany(numBits, bitsPerSymbol);
415572ff6f6SMatthew Dillon txTime = TURBO_SIFS_TIME
416572ff6f6SMatthew Dillon + TURBO_PREAMBLE_TIME
417572ff6f6SMatthew Dillon + (numSymbols * TURBO_SYMBOL_TIME);
418572ff6f6SMatthew Dillon break;
419572ff6f6SMatthew Dillon default:
420572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_PHYIO,
421572ff6f6SMatthew Dillon "%s: unknown phy %u (rate ix %u)\n",
422572ff6f6SMatthew Dillon __func__, rates->info[rateix].phy, rateix);
423572ff6f6SMatthew Dillon txTime = 0;
424572ff6f6SMatthew Dillon break;
425572ff6f6SMatthew Dillon }
426572ff6f6SMatthew Dillon return txTime;
427572ff6f6SMatthew Dillon }
428572ff6f6SMatthew Dillon
429572ff6f6SMatthew Dillon int
ath_hal_get_curmode(struct ath_hal * ah,const struct ieee80211_channel * chan)430572ff6f6SMatthew Dillon ath_hal_get_curmode(struct ath_hal *ah, const struct ieee80211_channel *chan)
431572ff6f6SMatthew Dillon {
432572ff6f6SMatthew Dillon /*
433572ff6f6SMatthew Dillon * Pick a default mode at bootup. A channel change is inevitable.
434572ff6f6SMatthew Dillon */
435572ff6f6SMatthew Dillon if (!chan)
436572ff6f6SMatthew Dillon return HAL_MODE_11NG_HT20;
437572ff6f6SMatthew Dillon
438572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_TURBO(chan))
439572ff6f6SMatthew Dillon return HAL_MODE_TURBO;
440572ff6f6SMatthew Dillon
441572ff6f6SMatthew Dillon /* check for NA_HT before plain A, since IS_CHAN_A includes NA_HT */
442572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
443572ff6f6SMatthew Dillon return HAL_MODE_11NA_HT20;
444572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
445572ff6f6SMatthew Dillon return HAL_MODE_11NA_HT40PLUS;
446572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_5GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
447572ff6f6SMatthew Dillon return HAL_MODE_11NA_HT40MINUS;
448572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_A(chan))
449572ff6f6SMatthew Dillon return HAL_MODE_11A;
450572ff6f6SMatthew Dillon
451572ff6f6SMatthew Dillon /* check for NG_HT before plain G, since IS_CHAN_G includes NG_HT */
452572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT20(chan))
453572ff6f6SMatthew Dillon return HAL_MODE_11NG_HT20;
454572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40U(chan))
455572ff6f6SMatthew Dillon return HAL_MODE_11NG_HT40PLUS;
456572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_2GHZ(chan) && IEEE80211_IS_CHAN_HT40D(chan))
457572ff6f6SMatthew Dillon return HAL_MODE_11NG_HT40MINUS;
458572ff6f6SMatthew Dillon
459572ff6f6SMatthew Dillon /*
460572ff6f6SMatthew Dillon * XXX For FreeBSD, will this work correctly given the DYN
461572ff6f6SMatthew Dillon * chan mode (OFDM+CCK dynamic) ? We have pure-G versions DYN-BG..
462572ff6f6SMatthew Dillon */
463572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_G(chan))
464572ff6f6SMatthew Dillon return HAL_MODE_11G;
465572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_B(chan))
466572ff6f6SMatthew Dillon return HAL_MODE_11B;
467572ff6f6SMatthew Dillon
468572ff6f6SMatthew Dillon HALASSERT(0);
469572ff6f6SMatthew Dillon return HAL_MODE_11NG_HT20;
470572ff6f6SMatthew Dillon }
471572ff6f6SMatthew Dillon
472572ff6f6SMatthew Dillon
473572ff6f6SMatthew Dillon typedef enum {
474572ff6f6SMatthew Dillon WIRELESS_MODE_11a = 0,
475572ff6f6SMatthew Dillon WIRELESS_MODE_TURBO = 1,
476572ff6f6SMatthew Dillon WIRELESS_MODE_11b = 2,
477572ff6f6SMatthew Dillon WIRELESS_MODE_11g = 3,
478572ff6f6SMatthew Dillon WIRELESS_MODE_108g = 4,
479572ff6f6SMatthew Dillon
480572ff6f6SMatthew Dillon WIRELESS_MODE_MAX
481572ff6f6SMatthew Dillon } WIRELESS_MODE;
482572ff6f6SMatthew Dillon
483572ff6f6SMatthew Dillon static WIRELESS_MODE
ath_hal_chan2wmode(struct ath_hal * ah,const struct ieee80211_channel * chan)484572ff6f6SMatthew Dillon ath_hal_chan2wmode(struct ath_hal *ah, const struct ieee80211_channel *chan)
485572ff6f6SMatthew Dillon {
486572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_B(chan))
487572ff6f6SMatthew Dillon return WIRELESS_MODE_11b;
488572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_G(chan))
489572ff6f6SMatthew Dillon return WIRELESS_MODE_11g;
490572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_108G(chan))
491572ff6f6SMatthew Dillon return WIRELESS_MODE_108g;
492572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_TURBO(chan))
493572ff6f6SMatthew Dillon return WIRELESS_MODE_TURBO;
494572ff6f6SMatthew Dillon return WIRELESS_MODE_11a;
495572ff6f6SMatthew Dillon }
496572ff6f6SMatthew Dillon
497572ff6f6SMatthew Dillon /*
498572ff6f6SMatthew Dillon * Convert between microseconds and core system clocks.
499572ff6f6SMatthew Dillon */
500572ff6f6SMatthew Dillon /* 11a Turbo 11b 11g 108g */
501572ff6f6SMatthew Dillon static const uint8_t CLOCK_RATE[] = { 40, 80, 22, 44, 88 };
502572ff6f6SMatthew Dillon
503572ff6f6SMatthew Dillon #define CLOCK_FAST_RATE_5GHZ_OFDM 44
504572ff6f6SMatthew Dillon
505572ff6f6SMatthew Dillon u_int
ath_hal_mac_clks(struct ath_hal * ah,u_int usecs)506572ff6f6SMatthew Dillon ath_hal_mac_clks(struct ath_hal *ah, u_int usecs)
507572ff6f6SMatthew Dillon {
508572ff6f6SMatthew Dillon const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan;
509572ff6f6SMatthew Dillon u_int clks;
510572ff6f6SMatthew Dillon
511572ff6f6SMatthew Dillon /* NB: ah_curchan may be null when called attach time */
512572ff6f6SMatthew Dillon /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */
513572ff6f6SMatthew Dillon if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) {
514572ff6f6SMatthew Dillon clks = usecs * CLOCK_FAST_RATE_5GHZ_OFDM;
515572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(c))
516572ff6f6SMatthew Dillon clks <<= 1;
517572ff6f6SMatthew Dillon } else if (c != AH_NULL) {
518572ff6f6SMatthew Dillon clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
519572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(c))
520572ff6f6SMatthew Dillon clks <<= 1;
521572ff6f6SMatthew Dillon } else
522572ff6f6SMatthew Dillon clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b];
523572ff6f6SMatthew Dillon
524572ff6f6SMatthew Dillon /* Compensate for half/quarter rate */
525572ff6f6SMatthew Dillon if (c != AH_NULL && IEEE80211_IS_CHAN_HALF(c))
526572ff6f6SMatthew Dillon clks = clks / 2;
527572ff6f6SMatthew Dillon else if (c != AH_NULL && IEEE80211_IS_CHAN_QUARTER(c))
528572ff6f6SMatthew Dillon clks = clks / 4;
529572ff6f6SMatthew Dillon
530572ff6f6SMatthew Dillon return clks;
531572ff6f6SMatthew Dillon }
532572ff6f6SMatthew Dillon
533572ff6f6SMatthew Dillon u_int
ath_hal_mac_usec(struct ath_hal * ah,u_int clks)534572ff6f6SMatthew Dillon ath_hal_mac_usec(struct ath_hal *ah, u_int clks)
535572ff6f6SMatthew Dillon {
536572ff6f6SMatthew Dillon const struct ieee80211_channel *c = AH_PRIVATE(ah)->ah_curchan;
537572ff6f6SMatthew Dillon u_int usec;
538572ff6f6SMatthew Dillon
539572ff6f6SMatthew Dillon /* NB: ah_curchan may be null when called attach time */
540572ff6f6SMatthew Dillon /* XXX merlin and later specific workaround - 5ghz fast clock is 44 */
541572ff6f6SMatthew Dillon if (c != AH_NULL && IS_5GHZ_FAST_CLOCK_EN(ah, c)) {
542572ff6f6SMatthew Dillon usec = clks / CLOCK_FAST_RATE_5GHZ_OFDM;
543572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(c))
544572ff6f6SMatthew Dillon usec >>= 1;
545572ff6f6SMatthew Dillon } else if (c != AH_NULL) {
546572ff6f6SMatthew Dillon usec = clks / CLOCK_RATE[ath_hal_chan2wmode(ah, c)];
547572ff6f6SMatthew Dillon if (IEEE80211_IS_CHAN_HT40(c))
548572ff6f6SMatthew Dillon usec >>= 1;
549572ff6f6SMatthew Dillon } else
550572ff6f6SMatthew Dillon usec = clks / CLOCK_RATE[WIRELESS_MODE_11b];
551572ff6f6SMatthew Dillon return usec;
552572ff6f6SMatthew Dillon }
553572ff6f6SMatthew Dillon
554572ff6f6SMatthew Dillon /*
555572ff6f6SMatthew Dillon * Setup a h/w rate table's reverse lookup table and
556572ff6f6SMatthew Dillon * fill in ack durations. This routine is called for
557572ff6f6SMatthew Dillon * each rate table returned through the ah_getRateTable
558572ff6f6SMatthew Dillon * method. The reverse lookup tables are assumed to be
559572ff6f6SMatthew Dillon * initialized to zero (or at least the first entry).
560572ff6f6SMatthew Dillon * We use this as a key that indicates whether or not
561572ff6f6SMatthew Dillon * we've previously setup the reverse lookup table.
562572ff6f6SMatthew Dillon *
563572ff6f6SMatthew Dillon * XXX not reentrant, but shouldn't matter
564572ff6f6SMatthew Dillon */
565572ff6f6SMatthew Dillon void
ath_hal_setupratetable(struct ath_hal * ah,HAL_RATE_TABLE * rt)566572ff6f6SMatthew Dillon ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt)
567572ff6f6SMatthew Dillon {
568572ff6f6SMatthew Dillon #define N(a) (sizeof(a)/sizeof(a[0]))
569572ff6f6SMatthew Dillon int i;
570572ff6f6SMatthew Dillon
571572ff6f6SMatthew Dillon if (rt->rateCodeToIndex[0] != 0) /* already setup */
572572ff6f6SMatthew Dillon return;
573572ff6f6SMatthew Dillon for (i = 0; i < N(rt->rateCodeToIndex); i++)
574572ff6f6SMatthew Dillon rt->rateCodeToIndex[i] = (uint8_t) -1;
575572ff6f6SMatthew Dillon for (i = 0; i < rt->rateCount; i++) {
576572ff6f6SMatthew Dillon uint8_t code = rt->info[i].rateCode;
577572ff6f6SMatthew Dillon uint8_t cix = rt->info[i].controlRate;
578572ff6f6SMatthew Dillon
579572ff6f6SMatthew Dillon HALASSERT(code < N(rt->rateCodeToIndex));
580572ff6f6SMatthew Dillon rt->rateCodeToIndex[code] = i;
581572ff6f6SMatthew Dillon HALASSERT((code | rt->info[i].shortPreamble) <
582572ff6f6SMatthew Dillon N(rt->rateCodeToIndex));
583572ff6f6SMatthew Dillon rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i;
584572ff6f6SMatthew Dillon /*
585572ff6f6SMatthew Dillon * XXX for 11g the control rate to use for 5.5 and 11 Mb/s
586572ff6f6SMatthew Dillon * depends on whether they are marked as basic rates;
587572ff6f6SMatthew Dillon * the static tables are setup with an 11b-compatible
588572ff6f6SMatthew Dillon * 2Mb/s rate which will work but is suboptimal
589572ff6f6SMatthew Dillon */
590572ff6f6SMatthew Dillon rt->info[i].lpAckDuration = ath_hal_computetxtime(ah, rt,
591572ff6f6SMatthew Dillon WLAN_CTRL_FRAME_SIZE, cix, AH_FALSE);
592572ff6f6SMatthew Dillon rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt,
593572ff6f6SMatthew Dillon WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE);
594572ff6f6SMatthew Dillon }
595572ff6f6SMatthew Dillon #undef N
596572ff6f6SMatthew Dillon }
597572ff6f6SMatthew Dillon
598572ff6f6SMatthew Dillon HAL_STATUS
ath_hal_getcapability(struct ath_hal * ah,HAL_CAPABILITY_TYPE type,uint32_t capability,uint32_t * result)599572ff6f6SMatthew Dillon ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
600572ff6f6SMatthew Dillon uint32_t capability, uint32_t *result)
601572ff6f6SMatthew Dillon {
602572ff6f6SMatthew Dillon const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps;
603572ff6f6SMatthew Dillon
604572ff6f6SMatthew Dillon switch (type) {
605572ff6f6SMatthew Dillon case HAL_CAP_REG_DMN: /* regulatory domain */
606572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_currentRD;
607572ff6f6SMatthew Dillon return HAL_OK;
608572ff6f6SMatthew Dillon case HAL_CAP_DFS_DMN: /* DFS Domain */
609572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_dfsDomain;
610572ff6f6SMatthew Dillon return HAL_OK;
611572ff6f6SMatthew Dillon case HAL_CAP_CIPHER: /* cipher handled in hardware */
612572ff6f6SMatthew Dillon case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */
613572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
614572ff6f6SMatthew Dillon case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */
615572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
616572ff6f6SMatthew Dillon case HAL_CAP_PHYCOUNTERS: /* hardware PHY error counters */
617572ff6f6SMatthew Dillon return pCap->halHwPhyCounterSupport ? HAL_OK : HAL_ENXIO;
618572ff6f6SMatthew Dillon case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC when WMM is turned on */
619572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
620572ff6f6SMatthew Dillon case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */
621572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
622572ff6f6SMatthew Dillon case HAL_CAP_KEYCACHE_SIZE: /* hardware key cache size */
623572ff6f6SMatthew Dillon *result = pCap->halKeyCacheSize;
624572ff6f6SMatthew Dillon return HAL_OK;
625572ff6f6SMatthew Dillon case HAL_CAP_NUM_TXQUEUES: /* number of hardware tx queues */
626572ff6f6SMatthew Dillon *result = pCap->halTotalQueues;
627572ff6f6SMatthew Dillon return HAL_OK;
628572ff6f6SMatthew Dillon case HAL_CAP_VEOL: /* hardware supports virtual EOL */
629572ff6f6SMatthew Dillon return pCap->halVEOLSupport ? HAL_OK : HAL_ENOTSUPP;
630572ff6f6SMatthew Dillon case HAL_CAP_PSPOLL: /* hardware PS-Poll support works */
631572ff6f6SMatthew Dillon return pCap->halPSPollBroken ? HAL_ENOTSUPP : HAL_OK;
632572ff6f6SMatthew Dillon case HAL_CAP_COMPRESSION:
633572ff6f6SMatthew Dillon return pCap->halCompressSupport ? HAL_OK : HAL_ENOTSUPP;
634572ff6f6SMatthew Dillon case HAL_CAP_BURST:
635572ff6f6SMatthew Dillon return pCap->halBurstSupport ? HAL_OK : HAL_ENOTSUPP;
636572ff6f6SMatthew Dillon case HAL_CAP_FASTFRAME:
637572ff6f6SMatthew Dillon return pCap->halFastFramesSupport ? HAL_OK : HAL_ENOTSUPP;
638572ff6f6SMatthew Dillon case HAL_CAP_DIAG: /* hardware diagnostic support */
639572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_diagreg;
640572ff6f6SMatthew Dillon return HAL_OK;
641572ff6f6SMatthew Dillon case HAL_CAP_TXPOW: /* global tx power limit */
642572ff6f6SMatthew Dillon switch (capability) {
643572ff6f6SMatthew Dillon case 0: /* facility is supported */
644572ff6f6SMatthew Dillon return HAL_OK;
645572ff6f6SMatthew Dillon case 1: /* current limit */
646572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_powerLimit;
647572ff6f6SMatthew Dillon return HAL_OK;
648572ff6f6SMatthew Dillon case 2: /* current max tx power */
649572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_maxPowerLevel;
650572ff6f6SMatthew Dillon return HAL_OK;
651572ff6f6SMatthew Dillon case 3: /* scale factor */
652572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_tpScale;
653572ff6f6SMatthew Dillon return HAL_OK;
654572ff6f6SMatthew Dillon }
655572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
656572ff6f6SMatthew Dillon case HAL_CAP_BSSIDMASK: /* hardware supports bssid mask */
657572ff6f6SMatthew Dillon return pCap->halBssIdMaskSupport ? HAL_OK : HAL_ENOTSUPP;
658572ff6f6SMatthew Dillon case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */
659572ff6f6SMatthew Dillon return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENOTSUPP;
660572ff6f6SMatthew Dillon case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */
661572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
662572ff6f6SMatthew Dillon case HAL_CAP_RFSILENT: /* rfsilent support */
663572ff6f6SMatthew Dillon switch (capability) {
664572ff6f6SMatthew Dillon case 0: /* facility is supported */
665572ff6f6SMatthew Dillon return pCap->halRfSilentSupport ? HAL_OK : HAL_ENOTSUPP;
666572ff6f6SMatthew Dillon case 1: /* current setting */
667572ff6f6SMatthew Dillon return AH_PRIVATE(ah)->ah_rfkillEnabled ?
668572ff6f6SMatthew Dillon HAL_OK : HAL_ENOTSUPP;
669572ff6f6SMatthew Dillon case 2: /* rfsilent config */
670572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_rfsilent;
671572ff6f6SMatthew Dillon return HAL_OK;
672572ff6f6SMatthew Dillon }
673572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
674572ff6f6SMatthew Dillon case HAL_CAP_11D:
675572ff6f6SMatthew Dillon return HAL_OK;
676572ff6f6SMatthew Dillon
677572ff6f6SMatthew Dillon case HAL_CAP_HT:
678572ff6f6SMatthew Dillon return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP;
679572ff6f6SMatthew Dillon case HAL_CAP_GTXTO:
680572ff6f6SMatthew Dillon return pCap->halGTTSupport ? HAL_OK : HAL_ENOTSUPP;
681572ff6f6SMatthew Dillon case HAL_CAP_FAST_CC:
682572ff6f6SMatthew Dillon return pCap->halFastCCSupport ? HAL_OK : HAL_ENOTSUPP;
683572ff6f6SMatthew Dillon case HAL_CAP_TX_CHAINMASK: /* mask of TX chains supported */
684572ff6f6SMatthew Dillon *result = pCap->halTxChainMask;
685572ff6f6SMatthew Dillon return HAL_OK;
686572ff6f6SMatthew Dillon case HAL_CAP_RX_CHAINMASK: /* mask of RX chains supported */
687572ff6f6SMatthew Dillon *result = pCap->halRxChainMask;
688572ff6f6SMatthew Dillon return HAL_OK;
689572ff6f6SMatthew Dillon case HAL_CAP_NUM_GPIO_PINS:
690572ff6f6SMatthew Dillon *result = pCap->halNumGpioPins;
691572ff6f6SMatthew Dillon return HAL_OK;
692572ff6f6SMatthew Dillon case HAL_CAP_CST:
693572ff6f6SMatthew Dillon return pCap->halCSTSupport ? HAL_OK : HAL_ENOTSUPP;
694572ff6f6SMatthew Dillon case HAL_CAP_RTS_AGGR_LIMIT:
695572ff6f6SMatthew Dillon *result = pCap->halRtsAggrLimit;
696572ff6f6SMatthew Dillon return HAL_OK;
697572ff6f6SMatthew Dillon case HAL_CAP_4ADDR_AGGR:
698572ff6f6SMatthew Dillon return pCap->hal4AddrAggrSupport ? HAL_OK : HAL_ENOTSUPP;
699572ff6f6SMatthew Dillon case HAL_CAP_EXT_CHAN_DFS:
700572ff6f6SMatthew Dillon return pCap->halExtChanDfsSupport ? HAL_OK : HAL_ENOTSUPP;
701572ff6f6SMatthew Dillon case HAL_CAP_RX_STBC:
702572ff6f6SMatthew Dillon return pCap->halRxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
703572ff6f6SMatthew Dillon case HAL_CAP_TX_STBC:
704572ff6f6SMatthew Dillon return pCap->halTxStbcSupport ? HAL_OK : HAL_ENOTSUPP;
705572ff6f6SMatthew Dillon case HAL_CAP_COMBINED_RADAR_RSSI:
706572ff6f6SMatthew Dillon return pCap->halUseCombinedRadarRssi ? HAL_OK : HAL_ENOTSUPP;
707572ff6f6SMatthew Dillon case HAL_CAP_AUTO_SLEEP:
708572ff6f6SMatthew Dillon return pCap->halAutoSleepSupport ? HAL_OK : HAL_ENOTSUPP;
709572ff6f6SMatthew Dillon case HAL_CAP_MBSSID_AGGR_SUPPORT:
710572ff6f6SMatthew Dillon return pCap->halMbssidAggrSupport ? HAL_OK : HAL_ENOTSUPP;
711572ff6f6SMatthew Dillon case HAL_CAP_SPLIT_4KB_TRANS: /* hardware handles descriptors straddling 4k page boundary */
712572ff6f6SMatthew Dillon return pCap->hal4kbSplitTransSupport ? HAL_OK : HAL_ENOTSUPP;
713572ff6f6SMatthew Dillon case HAL_CAP_REG_FLAG:
714572ff6f6SMatthew Dillon *result = AH_PRIVATE(ah)->ah_currentRDext;
715572ff6f6SMatthew Dillon return HAL_OK;
716572ff6f6SMatthew Dillon case HAL_CAP_ENHANCED_DMA_SUPPORT:
717572ff6f6SMatthew Dillon return pCap->halEnhancedDmaSupport ? HAL_OK : HAL_ENOTSUPP;
718572ff6f6SMatthew Dillon case HAL_CAP_NUM_TXMAPS:
719572ff6f6SMatthew Dillon *result = pCap->halNumTxMaps;
720572ff6f6SMatthew Dillon return HAL_OK;
721572ff6f6SMatthew Dillon case HAL_CAP_TXDESCLEN:
722572ff6f6SMatthew Dillon *result = pCap->halTxDescLen;
723572ff6f6SMatthew Dillon return HAL_OK;
724572ff6f6SMatthew Dillon case HAL_CAP_TXSTATUSLEN:
725572ff6f6SMatthew Dillon *result = pCap->halTxStatusLen;
726572ff6f6SMatthew Dillon return HAL_OK;
727572ff6f6SMatthew Dillon case HAL_CAP_RXSTATUSLEN:
728572ff6f6SMatthew Dillon *result = pCap->halRxStatusLen;
729572ff6f6SMatthew Dillon return HAL_OK;
730572ff6f6SMatthew Dillon case HAL_CAP_RXFIFODEPTH:
731572ff6f6SMatthew Dillon switch (capability) {
732572ff6f6SMatthew Dillon case HAL_RX_QUEUE_HP:
733572ff6f6SMatthew Dillon *result = pCap->halRxHpFifoDepth;
734572ff6f6SMatthew Dillon return HAL_OK;
735572ff6f6SMatthew Dillon case HAL_RX_QUEUE_LP:
736572ff6f6SMatthew Dillon *result = pCap->halRxLpFifoDepth;
737572ff6f6SMatthew Dillon return HAL_OK;
738572ff6f6SMatthew Dillon default:
739572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
740572ff6f6SMatthew Dillon }
741572ff6f6SMatthew Dillon case HAL_CAP_RXBUFSIZE:
742572ff6f6SMatthew Dillon case HAL_CAP_NUM_MR_RETRIES:
743572ff6f6SMatthew Dillon *result = pCap->halNumMRRetries;
744572ff6f6SMatthew Dillon return HAL_OK;
745572ff6f6SMatthew Dillon case HAL_CAP_BT_COEX:
746572ff6f6SMatthew Dillon return pCap->halBtCoexSupport ? HAL_OK : HAL_ENOTSUPP;
747572ff6f6SMatthew Dillon case HAL_CAP_SPECTRAL_SCAN:
748572ff6f6SMatthew Dillon return pCap->halSpectralScanSupport ? HAL_OK : HAL_ENOTSUPP;
749572ff6f6SMatthew Dillon case HAL_CAP_HT20_SGI:
750572ff6f6SMatthew Dillon return pCap->halHTSGI20Support ? HAL_OK : HAL_ENOTSUPP;
751572ff6f6SMatthew Dillon case HAL_CAP_RXTSTAMP_PREC: /* rx desc tstamp precision (bits) */
752572ff6f6SMatthew Dillon *result = pCap->halTstampPrecision;
753572ff6f6SMatthew Dillon return HAL_OK;
754572ff6f6SMatthew Dillon case HAL_CAP_ANT_DIV_COMB: /* AR9285/AR9485 LNA diversity */
755572ff6f6SMatthew Dillon return pCap->halAntDivCombSupport ? HAL_OK : HAL_ENOTSUPP;
756572ff6f6SMatthew Dillon
757572ff6f6SMatthew Dillon case HAL_CAP_ENHANCED_DFS_SUPPORT:
758572ff6f6SMatthew Dillon return pCap->halEnhancedDfsSupport ? HAL_OK : HAL_ENOTSUPP;
759572ff6f6SMatthew Dillon
760572ff6f6SMatthew Dillon /* FreeBSD-specific entries for now */
761572ff6f6SMatthew Dillon case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */
762572ff6f6SMatthew Dillon return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP;
763572ff6f6SMatthew Dillon case HAL_CAP_INTRMASK: /* mask of supported interrupts */
764572ff6f6SMatthew Dillon *result = pCap->halIntrMask;
765572ff6f6SMatthew Dillon return HAL_OK;
766572ff6f6SMatthew Dillon case HAL_CAP_BSSIDMATCH: /* hardware has disable bssid match */
767572ff6f6SMatthew Dillon return pCap->halBssidMatchSupport ? HAL_OK : HAL_ENOTSUPP;
768572ff6f6SMatthew Dillon case HAL_CAP_STREAMS: /* number of 11n spatial streams */
769572ff6f6SMatthew Dillon switch (capability) {
770572ff6f6SMatthew Dillon case 0: /* TX */
771572ff6f6SMatthew Dillon *result = pCap->halTxStreams;
772572ff6f6SMatthew Dillon return HAL_OK;
773572ff6f6SMatthew Dillon case 1: /* RX */
774572ff6f6SMatthew Dillon *result = pCap->halRxStreams;
775572ff6f6SMatthew Dillon return HAL_OK;
776572ff6f6SMatthew Dillon default:
777572ff6f6SMatthew Dillon return HAL_ENOTSUPP;
778572ff6f6SMatthew Dillon }
779572ff6f6SMatthew Dillon case HAL_CAP_RXDESC_SELFLINK: /* hardware supports self-linked final RX descriptors correctly */
780572ff6f6SMatthew Dillon return pCap->halHasRxSelfLinkedTail ? HAL_OK : HAL_ENOTSUPP;
781572ff6f6SMatthew Dillon case HAL_CAP_LONG_RXDESC_TSF: /* 32 bit TSF in RX descriptor? */
782572ff6f6SMatthew Dillon return pCap->halHasLongRxDescTsf ? HAL_OK : HAL_ENOTSUPP;
783572ff6f6SMatthew Dillon case HAL_CAP_BB_READ_WAR: /* Baseband read WAR */
784572ff6f6SMatthew Dillon return pCap->halHasBBReadWar? HAL_OK : HAL_ENOTSUPP;
785572ff6f6SMatthew Dillon case HAL_CAP_SERIALISE_WAR: /* PCI register serialisation */
786572ff6f6SMatthew Dillon return pCap->halSerialiseRegWar ? HAL_OK : HAL_ENOTSUPP;
787572ff6f6SMatthew Dillon case HAL_CAP_MFP: /* Management frame protection setting */
788572ff6f6SMatthew Dillon *result = pCap->halMfpSupport;
789572ff6f6SMatthew Dillon return HAL_OK;
790572ff6f6SMatthew Dillon case HAL_CAP_RX_LNA_MIXING: /* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */
791572ff6f6SMatthew Dillon return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP;
792d98a0bcfSMatthew Dillon case HAL_CAP_DO_MYBEACON: /* Hardware supports filtering my-beacons */
793d98a0bcfSMatthew Dillon return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP;
794572ff6f6SMatthew Dillon default:
795572ff6f6SMatthew Dillon return HAL_EINVAL;
796572ff6f6SMatthew Dillon }
797572ff6f6SMatthew Dillon }
798572ff6f6SMatthew Dillon
799572ff6f6SMatthew Dillon HAL_BOOL
ath_hal_setcapability(struct ath_hal * ah,HAL_CAPABILITY_TYPE type,uint32_t capability,uint32_t setting,HAL_STATUS * status)800572ff6f6SMatthew Dillon ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type,
801572ff6f6SMatthew Dillon uint32_t capability, uint32_t setting, HAL_STATUS *status)
802572ff6f6SMatthew Dillon {
803572ff6f6SMatthew Dillon
804572ff6f6SMatthew Dillon switch (type) {
805572ff6f6SMatthew Dillon case HAL_CAP_TXPOW:
806572ff6f6SMatthew Dillon switch (capability) {
807572ff6f6SMatthew Dillon case 3:
808572ff6f6SMatthew Dillon if (setting <= HAL_TP_SCALE_MIN) {
809572ff6f6SMatthew Dillon AH_PRIVATE(ah)->ah_tpScale = setting;
810572ff6f6SMatthew Dillon return AH_TRUE;
811572ff6f6SMatthew Dillon }
812572ff6f6SMatthew Dillon break;
813572ff6f6SMatthew Dillon }
814572ff6f6SMatthew Dillon break;
815572ff6f6SMatthew Dillon case HAL_CAP_RFSILENT: /* rfsilent support */
816572ff6f6SMatthew Dillon /*
817572ff6f6SMatthew Dillon * NB: allow even if halRfSilentSupport is false
818572ff6f6SMatthew Dillon * in case the EEPROM is misprogrammed.
819572ff6f6SMatthew Dillon */
820572ff6f6SMatthew Dillon switch (capability) {
821572ff6f6SMatthew Dillon case 1: /* current setting */
822572ff6f6SMatthew Dillon AH_PRIVATE(ah)->ah_rfkillEnabled = (setting != 0);
823572ff6f6SMatthew Dillon return AH_TRUE;
824572ff6f6SMatthew Dillon case 2: /* rfsilent config */
825572ff6f6SMatthew Dillon /* XXX better done per-chip for validation? */
826572ff6f6SMatthew Dillon AH_PRIVATE(ah)->ah_rfsilent = setting;
827572ff6f6SMatthew Dillon return AH_TRUE;
828572ff6f6SMatthew Dillon }
829572ff6f6SMatthew Dillon break;
830572ff6f6SMatthew Dillon case HAL_CAP_REG_DMN: /* regulatory domain */
831572ff6f6SMatthew Dillon AH_PRIVATE(ah)->ah_currentRD = setting;
832572ff6f6SMatthew Dillon return AH_TRUE;
833572ff6f6SMatthew Dillon case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */
834572ff6f6SMatthew Dillon AH_PRIVATE(ah)->ah_rxornIsFatal = setting;
835572ff6f6SMatthew Dillon return AH_TRUE;
836572ff6f6SMatthew Dillon default:
837572ff6f6SMatthew Dillon break;
838572ff6f6SMatthew Dillon }
839572ff6f6SMatthew Dillon if (status)
840572ff6f6SMatthew Dillon *status = HAL_EINVAL;
841572ff6f6SMatthew Dillon return AH_FALSE;
842572ff6f6SMatthew Dillon }
843572ff6f6SMatthew Dillon
844572ff6f6SMatthew Dillon /*
845572ff6f6SMatthew Dillon * Common support for getDiagState method.
846572ff6f6SMatthew Dillon */
847572ff6f6SMatthew Dillon
848572ff6f6SMatthew Dillon static u_int
ath_hal_getregdump(struct ath_hal * ah,const HAL_REGRANGE * regs,void * dstbuf,int space)849572ff6f6SMatthew Dillon ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs,
850572ff6f6SMatthew Dillon void *dstbuf, int space)
851572ff6f6SMatthew Dillon {
852572ff6f6SMatthew Dillon uint32_t *dp = dstbuf;
853572ff6f6SMatthew Dillon int i;
854572ff6f6SMatthew Dillon
855572ff6f6SMatthew Dillon for (i = 0; space >= 2*sizeof(uint32_t); i++) {
856848b370cSMatthew Dillon uint32_t r = regs[i].start;
857848b370cSMatthew Dillon uint32_t e = regs[i].end;
858848b370cSMatthew Dillon *dp++ = r;
859848b370cSMatthew Dillon *dp++ = e;
860848b370cSMatthew Dillon space -= 2*sizeof(uint32_t);
861572ff6f6SMatthew Dillon do {
862572ff6f6SMatthew Dillon *dp++ = OS_REG_READ(ah, r);
863572ff6f6SMatthew Dillon r += sizeof(uint32_t);
864572ff6f6SMatthew Dillon space -= sizeof(uint32_t);
865572ff6f6SMatthew Dillon } while (r <= e && space >= sizeof(uint32_t));
866572ff6f6SMatthew Dillon }
867572ff6f6SMatthew Dillon return (char *) dp - (char *) dstbuf;
868572ff6f6SMatthew Dillon }
869572ff6f6SMatthew Dillon
870572ff6f6SMatthew Dillon static void
ath_hal_setregs(struct ath_hal * ah,const HAL_REGWRITE * regs,int space)871572ff6f6SMatthew Dillon ath_hal_setregs(struct ath_hal *ah, const HAL_REGWRITE *regs, int space)
872572ff6f6SMatthew Dillon {
873572ff6f6SMatthew Dillon while (space >= sizeof(HAL_REGWRITE)) {
874572ff6f6SMatthew Dillon OS_REG_WRITE(ah, regs->addr, regs->value);
875572ff6f6SMatthew Dillon regs++, space -= sizeof(HAL_REGWRITE);
876572ff6f6SMatthew Dillon }
877572ff6f6SMatthew Dillon }
878572ff6f6SMatthew Dillon
879572ff6f6SMatthew Dillon HAL_BOOL
ath_hal_getdiagstate(struct ath_hal * ah,int request,const void * args,uint32_t argsize,void ** result,uint32_t * resultsize)880572ff6f6SMatthew Dillon ath_hal_getdiagstate(struct ath_hal *ah, int request,
881572ff6f6SMatthew Dillon const void *args, uint32_t argsize,
882572ff6f6SMatthew Dillon void **result, uint32_t *resultsize)
883572ff6f6SMatthew Dillon {
884*b14ca477SMatthew Dillon
885572ff6f6SMatthew Dillon switch (request) {
886572ff6f6SMatthew Dillon case HAL_DIAG_REVS:
887572ff6f6SMatthew Dillon *result = &AH_PRIVATE(ah)->ah_devid;
888572ff6f6SMatthew Dillon *resultsize = sizeof(HAL_REVS);
889572ff6f6SMatthew Dillon return AH_TRUE;
890572ff6f6SMatthew Dillon case HAL_DIAG_REGS:
891572ff6f6SMatthew Dillon *resultsize = ath_hal_getregdump(ah, args, *result,*resultsize);
892572ff6f6SMatthew Dillon return AH_TRUE;
893572ff6f6SMatthew Dillon case HAL_DIAG_SETREGS:
894572ff6f6SMatthew Dillon ath_hal_setregs(ah, args, argsize);
895572ff6f6SMatthew Dillon *resultsize = 0;
896572ff6f6SMatthew Dillon return AH_TRUE;
897572ff6f6SMatthew Dillon case HAL_DIAG_FATALERR:
898572ff6f6SMatthew Dillon *result = &AH_PRIVATE(ah)->ah_fatalState[0];
899572ff6f6SMatthew Dillon *resultsize = sizeof(AH_PRIVATE(ah)->ah_fatalState);
900572ff6f6SMatthew Dillon return AH_TRUE;
901572ff6f6SMatthew Dillon case HAL_DIAG_EEREAD:
902572ff6f6SMatthew Dillon if (argsize != sizeof(uint16_t))
903572ff6f6SMatthew Dillon return AH_FALSE;
904572ff6f6SMatthew Dillon if (!ath_hal_eepromRead(ah, *(const uint16_t *)args, *result))
905572ff6f6SMatthew Dillon return AH_FALSE;
906572ff6f6SMatthew Dillon *resultsize = sizeof(uint16_t);
907572ff6f6SMatthew Dillon return AH_TRUE;
908572ff6f6SMatthew Dillon #ifdef AH_PRIVATE_DIAG
909572ff6f6SMatthew Dillon case HAL_DIAG_SETKEY: {
910572ff6f6SMatthew Dillon const HAL_DIAG_KEYVAL *dk;
911572ff6f6SMatthew Dillon
912572ff6f6SMatthew Dillon if (argsize != sizeof(HAL_DIAG_KEYVAL))
913572ff6f6SMatthew Dillon return AH_FALSE;
914572ff6f6SMatthew Dillon dk = (const HAL_DIAG_KEYVAL *)args;
915572ff6f6SMatthew Dillon return ah->ah_setKeyCacheEntry(ah, dk->dk_keyix,
916572ff6f6SMatthew Dillon &dk->dk_keyval, dk->dk_mac, dk->dk_xor);
917572ff6f6SMatthew Dillon }
918572ff6f6SMatthew Dillon case HAL_DIAG_RESETKEY:
919572ff6f6SMatthew Dillon if (argsize != sizeof(uint16_t))
920572ff6f6SMatthew Dillon return AH_FALSE;
921572ff6f6SMatthew Dillon return ah->ah_resetKeyCacheEntry(ah, *(const uint16_t *)args);
922572ff6f6SMatthew Dillon #ifdef AH_SUPPORT_WRITE_EEPROM
923572ff6f6SMatthew Dillon case HAL_DIAG_EEWRITE: {
924572ff6f6SMatthew Dillon const HAL_DIAG_EEVAL *ee;
925572ff6f6SMatthew Dillon if (argsize != sizeof(HAL_DIAG_EEVAL))
926572ff6f6SMatthew Dillon return AH_FALSE;
927572ff6f6SMatthew Dillon ee = (const HAL_DIAG_EEVAL *)args;
928572ff6f6SMatthew Dillon return ath_hal_eepromWrite(ah, ee->ee_off, ee->ee_data);
929572ff6f6SMatthew Dillon }
930572ff6f6SMatthew Dillon #endif /* AH_SUPPORT_WRITE_EEPROM */
931572ff6f6SMatthew Dillon #endif /* AH_PRIVATE_DIAG */
932572ff6f6SMatthew Dillon case HAL_DIAG_11NCOMPAT:
933572ff6f6SMatthew Dillon if (argsize == 0) {
934572ff6f6SMatthew Dillon *resultsize = sizeof(uint32_t);
935572ff6f6SMatthew Dillon *((uint32_t *)(*result)) =
936572ff6f6SMatthew Dillon AH_PRIVATE(ah)->ah_11nCompat;
937572ff6f6SMatthew Dillon } else if (argsize == sizeof(uint32_t)) {
938572ff6f6SMatthew Dillon AH_PRIVATE(ah)->ah_11nCompat = *(const uint32_t *)args;
939572ff6f6SMatthew Dillon } else
940572ff6f6SMatthew Dillon return AH_FALSE;
941572ff6f6SMatthew Dillon return AH_TRUE;
942572ff6f6SMatthew Dillon }
943572ff6f6SMatthew Dillon return AH_FALSE;
944572ff6f6SMatthew Dillon }
945572ff6f6SMatthew Dillon
946572ff6f6SMatthew Dillon /*
947572ff6f6SMatthew Dillon * Set the properties of the tx queue with the parameters
948572ff6f6SMatthew Dillon * from qInfo.
949572ff6f6SMatthew Dillon */
950572ff6f6SMatthew Dillon HAL_BOOL
ath_hal_setTxQProps(struct ath_hal * ah,HAL_TX_QUEUE_INFO * qi,const HAL_TXQ_INFO * qInfo)951572ff6f6SMatthew Dillon ath_hal_setTxQProps(struct ath_hal *ah,
952572ff6f6SMatthew Dillon HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo)
953572ff6f6SMatthew Dillon {
954572ff6f6SMatthew Dillon uint32_t cw;
955572ff6f6SMatthew Dillon
956572ff6f6SMatthew Dillon if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
957572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
958572ff6f6SMatthew Dillon "%s: inactive queue\n", __func__);
959572ff6f6SMatthew Dillon return AH_FALSE;
960572ff6f6SMatthew Dillon }
961572ff6f6SMatthew Dillon /* XXX validate parameters */
962572ff6f6SMatthew Dillon qi->tqi_ver = qInfo->tqi_ver;
963572ff6f6SMatthew Dillon qi->tqi_subtype = qInfo->tqi_subtype;
964572ff6f6SMatthew Dillon qi->tqi_qflags = qInfo->tqi_qflags;
965572ff6f6SMatthew Dillon qi->tqi_priority = qInfo->tqi_priority;
966572ff6f6SMatthew Dillon if (qInfo->tqi_aifs != HAL_TXQ_USEDEFAULT)
967572ff6f6SMatthew Dillon qi->tqi_aifs = AH_MIN(qInfo->tqi_aifs, 255);
968572ff6f6SMatthew Dillon else
969572ff6f6SMatthew Dillon qi->tqi_aifs = INIT_AIFS;
970572ff6f6SMatthew Dillon if (qInfo->tqi_cwmin != HAL_TXQ_USEDEFAULT) {
971572ff6f6SMatthew Dillon cw = AH_MIN(qInfo->tqi_cwmin, 1024);
972572ff6f6SMatthew Dillon /* make sure that the CWmin is of the form (2^n - 1) */
973572ff6f6SMatthew Dillon qi->tqi_cwmin = 1;
974572ff6f6SMatthew Dillon while (qi->tqi_cwmin < cw)
975572ff6f6SMatthew Dillon qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1;
976572ff6f6SMatthew Dillon } else
977572ff6f6SMatthew Dillon qi->tqi_cwmin = qInfo->tqi_cwmin;
978572ff6f6SMatthew Dillon if (qInfo->tqi_cwmax != HAL_TXQ_USEDEFAULT) {
979572ff6f6SMatthew Dillon cw = AH_MIN(qInfo->tqi_cwmax, 1024);
980572ff6f6SMatthew Dillon /* make sure that the CWmax is of the form (2^n - 1) */
981572ff6f6SMatthew Dillon qi->tqi_cwmax = 1;
982572ff6f6SMatthew Dillon while (qi->tqi_cwmax < cw)
983572ff6f6SMatthew Dillon qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1;
984572ff6f6SMatthew Dillon } else
985572ff6f6SMatthew Dillon qi->tqi_cwmax = INIT_CWMAX;
986572ff6f6SMatthew Dillon /* Set retry limit values */
987572ff6f6SMatthew Dillon if (qInfo->tqi_shretry != 0)
988572ff6f6SMatthew Dillon qi->tqi_shretry = AH_MIN(qInfo->tqi_shretry, 15);
989572ff6f6SMatthew Dillon else
990572ff6f6SMatthew Dillon qi->tqi_shretry = INIT_SH_RETRY;
991572ff6f6SMatthew Dillon if (qInfo->tqi_lgretry != 0)
992572ff6f6SMatthew Dillon qi->tqi_lgretry = AH_MIN(qInfo->tqi_lgretry, 15);
993572ff6f6SMatthew Dillon else
994572ff6f6SMatthew Dillon qi->tqi_lgretry = INIT_LG_RETRY;
995572ff6f6SMatthew Dillon qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod;
996572ff6f6SMatthew Dillon qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit;
997572ff6f6SMatthew Dillon qi->tqi_burstTime = qInfo->tqi_burstTime;
998572ff6f6SMatthew Dillon qi->tqi_readyTime = qInfo->tqi_readyTime;
999572ff6f6SMatthew Dillon
1000572ff6f6SMatthew Dillon switch (qInfo->tqi_subtype) {
1001572ff6f6SMatthew Dillon case HAL_WME_UPSD:
1002572ff6f6SMatthew Dillon if (qi->tqi_type == HAL_TX_QUEUE_DATA)
1003572ff6f6SMatthew Dillon qi->tqi_intFlags = HAL_TXQ_USE_LOCKOUT_BKOFF_DIS;
1004572ff6f6SMatthew Dillon break;
1005572ff6f6SMatthew Dillon default:
1006572ff6f6SMatthew Dillon break; /* NB: silence compiler */
1007572ff6f6SMatthew Dillon }
1008572ff6f6SMatthew Dillon return AH_TRUE;
1009572ff6f6SMatthew Dillon }
1010572ff6f6SMatthew Dillon
1011572ff6f6SMatthew Dillon HAL_BOOL
ath_hal_getTxQProps(struct ath_hal * ah,HAL_TXQ_INFO * qInfo,const HAL_TX_QUEUE_INFO * qi)1012572ff6f6SMatthew Dillon ath_hal_getTxQProps(struct ath_hal *ah,
1013572ff6f6SMatthew Dillon HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi)
1014572ff6f6SMatthew Dillon {
1015572ff6f6SMatthew Dillon if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) {
1016572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_TXQUEUE,
1017572ff6f6SMatthew Dillon "%s: inactive queue\n", __func__);
1018572ff6f6SMatthew Dillon return AH_FALSE;
1019572ff6f6SMatthew Dillon }
1020572ff6f6SMatthew Dillon
1021572ff6f6SMatthew Dillon qInfo->tqi_qflags = qi->tqi_qflags;
1022572ff6f6SMatthew Dillon qInfo->tqi_ver = qi->tqi_ver;
1023572ff6f6SMatthew Dillon qInfo->tqi_subtype = qi->tqi_subtype;
1024572ff6f6SMatthew Dillon qInfo->tqi_qflags = qi->tqi_qflags;
1025572ff6f6SMatthew Dillon qInfo->tqi_priority = qi->tqi_priority;
1026572ff6f6SMatthew Dillon qInfo->tqi_aifs = qi->tqi_aifs;
1027572ff6f6SMatthew Dillon qInfo->tqi_cwmin = qi->tqi_cwmin;
1028572ff6f6SMatthew Dillon qInfo->tqi_cwmax = qi->tqi_cwmax;
1029572ff6f6SMatthew Dillon qInfo->tqi_shretry = qi->tqi_shretry;
1030572ff6f6SMatthew Dillon qInfo->tqi_lgretry = qi->tqi_lgretry;
1031572ff6f6SMatthew Dillon qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod;
1032572ff6f6SMatthew Dillon qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit;
1033572ff6f6SMatthew Dillon qInfo->tqi_burstTime = qi->tqi_burstTime;
1034572ff6f6SMatthew Dillon qInfo->tqi_readyTime = qi->tqi_readyTime;
1035572ff6f6SMatthew Dillon return AH_TRUE;
1036572ff6f6SMatthew Dillon }
1037572ff6f6SMatthew Dillon
1038572ff6f6SMatthew Dillon /* 11a Turbo 11b 11g 108g */
1039572ff6f6SMatthew Dillon static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93 };
1040572ff6f6SMatthew Dillon
1041572ff6f6SMatthew Dillon /*
1042572ff6f6SMatthew Dillon * Read the current channel noise floor and return.
1043572ff6f6SMatthew Dillon * If nf cal hasn't finished, channel noise floor should be 0
1044572ff6f6SMatthew Dillon * and we return a nominal value based on band and frequency.
1045572ff6f6SMatthew Dillon *
1046572ff6f6SMatthew Dillon * NB: This is a private routine used by per-chip code to
1047572ff6f6SMatthew Dillon * implement the ah_getChanNoise method.
1048572ff6f6SMatthew Dillon */
1049572ff6f6SMatthew Dillon int16_t
ath_hal_getChanNoise(struct ath_hal * ah,const struct ieee80211_channel * chan)1050572ff6f6SMatthew Dillon ath_hal_getChanNoise(struct ath_hal *ah, const struct ieee80211_channel *chan)
1051572ff6f6SMatthew Dillon {
1052572ff6f6SMatthew Dillon HAL_CHANNEL_INTERNAL *ichan;
1053572ff6f6SMatthew Dillon
1054572ff6f6SMatthew Dillon ichan = ath_hal_checkchannel(ah, chan);
1055572ff6f6SMatthew Dillon if (ichan == AH_NULL) {
1056572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_NFCAL,
1057572ff6f6SMatthew Dillon "%s: invalid channel %u/0x%x; no mapping\n",
1058572ff6f6SMatthew Dillon __func__, chan->ic_freq, chan->ic_flags);
1059572ff6f6SMatthew Dillon return 0;
1060572ff6f6SMatthew Dillon }
1061572ff6f6SMatthew Dillon if (ichan->rawNoiseFloor == 0) {
1062572ff6f6SMatthew Dillon WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan);
1063572ff6f6SMatthew Dillon
1064572ff6f6SMatthew Dillon HALASSERT(mode < WIRELESS_MODE_MAX);
1065572ff6f6SMatthew Dillon return NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan);
1066572ff6f6SMatthew Dillon } else
1067572ff6f6SMatthew Dillon return ichan->rawNoiseFloor + ichan->noiseFloorAdjust;
1068572ff6f6SMatthew Dillon }
1069572ff6f6SMatthew Dillon
1070572ff6f6SMatthew Dillon /*
1071572ff6f6SMatthew Dillon * Fetch the current setup of ctl/ext noise floor values.
1072572ff6f6SMatthew Dillon *
1073572ff6f6SMatthew Dillon * If the CHANNEL_MIMO_NF_VALID flag isn't set, the array is simply
1074572ff6f6SMatthew Dillon * populated with values from NOISE_FLOOR[] + ath_hal_getNfAdjust().
1075572ff6f6SMatthew Dillon *
1076572ff6f6SMatthew Dillon * The caller must supply ctl/ext NF arrays which are at least
1077572ff6f6SMatthew Dillon * AH_MAX_CHAINS entries long.
1078572ff6f6SMatthew Dillon */
1079572ff6f6SMatthew Dillon int
ath_hal_get_mimo_chan_noise(struct ath_hal * ah,const struct ieee80211_channel * chan,int16_t * nf_ctl,int16_t * nf_ext)1080572ff6f6SMatthew Dillon ath_hal_get_mimo_chan_noise(struct ath_hal *ah,
1081572ff6f6SMatthew Dillon const struct ieee80211_channel *chan, int16_t *nf_ctl,
1082572ff6f6SMatthew Dillon int16_t *nf_ext)
1083572ff6f6SMatthew Dillon {
1084572ff6f6SMatthew Dillon #ifdef AH_SUPPORT_AR5416
1085572ff6f6SMatthew Dillon HAL_CHANNEL_INTERNAL *ichan;
1086572ff6f6SMatthew Dillon int i;
1087572ff6f6SMatthew Dillon
1088572ff6f6SMatthew Dillon ichan = ath_hal_checkchannel(ah, chan);
1089572ff6f6SMatthew Dillon if (ichan == AH_NULL) {
1090572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_NFCAL,
1091572ff6f6SMatthew Dillon "%s: invalid channel %u/0x%x; no mapping\n",
1092572ff6f6SMatthew Dillon __func__, chan->ic_freq, chan->ic_flags);
1093572ff6f6SMatthew Dillon for (i = 0; i < AH_MAX_CHAINS; i++) {
1094572ff6f6SMatthew Dillon nf_ctl[i] = nf_ext[i] = 0;
1095572ff6f6SMatthew Dillon }
1096572ff6f6SMatthew Dillon return 0;
1097572ff6f6SMatthew Dillon }
1098572ff6f6SMatthew Dillon
1099572ff6f6SMatthew Dillon /* Return 0 if there's no valid MIMO values (yet) */
1100572ff6f6SMatthew Dillon if (! (ichan->privFlags & CHANNEL_MIMO_NF_VALID)) {
1101572ff6f6SMatthew Dillon for (i = 0; i < AH_MAX_CHAINS; i++) {
1102572ff6f6SMatthew Dillon nf_ctl[i] = nf_ext[i] = 0;
1103572ff6f6SMatthew Dillon }
1104572ff6f6SMatthew Dillon return 0;
1105572ff6f6SMatthew Dillon }
1106572ff6f6SMatthew Dillon if (ichan->rawNoiseFloor == 0) {
1107572ff6f6SMatthew Dillon WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan);
1108572ff6f6SMatthew Dillon HALASSERT(mode < WIRELESS_MODE_MAX);
1109572ff6f6SMatthew Dillon /*
1110572ff6f6SMatthew Dillon * See the comment below - this could cause issues for
1111572ff6f6SMatthew Dillon * stations which have a very low RSSI, below the
1112572ff6f6SMatthew Dillon * 'normalised' NF values in NOISE_FLOOR[].
1113572ff6f6SMatthew Dillon */
1114572ff6f6SMatthew Dillon for (i = 0; i < AH_MAX_CHAINS; i++) {
1115572ff6f6SMatthew Dillon nf_ctl[i] = nf_ext[i] = NOISE_FLOOR[mode] +
1116572ff6f6SMatthew Dillon ath_hal_getNfAdjust(ah, ichan);
1117572ff6f6SMatthew Dillon }
1118572ff6f6SMatthew Dillon return 1;
1119572ff6f6SMatthew Dillon } else {
1120572ff6f6SMatthew Dillon /*
1121572ff6f6SMatthew Dillon * The value returned here from a MIMO radio is presumed to be
1122572ff6f6SMatthew Dillon * "good enough" as a NF calculation. As RSSI values are calculated
1123572ff6f6SMatthew Dillon * against this, an adjusted NF may be higher than the RSSI value
1124572ff6f6SMatthew Dillon * returned from a vary weak station, resulting in an obscenely
1125572ff6f6SMatthew Dillon * high signal strength calculation being returned.
1126572ff6f6SMatthew Dillon *
1127572ff6f6SMatthew Dillon * This should be re-evaluated at a later date, along with any
1128572ff6f6SMatthew Dillon * signal strength calculations which are made. Quite likely the
1129572ff6f6SMatthew Dillon * RSSI values will need to be adjusted to ensure the calculations
1130572ff6f6SMatthew Dillon * don't "wrap" when RSSI is less than the "adjusted" NF value.
1131572ff6f6SMatthew Dillon * ("Adjust" here is via ichan->noiseFloorAdjust.)
1132572ff6f6SMatthew Dillon */
1133572ff6f6SMatthew Dillon for (i = 0; i < AH_MAX_CHAINS; i++) {
1134572ff6f6SMatthew Dillon nf_ctl[i] = ichan->noiseFloorCtl[i] + ath_hal_getNfAdjust(ah, ichan);
1135572ff6f6SMatthew Dillon nf_ext[i] = ichan->noiseFloorExt[i] + ath_hal_getNfAdjust(ah, ichan);
1136572ff6f6SMatthew Dillon }
1137572ff6f6SMatthew Dillon return 1;
1138572ff6f6SMatthew Dillon }
1139572ff6f6SMatthew Dillon #else
1140572ff6f6SMatthew Dillon return 0;
1141572ff6f6SMatthew Dillon #endif /* AH_SUPPORT_AR5416 */
1142572ff6f6SMatthew Dillon }
1143572ff6f6SMatthew Dillon
1144572ff6f6SMatthew Dillon /*
1145572ff6f6SMatthew Dillon * Process all valid raw noise floors into the dBm noise floor values.
1146572ff6f6SMatthew Dillon * Though our device has no reference for a dBm noise floor, we perform
1147572ff6f6SMatthew Dillon * a relative minimization of NF's based on the lowest NF found across a
1148572ff6f6SMatthew Dillon * channel scan.
1149572ff6f6SMatthew Dillon */
1150572ff6f6SMatthew Dillon void
ath_hal_process_noisefloor(struct ath_hal * ah)1151572ff6f6SMatthew Dillon ath_hal_process_noisefloor(struct ath_hal *ah)
1152572ff6f6SMatthew Dillon {
1153572ff6f6SMatthew Dillon HAL_CHANNEL_INTERNAL *c;
1154572ff6f6SMatthew Dillon int16_t correct2, correct5;
1155572ff6f6SMatthew Dillon int16_t lowest2, lowest5;
1156572ff6f6SMatthew Dillon int i;
1157572ff6f6SMatthew Dillon
1158572ff6f6SMatthew Dillon /*
1159572ff6f6SMatthew Dillon * Find the lowest 2GHz and 5GHz noise floor values after adjusting
1160572ff6f6SMatthew Dillon * for statistically recorded NF/channel deviation.
1161572ff6f6SMatthew Dillon */
1162572ff6f6SMatthew Dillon correct2 = lowest2 = 0;
1163572ff6f6SMatthew Dillon correct5 = lowest5 = 0;
1164572ff6f6SMatthew Dillon for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) {
1165572ff6f6SMatthew Dillon WIRELESS_MODE mode;
1166572ff6f6SMatthew Dillon int16_t nf;
1167572ff6f6SMatthew Dillon
1168572ff6f6SMatthew Dillon c = &AH_PRIVATE(ah)->ah_channels[i];
1169572ff6f6SMatthew Dillon if (c->rawNoiseFloor >= 0)
1170572ff6f6SMatthew Dillon continue;
1171572ff6f6SMatthew Dillon /* XXX can't identify proper mode */
1172572ff6f6SMatthew Dillon mode = IS_CHAN_5GHZ(c) ? WIRELESS_MODE_11a : WIRELESS_MODE_11g;
1173572ff6f6SMatthew Dillon nf = c->rawNoiseFloor + NOISE_FLOOR[mode] +
1174572ff6f6SMatthew Dillon ath_hal_getNfAdjust(ah, c);
1175572ff6f6SMatthew Dillon if (IS_CHAN_5GHZ(c)) {
1176572ff6f6SMatthew Dillon if (nf < lowest5) {
1177572ff6f6SMatthew Dillon lowest5 = nf;
1178572ff6f6SMatthew Dillon correct5 = NOISE_FLOOR[mode] -
1179572ff6f6SMatthew Dillon (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c));
1180572ff6f6SMatthew Dillon }
1181572ff6f6SMatthew Dillon } else {
1182572ff6f6SMatthew Dillon if (nf < lowest2) {
1183572ff6f6SMatthew Dillon lowest2 = nf;
1184572ff6f6SMatthew Dillon correct2 = NOISE_FLOOR[mode] -
1185572ff6f6SMatthew Dillon (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c));
1186572ff6f6SMatthew Dillon }
1187572ff6f6SMatthew Dillon }
1188572ff6f6SMatthew Dillon }
1189572ff6f6SMatthew Dillon
1190572ff6f6SMatthew Dillon /* Correct the channels to reach the expected NF value */
1191572ff6f6SMatthew Dillon for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) {
1192572ff6f6SMatthew Dillon c = &AH_PRIVATE(ah)->ah_channels[i];
1193572ff6f6SMatthew Dillon if (c->rawNoiseFloor >= 0)
1194572ff6f6SMatthew Dillon continue;
1195572ff6f6SMatthew Dillon /* Apply correction factor */
1196572ff6f6SMatthew Dillon c->noiseFloorAdjust = ath_hal_getNfAdjust(ah, c) +
1197572ff6f6SMatthew Dillon (IS_CHAN_5GHZ(c) ? correct5 : correct2);
1198572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_NFCAL, "%u raw nf %d adjust %d\n",
1199572ff6f6SMatthew Dillon c->channel, c->rawNoiseFloor, c->noiseFloorAdjust);
1200572ff6f6SMatthew Dillon }
1201572ff6f6SMatthew Dillon }
1202572ff6f6SMatthew Dillon
1203572ff6f6SMatthew Dillon /*
1204572ff6f6SMatthew Dillon * INI support routines.
1205572ff6f6SMatthew Dillon */
1206572ff6f6SMatthew Dillon
1207572ff6f6SMatthew Dillon int
ath_hal_ini_write(struct ath_hal * ah,const HAL_INI_ARRAY * ia,int col,int regWr)1208572ff6f6SMatthew Dillon ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia,
1209572ff6f6SMatthew Dillon int col, int regWr)
1210572ff6f6SMatthew Dillon {
1211572ff6f6SMatthew Dillon int r;
1212572ff6f6SMatthew Dillon
1213572ff6f6SMatthew Dillon HALASSERT(col < ia->cols);
1214572ff6f6SMatthew Dillon for (r = 0; r < ia->rows; r++) {
1215572ff6f6SMatthew Dillon OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0),
1216572ff6f6SMatthew Dillon HAL_INI_VAL(ia, r, col));
1217572ff6f6SMatthew Dillon
1218572ff6f6SMatthew Dillon /* Analog shift register delay seems needed for Merlin - PR kern/154220 */
1219572ff6f6SMatthew Dillon if (HAL_INI_VAL(ia, r, 0) >= 0x7800 && HAL_INI_VAL(ia, r, 0) < 0x7900)
1220572ff6f6SMatthew Dillon OS_DELAY(100);
1221572ff6f6SMatthew Dillon
1222572ff6f6SMatthew Dillon DMA_YIELD(regWr);
1223572ff6f6SMatthew Dillon }
1224572ff6f6SMatthew Dillon return regWr;
1225572ff6f6SMatthew Dillon }
1226572ff6f6SMatthew Dillon
1227572ff6f6SMatthew Dillon void
ath_hal_ini_bank_setup(uint32_t data[],const HAL_INI_ARRAY * ia,int col)1228572ff6f6SMatthew Dillon ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col)
1229572ff6f6SMatthew Dillon {
1230572ff6f6SMatthew Dillon int r;
1231572ff6f6SMatthew Dillon
1232572ff6f6SMatthew Dillon HALASSERT(col < ia->cols);
1233572ff6f6SMatthew Dillon for (r = 0; r < ia->rows; r++)
1234572ff6f6SMatthew Dillon data[r] = HAL_INI_VAL(ia, r, col);
1235572ff6f6SMatthew Dillon }
1236572ff6f6SMatthew Dillon
1237572ff6f6SMatthew Dillon int
ath_hal_ini_bank_write(struct ath_hal * ah,const HAL_INI_ARRAY * ia,const uint32_t data[],int regWr)1238572ff6f6SMatthew Dillon ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia,
1239572ff6f6SMatthew Dillon const uint32_t data[], int regWr)
1240572ff6f6SMatthew Dillon {
1241572ff6f6SMatthew Dillon int r;
1242572ff6f6SMatthew Dillon
1243572ff6f6SMatthew Dillon for (r = 0; r < ia->rows; r++) {
1244572ff6f6SMatthew Dillon OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), data[r]);
1245572ff6f6SMatthew Dillon DMA_YIELD(regWr);
1246572ff6f6SMatthew Dillon }
1247572ff6f6SMatthew Dillon return regWr;
1248572ff6f6SMatthew Dillon }
1249572ff6f6SMatthew Dillon
1250572ff6f6SMatthew Dillon /*
1251572ff6f6SMatthew Dillon * These are EEPROM board related routines which should likely live in
1252572ff6f6SMatthew Dillon * a helper library of some sort.
1253572ff6f6SMatthew Dillon */
1254572ff6f6SMatthew Dillon
1255572ff6f6SMatthew Dillon /**************************************************************
1256572ff6f6SMatthew Dillon * ath_ee_getLowerUppderIndex
1257572ff6f6SMatthew Dillon *
1258572ff6f6SMatthew Dillon * Return indices surrounding the value in sorted integer lists.
1259572ff6f6SMatthew Dillon * Requirement: the input list must be monotonically increasing
1260572ff6f6SMatthew Dillon * and populated up to the list size
1261572ff6f6SMatthew Dillon * Returns: match is set if an index in the array matches exactly
1262572ff6f6SMatthew Dillon * or a the target is before or after the range of the array.
1263572ff6f6SMatthew Dillon */
1264572ff6f6SMatthew Dillon HAL_BOOL
ath_ee_getLowerUpperIndex(uint8_t target,uint8_t * pList,uint16_t listSize,uint16_t * indexL,uint16_t * indexR)1265572ff6f6SMatthew Dillon ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize,
1266572ff6f6SMatthew Dillon uint16_t *indexL, uint16_t *indexR)
1267572ff6f6SMatthew Dillon {
1268572ff6f6SMatthew Dillon uint16_t i;
1269572ff6f6SMatthew Dillon
1270572ff6f6SMatthew Dillon /*
1271572ff6f6SMatthew Dillon * Check first and last elements for beyond ordered array cases.
1272572ff6f6SMatthew Dillon */
1273572ff6f6SMatthew Dillon if (target <= pList[0]) {
1274572ff6f6SMatthew Dillon *indexL = *indexR = 0;
1275572ff6f6SMatthew Dillon return AH_TRUE;
1276572ff6f6SMatthew Dillon }
1277572ff6f6SMatthew Dillon if (target >= pList[listSize-1]) {
1278572ff6f6SMatthew Dillon *indexL = *indexR = (uint16_t)(listSize - 1);
1279572ff6f6SMatthew Dillon return AH_TRUE;
1280572ff6f6SMatthew Dillon }
1281572ff6f6SMatthew Dillon
1282572ff6f6SMatthew Dillon /* look for value being near or between 2 values in list */
1283572ff6f6SMatthew Dillon for (i = 0; i < listSize - 1; i++) {
1284572ff6f6SMatthew Dillon /*
1285572ff6f6SMatthew Dillon * If value is close to the current value of the list
1286572ff6f6SMatthew Dillon * then target is not between values, it is one of the values
1287572ff6f6SMatthew Dillon */
1288572ff6f6SMatthew Dillon if (pList[i] == target) {
1289572ff6f6SMatthew Dillon *indexL = *indexR = i;
1290572ff6f6SMatthew Dillon return AH_TRUE;
1291572ff6f6SMatthew Dillon }
1292572ff6f6SMatthew Dillon /*
1293572ff6f6SMatthew Dillon * Look for value being between current value and next value
1294572ff6f6SMatthew Dillon * if so return these 2 values
1295572ff6f6SMatthew Dillon */
1296572ff6f6SMatthew Dillon if (target < pList[i + 1]) {
1297572ff6f6SMatthew Dillon *indexL = i;
1298572ff6f6SMatthew Dillon *indexR = (uint16_t)(i + 1);
1299572ff6f6SMatthew Dillon return AH_FALSE;
1300572ff6f6SMatthew Dillon }
1301572ff6f6SMatthew Dillon }
1302572ff6f6SMatthew Dillon HALASSERT(0);
1303572ff6f6SMatthew Dillon *indexL = *indexR = 0;
1304572ff6f6SMatthew Dillon return AH_FALSE;
1305572ff6f6SMatthew Dillon }
1306572ff6f6SMatthew Dillon
1307572ff6f6SMatthew Dillon /**************************************************************
1308572ff6f6SMatthew Dillon * ath_ee_FillVpdTable
1309572ff6f6SMatthew Dillon *
1310572ff6f6SMatthew Dillon * Fill the Vpdlist for indices Pmax-Pmin
1311572ff6f6SMatthew Dillon * Note: pwrMin, pwrMax and Vpdlist are all in dBm * 4
1312572ff6f6SMatthew Dillon */
1313572ff6f6SMatthew Dillon HAL_BOOL
ath_ee_FillVpdTable(uint8_t pwrMin,uint8_t pwrMax,uint8_t * pPwrList,uint8_t * pVpdList,uint16_t numIntercepts,uint8_t * pRetVpdList)1314572ff6f6SMatthew Dillon ath_ee_FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList,
1315572ff6f6SMatthew Dillon uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList)
1316572ff6f6SMatthew Dillon {
1317572ff6f6SMatthew Dillon uint16_t i, k;
1318572ff6f6SMatthew Dillon uint8_t currPwr = pwrMin;
1319572ff6f6SMatthew Dillon uint16_t idxL, idxR;
1320572ff6f6SMatthew Dillon
1321572ff6f6SMatthew Dillon HALASSERT(pwrMax > pwrMin);
1322572ff6f6SMatthew Dillon for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) {
1323572ff6f6SMatthew Dillon ath_ee_getLowerUpperIndex(currPwr, pPwrList, numIntercepts,
1324572ff6f6SMatthew Dillon &(idxL), &(idxR));
1325572ff6f6SMatthew Dillon if (idxR < 1)
1326572ff6f6SMatthew Dillon idxR = 1; /* extrapolate below */
1327572ff6f6SMatthew Dillon if (idxL == numIntercepts - 1)
1328572ff6f6SMatthew Dillon idxL = (uint16_t)(numIntercepts - 2); /* extrapolate above */
1329572ff6f6SMatthew Dillon if (pPwrList[idxL] == pPwrList[idxR])
1330572ff6f6SMatthew Dillon k = pVpdList[idxL];
1331572ff6f6SMatthew Dillon else
1332572ff6f6SMatthew Dillon k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) /
1333572ff6f6SMatthew Dillon (pPwrList[idxR] - pPwrList[idxL]) );
1334572ff6f6SMatthew Dillon HALASSERT(k < 256);
1335572ff6f6SMatthew Dillon pRetVpdList[i] = (uint8_t)k;
1336572ff6f6SMatthew Dillon currPwr += 2; /* half dB steps */
1337572ff6f6SMatthew Dillon }
1338572ff6f6SMatthew Dillon
1339572ff6f6SMatthew Dillon return AH_TRUE;
1340572ff6f6SMatthew Dillon }
1341572ff6f6SMatthew Dillon
1342572ff6f6SMatthew Dillon /**************************************************************************
1343572ff6f6SMatthew Dillon * ath_ee_interpolate
1344572ff6f6SMatthew Dillon *
1345572ff6f6SMatthew Dillon * Returns signed interpolated or the scaled up interpolated value
1346572ff6f6SMatthew Dillon */
1347572ff6f6SMatthew Dillon int16_t
ath_ee_interpolate(uint16_t target,uint16_t srcLeft,uint16_t srcRight,int16_t targetLeft,int16_t targetRight)1348572ff6f6SMatthew Dillon ath_ee_interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight,
1349572ff6f6SMatthew Dillon int16_t targetLeft, int16_t targetRight)
1350572ff6f6SMatthew Dillon {
1351572ff6f6SMatthew Dillon int16_t rv;
1352572ff6f6SMatthew Dillon
1353572ff6f6SMatthew Dillon if (srcRight == srcLeft) {
1354572ff6f6SMatthew Dillon rv = targetLeft;
1355572ff6f6SMatthew Dillon } else {
1356572ff6f6SMatthew Dillon rv = (int16_t)( ((target - srcLeft) * targetRight +
1357572ff6f6SMatthew Dillon (srcRight - target) * targetLeft) / (srcRight - srcLeft) );
1358572ff6f6SMatthew Dillon }
1359572ff6f6SMatthew Dillon return rv;
1360572ff6f6SMatthew Dillon }
1361572ff6f6SMatthew Dillon
1362572ff6f6SMatthew Dillon /*
1363572ff6f6SMatthew Dillon * Adjust the TSF.
1364572ff6f6SMatthew Dillon */
1365572ff6f6SMatthew Dillon void
ath_hal_adjusttsf(struct ath_hal * ah,int32_t tsfdelta)1366572ff6f6SMatthew Dillon ath_hal_adjusttsf(struct ath_hal *ah, int32_t tsfdelta)
1367572ff6f6SMatthew Dillon {
1368572ff6f6SMatthew Dillon /* XXX handle wrap/overflow */
1369572ff6f6SMatthew Dillon OS_REG_WRITE(ah, AR_TSF_L32, OS_REG_READ(ah, AR_TSF_L32) + tsfdelta);
1370572ff6f6SMatthew Dillon }
1371572ff6f6SMatthew Dillon
1372572ff6f6SMatthew Dillon /*
1373572ff6f6SMatthew Dillon * Enable or disable CCA.
1374572ff6f6SMatthew Dillon */
1375572ff6f6SMatthew Dillon void
ath_hal_setcca(struct ath_hal * ah,int ena)1376572ff6f6SMatthew Dillon ath_hal_setcca(struct ath_hal *ah, int ena)
1377572ff6f6SMatthew Dillon {
1378572ff6f6SMatthew Dillon /*
1379572ff6f6SMatthew Dillon * NB: fill me in; this is not provided by default because disabling
1380572ff6f6SMatthew Dillon * CCA in most locales violates regulatory.
1381572ff6f6SMatthew Dillon */
1382572ff6f6SMatthew Dillon }
1383572ff6f6SMatthew Dillon
1384572ff6f6SMatthew Dillon /*
1385572ff6f6SMatthew Dillon * Get CCA setting.
1386572ff6f6SMatthew Dillon */
1387572ff6f6SMatthew Dillon int
ath_hal_getcca(struct ath_hal * ah)1388572ff6f6SMatthew Dillon ath_hal_getcca(struct ath_hal *ah)
1389572ff6f6SMatthew Dillon {
1390572ff6f6SMatthew Dillon u_int32_t diag;
1391572ff6f6SMatthew Dillon if (ath_hal_getcapability(ah, HAL_CAP_DIAG, 0, &diag) != HAL_OK)
1392572ff6f6SMatthew Dillon return 1;
1393572ff6f6SMatthew Dillon return ((diag & 0x500000) == 0);
1394572ff6f6SMatthew Dillon }
1395572ff6f6SMatthew Dillon
1396572ff6f6SMatthew Dillon /*
1397572ff6f6SMatthew Dillon * This routine is only needed when supporting EEPROM-in-RAM setups
1398572ff6f6SMatthew Dillon * (eg embedded SoCs and on-board PCI/PCIe devices.)
1399572ff6f6SMatthew Dillon */
1400572ff6f6SMatthew Dillon /* NB: This is in 16 bit words; not bytes */
1401572ff6f6SMatthew Dillon /* XXX This doesn't belong here! */
1402572ff6f6SMatthew Dillon #define ATH_DATA_EEPROM_SIZE 2048
1403572ff6f6SMatthew Dillon
1404572ff6f6SMatthew Dillon HAL_BOOL
ath_hal_EepromDataRead(struct ath_hal * ah,u_int off,uint16_t * data)1405572ff6f6SMatthew Dillon ath_hal_EepromDataRead(struct ath_hal *ah, u_int off, uint16_t *data)
1406572ff6f6SMatthew Dillon {
1407572ff6f6SMatthew Dillon if (ah->ah_eepromdata == AH_NULL) {
1408572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_ANY, "%s: no eeprom data!\n", __func__);
1409572ff6f6SMatthew Dillon return AH_FALSE;
1410572ff6f6SMatthew Dillon }
1411572ff6f6SMatthew Dillon if (off > ATH_DATA_EEPROM_SIZE) {
1412572ff6f6SMatthew Dillon HALDEBUG(ah, HAL_DEBUG_ANY, "%s: offset %x > %x\n",
1413572ff6f6SMatthew Dillon __func__, off, ATH_DATA_EEPROM_SIZE);
1414572ff6f6SMatthew Dillon return AH_FALSE;
1415572ff6f6SMatthew Dillon }
1416572ff6f6SMatthew Dillon (*data) = ah->ah_eepromdata[off];
1417572ff6f6SMatthew Dillon return AH_TRUE;
1418572ff6f6SMatthew Dillon }
1419572ff6f6SMatthew Dillon
1420572ff6f6SMatthew Dillon /*
1421572ff6f6SMatthew Dillon * Do a 2GHz specific MHz->IEEE based on the hardware
1422572ff6f6SMatthew Dillon * frequency.
1423572ff6f6SMatthew Dillon *
1424572ff6f6SMatthew Dillon * This is the unmapped frequency which is programmed into the hardware.
1425572ff6f6SMatthew Dillon */
1426572ff6f6SMatthew Dillon int
ath_hal_mhz2ieee_2ghz(struct ath_hal * ah,int freq)1427*b14ca477SMatthew Dillon ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, int freq)
1428572ff6f6SMatthew Dillon {
1429572ff6f6SMatthew Dillon
1430*b14ca477SMatthew Dillon if (freq == 2484)
1431572ff6f6SMatthew Dillon return 14;
1432*b14ca477SMatthew Dillon if (freq < 2484)
1433*b14ca477SMatthew Dillon return ((int) freq - 2407) / 5;
1434572ff6f6SMatthew Dillon else
1435*b14ca477SMatthew Dillon return 15 + ((freq - 2512) / 20);
1436*b14ca477SMatthew Dillon }
1437*b14ca477SMatthew Dillon
1438*b14ca477SMatthew Dillon /*
1439*b14ca477SMatthew Dillon * Clear the current survey data.
1440*b14ca477SMatthew Dillon *
1441*b14ca477SMatthew Dillon * This should be done during a channel change.
1442*b14ca477SMatthew Dillon */
1443*b14ca477SMatthew Dillon void
ath_hal_survey_clear(struct ath_hal * ah)1444*b14ca477SMatthew Dillon ath_hal_survey_clear(struct ath_hal *ah)
1445*b14ca477SMatthew Dillon {
1446*b14ca477SMatthew Dillon OS_MEMZERO(&AH_PRIVATE(ah)->ah_chansurvey,
1447*b14ca477SMatthew Dillon sizeof(AH_PRIVATE(ah)->ah_chansurvey));
1448*b14ca477SMatthew Dillon }
1449*b14ca477SMatthew Dillon
1450*b14ca477SMatthew Dillon /*
1451*b14ca477SMatthew Dillon * Add a sample to the channel survey.
1452*b14ca477SMatthew Dillon */
1453*b14ca477SMatthew Dillon void
ath_hal_survey_add_sample(struct ath_hal * ah,HAL_SURVEY_SAMPLE * hs)1454*b14ca477SMatthew Dillon ath_hal_survey_add_sample(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs)
1455*b14ca477SMatthew Dillon {
1456*b14ca477SMatthew Dillon HAL_CHANNEL_SURVEY *cs;
1457*b14ca477SMatthew Dillon
1458*b14ca477SMatthew Dillon cs = &AH_PRIVATE(ah)->ah_chansurvey;
1459*b14ca477SMatthew Dillon
1460*b14ca477SMatthew Dillon OS_MEMCPY(&cs->samples[cs->cur_sample], hs, sizeof(*hs));
1461*b14ca477SMatthew Dillon cs->samples[cs->cur_sample].seq_num = cs->cur_seq;
1462*b14ca477SMatthew Dillon cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT;
1463*b14ca477SMatthew Dillon cs->cur_seq++;
1464572ff6f6SMatthew Dillon }
1465