Commit | Line | Data |
---|---|---|
f1dc5600 | 1 | /* |
cee075a2 | 2 | * Copyright (c) 2008-2009 Atheros Communications Inc. |
f1dc5600 S |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
990b70ab | 17 | #include "hw.h" |
f1dc5600 | 18 | |
cbe61d8a | 19 | static void ath9k_hw_set_txq_interrupts(struct ath_hw *ah, |
f1dc5600 S |
20 | struct ath9k_tx_queue_info *qi) |
21 | { | |
c46917bb LR |
22 | ath_print(ath9k_hw_common(ah), ATH_DBG_INTERRUPT, |
23 | "tx ok 0x%x err 0x%x desc 0x%x eol 0x%x urn 0x%x\n", | |
24 | ah->txok_interrupt_mask, ah->txerr_interrupt_mask, | |
25 | ah->txdesc_interrupt_mask, ah->txeol_interrupt_mask, | |
26 | ah->txurn_interrupt_mask); | |
f1dc5600 S |
27 | |
28 | REG_WRITE(ah, AR_IMR_S0, | |
2660b81a S |
29 | SM(ah->txok_interrupt_mask, AR_IMR_S0_QCU_TXOK) |
30 | | SM(ah->txdesc_interrupt_mask, AR_IMR_S0_QCU_TXDESC)); | |
f1dc5600 | 31 | REG_WRITE(ah, AR_IMR_S1, |
2660b81a S |
32 | SM(ah->txerr_interrupt_mask, AR_IMR_S1_QCU_TXERR) |
33 | | SM(ah->txeol_interrupt_mask, AR_IMR_S1_QCU_TXEOL)); | |
f1dc5600 | 34 | REG_RMW_FIELD(ah, AR_IMR_S2, |
2660b81a | 35 | AR_IMR_S2_QCU_TXURN, ah->txurn_interrupt_mask); |
f1dc5600 S |
36 | } |
37 | ||
cbe61d8a | 38 | u32 ath9k_hw_gettxbuf(struct ath_hw *ah, u32 q) |
f1dc5600 S |
39 | { |
40 | return REG_READ(ah, AR_QTXDP(q)); | |
41 | } | |
7322fd19 | 42 | EXPORT_SYMBOL(ath9k_hw_gettxbuf); |
f1dc5600 | 43 | |
54e4cec6 | 44 | void ath9k_hw_puttxbuf(struct ath_hw *ah, u32 q, u32 txdp) |
f1dc5600 S |
45 | { |
46 | REG_WRITE(ah, AR_QTXDP(q), txdp); | |
f1dc5600 | 47 | } |
7322fd19 | 48 | EXPORT_SYMBOL(ath9k_hw_puttxbuf); |
f1dc5600 | 49 | |
54e4cec6 | 50 | void ath9k_hw_txstart(struct ath_hw *ah, u32 q) |
f1dc5600 | 51 | { |
c46917bb LR |
52 | ath_print(ath9k_hw_common(ah), ATH_DBG_QUEUE, |
53 | "Enable TXE on queue: %u\n", q); | |
f1dc5600 | 54 | REG_WRITE(ah, AR_Q_TXE, 1 << q); |
f1dc5600 | 55 | } |
7322fd19 | 56 | EXPORT_SYMBOL(ath9k_hw_txstart); |
f1dc5600 | 57 | |
cbe61d8a | 58 | u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q) |
f1dc5600 S |
59 | { |
60 | u32 npend; | |
61 | ||
62 | npend = REG_READ(ah, AR_QSTS(q)) & AR_Q_STS_PEND_FR_CNT; | |
63 | if (npend == 0) { | |
64 | ||
65 | if (REG_READ(ah, AR_Q_TXE) & (1 << q)) | |
66 | npend = 1; | |
67 | } | |
68 | ||
69 | return npend; | |
70 | } | |
7322fd19 | 71 | EXPORT_SYMBOL(ath9k_hw_numtxpending); |
f1dc5600 | 72 | |
cbe61d8a | 73 | bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) |
f1dc5600 | 74 | { |
f1dc5600 S |
75 | u32 txcfg, curLevel, newLevel; |
76 | enum ath9k_int omask; | |
77 | ||
2660b81a | 78 | if (ah->tx_trig_level >= MAX_TX_FIFO_THRESHOLD) |
f1dc5600 S |
79 | return false; |
80 | ||
2660b81a | 81 | omask = ath9k_hw_set_interrupts(ah, ah->mask_reg & ~ATH9K_INT_GLOBAL); |
f1dc5600 S |
82 | |
83 | txcfg = REG_READ(ah, AR_TXCFG); | |
84 | curLevel = MS(txcfg, AR_FTRIG); | |
85 | newLevel = curLevel; | |
86 | if (bIncTrigLevel) { | |
87 | if (curLevel < MAX_TX_FIFO_THRESHOLD) | |
88 | newLevel++; | |
89 | } else if (curLevel > MIN_TX_FIFO_THRESHOLD) | |
90 | newLevel--; | |
91 | if (newLevel != curLevel) | |
92 | REG_WRITE(ah, AR_TXCFG, | |
93 | (txcfg & ~AR_FTRIG) | SM(newLevel, AR_FTRIG)); | |
94 | ||
95 | ath9k_hw_set_interrupts(ah, omask); | |
96 | ||
2660b81a | 97 | ah->tx_trig_level = newLevel; |
f1dc5600 S |
98 | |
99 | return newLevel != curLevel; | |
100 | } | |
7322fd19 | 101 | EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel); |
f1dc5600 | 102 | |
cbe61d8a | 103 | bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) |
f1dc5600 | 104 | { |
94ff91d4 S |
105 | #define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */ |
106 | #define ATH9K_TIME_QUANTUM 100 /* usec */ | |
c46917bb | 107 | struct ath_common *common = ath9k_hw_common(ah); |
2660b81a | 108 | struct ath9k_hw_capabilities *pCap = &ah->caps; |
94ff91d4 | 109 | struct ath9k_tx_queue_info *qi; |
f1dc5600 | 110 | u32 tsfLow, j, wait; |
94ff91d4 S |
111 | u32 wait_time = ATH9K_TX_STOP_DMA_TIMEOUT / ATH9K_TIME_QUANTUM; |
112 | ||
113 | if (q >= pCap->total_queues) { | |
c46917bb LR |
114 | ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, " |
115 | "invalid queue: %u\n", q); | |
94ff91d4 S |
116 | return false; |
117 | } | |
118 | ||
2660b81a | 119 | qi = &ah->txq[q]; |
94ff91d4 | 120 | if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { |
c46917bb LR |
121 | ath_print(common, ATH_DBG_QUEUE, "Stopping TX DMA, " |
122 | "inactive queue: %u\n", q); | |
94ff91d4 S |
123 | return false; |
124 | } | |
f1dc5600 S |
125 | |
126 | REG_WRITE(ah, AR_Q_TXD, 1 << q); | |
127 | ||
94ff91d4 | 128 | for (wait = wait_time; wait != 0; wait--) { |
f1dc5600 S |
129 | if (ath9k_hw_numtxpending(ah, q) == 0) |
130 | break; | |
94ff91d4 | 131 | udelay(ATH9K_TIME_QUANTUM); |
f1dc5600 S |
132 | } |
133 | ||
134 | if (ath9k_hw_numtxpending(ah, q)) { | |
c46917bb LR |
135 | ath_print(common, ATH_DBG_QUEUE, |
136 | "%s: Num of pending TX Frames %d on Q %d\n", | |
137 | __func__, ath9k_hw_numtxpending(ah, q), q); | |
f1dc5600 S |
138 | |
139 | for (j = 0; j < 2; j++) { | |
140 | tsfLow = REG_READ(ah, AR_TSF_L32); | |
141 | REG_WRITE(ah, AR_QUIET2, | |
142 | SM(10, AR_QUIET2_QUIET_DUR)); | |
143 | REG_WRITE(ah, AR_QUIET_PERIOD, 100); | |
144 | REG_WRITE(ah, AR_NEXT_QUIET_TIMER, tsfLow >> 10); | |
145 | REG_SET_BIT(ah, AR_TIMER_MODE, | |
146 | AR_QUIET_TIMER_EN); | |
147 | ||
148 | if ((REG_READ(ah, AR_TSF_L32) >> 10) == (tsfLow >> 10)) | |
149 | break; | |
150 | ||
c46917bb LR |
151 | ath_print(common, ATH_DBG_QUEUE, |
152 | "TSF has moved while trying to set " | |
153 | "quiet time TSF: 0x%08x\n", tsfLow); | |
f1dc5600 S |
154 | } |
155 | ||
156 | REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); | |
157 | ||
158 | udelay(200); | |
159 | REG_CLR_BIT(ah, AR_TIMER_MODE, AR_QUIET_TIMER_EN); | |
160 | ||
94ff91d4 | 161 | wait = wait_time; |
f1dc5600 S |
162 | while (ath9k_hw_numtxpending(ah, q)) { |
163 | if ((--wait) == 0) { | |
c46917bb LR |
164 | ath_print(common, ATH_DBG_QUEUE, |
165 | "Failed to stop TX DMA in 100 " | |
166 | "msec after killing last frame\n"); | |
f1dc5600 S |
167 | break; |
168 | } | |
94ff91d4 | 169 | udelay(ATH9K_TIME_QUANTUM); |
f1dc5600 S |
170 | } |
171 | ||
172 | REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); | |
173 | } | |
174 | ||
175 | REG_WRITE(ah, AR_Q_TXD, 0); | |
f1dc5600 | 176 | return wait != 0; |
94ff91d4 S |
177 | |
178 | #undef ATH9K_TX_STOP_DMA_TIMEOUT | |
179 | #undef ATH9K_TIME_QUANTUM | |
f1dc5600 | 180 | } |
7322fd19 | 181 | EXPORT_SYMBOL(ath9k_hw_stoptxdma); |
f1dc5600 | 182 | |
54e4cec6 | 183 | void ath9k_hw_filltxdesc(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
184 | u32 segLen, bool firstSeg, |
185 | bool lastSeg, const struct ath_desc *ds0) | |
186 | { | |
187 | struct ar5416_desc *ads = AR5416DESC(ds); | |
188 | ||
189 | if (firstSeg) { | |
190 | ads->ds_ctl1 |= segLen | (lastSeg ? 0 : AR_TxMore); | |
191 | } else if (lastSeg) { | |
192 | ads->ds_ctl0 = 0; | |
193 | ads->ds_ctl1 = segLen; | |
194 | ads->ds_ctl2 = AR5416DESC_CONST(ds0)->ds_ctl2; | |
195 | ads->ds_ctl3 = AR5416DESC_CONST(ds0)->ds_ctl3; | |
196 | } else { | |
197 | ads->ds_ctl0 = 0; | |
198 | ads->ds_ctl1 = segLen | AR_TxMore; | |
199 | ads->ds_ctl2 = 0; | |
200 | ads->ds_ctl3 = 0; | |
201 | } | |
202 | ads->ds_txstatus0 = ads->ds_txstatus1 = 0; | |
203 | ads->ds_txstatus2 = ads->ds_txstatus3 = 0; | |
204 | ads->ds_txstatus4 = ads->ds_txstatus5 = 0; | |
205 | ads->ds_txstatus6 = ads->ds_txstatus7 = 0; | |
206 | ads->ds_txstatus8 = ads->ds_txstatus9 = 0; | |
f1dc5600 | 207 | } |
7322fd19 | 208 | EXPORT_SYMBOL(ath9k_hw_filltxdesc); |
f1dc5600 | 209 | |
cbe61d8a | 210 | void ath9k_hw_cleartxdesc(struct ath_hw *ah, struct ath_desc *ds) |
f1dc5600 S |
211 | { |
212 | struct ar5416_desc *ads = AR5416DESC(ds); | |
213 | ||
214 | ads->ds_txstatus0 = ads->ds_txstatus1 = 0; | |
215 | ads->ds_txstatus2 = ads->ds_txstatus3 = 0; | |
216 | ads->ds_txstatus4 = ads->ds_txstatus5 = 0; | |
217 | ads->ds_txstatus6 = ads->ds_txstatus7 = 0; | |
218 | ads->ds_txstatus8 = ads->ds_txstatus9 = 0; | |
219 | } | |
7322fd19 | 220 | EXPORT_SYMBOL(ath9k_hw_cleartxdesc); |
f1dc5600 | 221 | |
cbe61d8a | 222 | int ath9k_hw_txprocdesc(struct ath_hw *ah, struct ath_desc *ds) |
f1dc5600 S |
223 | { |
224 | struct ar5416_desc *ads = AR5416DESC(ds); | |
225 | ||
226 | if ((ads->ds_txstatus9 & AR_TxDone) == 0) | |
227 | return -EINPROGRESS; | |
228 | ||
229 | ds->ds_txstat.ts_seqnum = MS(ads->ds_txstatus9, AR_SeqNum); | |
230 | ds->ds_txstat.ts_tstamp = ads->AR_SendTimestamp; | |
231 | ds->ds_txstat.ts_status = 0; | |
232 | ds->ds_txstat.ts_flags = 0; | |
233 | ||
e7824a50 LR |
234 | if (ads->ds_txstatus1 & AR_FrmXmitOK) |
235 | ds->ds_txstat.ts_status |= ATH9K_TX_ACKED; | |
f1dc5600 S |
236 | if (ads->ds_txstatus1 & AR_ExcessiveRetries) |
237 | ds->ds_txstat.ts_status |= ATH9K_TXERR_XRETRY; | |
238 | if (ads->ds_txstatus1 & AR_Filtered) | |
239 | ds->ds_txstat.ts_status |= ATH9K_TXERR_FILT; | |
daa9deb3 | 240 | if (ads->ds_txstatus1 & AR_FIFOUnderrun) { |
f1dc5600 | 241 | ds->ds_txstat.ts_status |= ATH9K_TXERR_FIFO; |
daa9deb3 S |
242 | ath9k_hw_updatetxtriglevel(ah, true); |
243 | } | |
f1dc5600 S |
244 | if (ads->ds_txstatus9 & AR_TxOpExceeded) |
245 | ds->ds_txstat.ts_status |= ATH9K_TXERR_XTXOP; | |
246 | if (ads->ds_txstatus1 & AR_TxTimerExpired) | |
247 | ds->ds_txstat.ts_status |= ATH9K_TXERR_TIMER_EXPIRED; | |
248 | ||
249 | if (ads->ds_txstatus1 & AR_DescCfgErr) | |
250 | ds->ds_txstat.ts_flags |= ATH9K_TX_DESC_CFG_ERR; | |
251 | if (ads->ds_txstatus1 & AR_TxDataUnderrun) { | |
252 | ds->ds_txstat.ts_flags |= ATH9K_TX_DATA_UNDERRUN; | |
253 | ath9k_hw_updatetxtriglevel(ah, true); | |
254 | } | |
255 | if (ads->ds_txstatus1 & AR_TxDelimUnderrun) { | |
256 | ds->ds_txstat.ts_flags |= ATH9K_TX_DELIM_UNDERRUN; | |
257 | ath9k_hw_updatetxtriglevel(ah, true); | |
258 | } | |
259 | if (ads->ds_txstatus0 & AR_TxBaStatus) { | |
260 | ds->ds_txstat.ts_flags |= ATH9K_TX_BA; | |
261 | ds->ds_txstat.ba_low = ads->AR_BaBitmapLow; | |
262 | ds->ds_txstat.ba_high = ads->AR_BaBitmapHigh; | |
263 | } | |
264 | ||
265 | ds->ds_txstat.ts_rateindex = MS(ads->ds_txstatus9, AR_FinalTxIdx); | |
266 | switch (ds->ds_txstat.ts_rateindex) { | |
267 | case 0: | |
268 | ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate0); | |
269 | break; | |
270 | case 1: | |
271 | ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate1); | |
272 | break; | |
273 | case 2: | |
274 | ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate2); | |
275 | break; | |
276 | case 3: | |
277 | ds->ds_txstat.ts_ratecode = MS(ads->ds_ctl3, AR_XmitRate3); | |
278 | break; | |
279 | } | |
280 | ||
281 | ds->ds_txstat.ts_rssi = MS(ads->ds_txstatus5, AR_TxRSSICombined); | |
282 | ds->ds_txstat.ts_rssi_ctl0 = MS(ads->ds_txstatus0, AR_TxRSSIAnt00); | |
283 | ds->ds_txstat.ts_rssi_ctl1 = MS(ads->ds_txstatus0, AR_TxRSSIAnt01); | |
284 | ds->ds_txstat.ts_rssi_ctl2 = MS(ads->ds_txstatus0, AR_TxRSSIAnt02); | |
285 | ds->ds_txstat.ts_rssi_ext0 = MS(ads->ds_txstatus5, AR_TxRSSIAnt10); | |
286 | ds->ds_txstat.ts_rssi_ext1 = MS(ads->ds_txstatus5, AR_TxRSSIAnt11); | |
287 | ds->ds_txstat.ts_rssi_ext2 = MS(ads->ds_txstatus5, AR_TxRSSIAnt12); | |
288 | ds->ds_txstat.evm0 = ads->AR_TxEVM0; | |
289 | ds->ds_txstat.evm1 = ads->AR_TxEVM1; | |
290 | ds->ds_txstat.evm2 = ads->AR_TxEVM2; | |
291 | ds->ds_txstat.ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt); | |
292 | ds->ds_txstat.ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt); | |
293 | ds->ds_txstat.ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt); | |
83befbde | 294 | ds->ds_txstat.ts_antenna = 0; |
f1dc5600 S |
295 | |
296 | return 0; | |
297 | } | |
7322fd19 | 298 | EXPORT_SYMBOL(ath9k_hw_txprocdesc); |
f1dc5600 | 299 | |
cbe61d8a | 300 | void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
301 | u32 pktLen, enum ath9k_pkt_type type, u32 txPower, |
302 | u32 keyIx, enum ath9k_key_type keyType, u32 flags) | |
303 | { | |
304 | struct ar5416_desc *ads = AR5416DESC(ds); | |
f1dc5600 | 305 | |
2660b81a | 306 | txPower += ah->txpower_indexoffset; |
f1dc5600 S |
307 | if (txPower > 63) |
308 | txPower = 63; | |
309 | ||
310 | ads->ds_ctl0 = (pktLen & AR_FrameLen) | |
311 | | (flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) | |
312 | | SM(txPower, AR_XmitPower) | |
313 | | (flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) | |
314 | | (flags & ATH9K_TXDESC_CLRDMASK ? AR_ClrDestMask : 0) | |
315 | | (flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0) | |
316 | | (keyIx != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0); | |
317 | ||
318 | ads->ds_ctl1 = | |
319 | (keyIx != ATH9K_TXKEYIX_INVALID ? SM(keyIx, AR_DestIdx) : 0) | |
320 | | SM(type, AR_FrameType) | |
321 | | (flags & ATH9K_TXDESC_NOACK ? AR_NoAck : 0) | |
322 | | (flags & ATH9K_TXDESC_EXT_ONLY ? AR_ExtOnly : 0) | |
323 | | (flags & ATH9K_TXDESC_EXT_AND_CTL ? AR_ExtAndCtl : 0); | |
324 | ||
325 | ads->ds_ctl6 = SM(keyType, AR_EncrType); | |
326 | ||
327 | if (AR_SREV_9285(ah)) { | |
328 | ads->ds_ctl8 = 0; | |
329 | ads->ds_ctl9 = 0; | |
330 | ads->ds_ctl10 = 0; | |
331 | ads->ds_ctl11 = 0; | |
332 | } | |
333 | } | |
7322fd19 | 334 | EXPORT_SYMBOL(ath9k_hw_set11n_txdesc); |
f1dc5600 | 335 | |
cbe61d8a | 336 | void ath9k_hw_set11n_ratescenario(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
337 | struct ath_desc *lastds, |
338 | u32 durUpdateEn, u32 rtsctsRate, | |
339 | u32 rtsctsDuration, | |
340 | struct ath9k_11n_rate_series series[], | |
341 | u32 nseries, u32 flags) | |
342 | { | |
343 | struct ar5416_desc *ads = AR5416DESC(ds); | |
344 | struct ar5416_desc *last_ads = AR5416DESC(lastds); | |
345 | u32 ds_ctl0; | |
346 | ||
f1dc5600 S |
347 | if (flags & (ATH9K_TXDESC_RTSENA | ATH9K_TXDESC_CTSENA)) { |
348 | ds_ctl0 = ads->ds_ctl0; | |
349 | ||
350 | if (flags & ATH9K_TXDESC_RTSENA) { | |
351 | ds_ctl0 &= ~AR_CTSEnable; | |
352 | ds_ctl0 |= AR_RTSEnable; | |
353 | } else { | |
354 | ds_ctl0 &= ~AR_RTSEnable; | |
355 | ds_ctl0 |= AR_CTSEnable; | |
356 | } | |
357 | ||
358 | ads->ds_ctl0 = ds_ctl0; | |
359 | } else { | |
360 | ads->ds_ctl0 = | |
361 | (ads->ds_ctl0 & ~(AR_RTSEnable | AR_CTSEnable)); | |
362 | } | |
363 | ||
364 | ads->ds_ctl2 = set11nTries(series, 0) | |
365 | | set11nTries(series, 1) | |
366 | | set11nTries(series, 2) | |
367 | | set11nTries(series, 3) | |
368 | | (durUpdateEn ? AR_DurUpdateEna : 0) | |
369 | | SM(0, AR_BurstDur); | |
370 | ||
371 | ads->ds_ctl3 = set11nRate(series, 0) | |
372 | | set11nRate(series, 1) | |
373 | | set11nRate(series, 2) | |
374 | | set11nRate(series, 3); | |
375 | ||
376 | ads->ds_ctl4 = set11nPktDurRTSCTS(series, 0) | |
377 | | set11nPktDurRTSCTS(series, 1); | |
378 | ||
379 | ads->ds_ctl5 = set11nPktDurRTSCTS(series, 2) | |
380 | | set11nPktDurRTSCTS(series, 3); | |
381 | ||
382 | ads->ds_ctl7 = set11nRateFlags(series, 0) | |
383 | | set11nRateFlags(series, 1) | |
384 | | set11nRateFlags(series, 2) | |
385 | | set11nRateFlags(series, 3) | |
386 | | SM(rtsctsRate, AR_RTSCTSRate); | |
387 | last_ads->ds_ctl2 = ads->ds_ctl2; | |
388 | last_ads->ds_ctl3 = ads->ds_ctl3; | |
389 | } | |
7322fd19 | 390 | EXPORT_SYMBOL(ath9k_hw_set11n_ratescenario); |
f1dc5600 | 391 | |
cbe61d8a | 392 | void ath9k_hw_set11n_aggr_first(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
393 | u32 aggrLen) |
394 | { | |
395 | struct ar5416_desc *ads = AR5416DESC(ds); | |
396 | ||
397 | ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); | |
398 | ads->ds_ctl6 &= ~AR_AggrLen; | |
399 | ads->ds_ctl6 |= SM(aggrLen, AR_AggrLen); | |
400 | } | |
7322fd19 | 401 | EXPORT_SYMBOL(ath9k_hw_set11n_aggr_first); |
f1dc5600 | 402 | |
cbe61d8a | 403 | void ath9k_hw_set11n_aggr_middle(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
404 | u32 numDelims) |
405 | { | |
406 | struct ar5416_desc *ads = AR5416DESC(ds); | |
407 | unsigned int ctl6; | |
408 | ||
409 | ads->ds_ctl1 |= (AR_IsAggr | AR_MoreAggr); | |
410 | ||
411 | ctl6 = ads->ds_ctl6; | |
412 | ctl6 &= ~AR_PadDelim; | |
413 | ctl6 |= SM(numDelims, AR_PadDelim); | |
414 | ads->ds_ctl6 = ctl6; | |
415 | } | |
7322fd19 | 416 | EXPORT_SYMBOL(ath9k_hw_set11n_aggr_middle); |
f1dc5600 | 417 | |
cbe61d8a | 418 | void ath9k_hw_set11n_aggr_last(struct ath_hw *ah, struct ath_desc *ds) |
f1dc5600 S |
419 | { |
420 | struct ar5416_desc *ads = AR5416DESC(ds); | |
421 | ||
422 | ads->ds_ctl1 |= AR_IsAggr; | |
423 | ads->ds_ctl1 &= ~AR_MoreAggr; | |
424 | ads->ds_ctl6 &= ~AR_PadDelim; | |
425 | } | |
7322fd19 | 426 | EXPORT_SYMBOL(ath9k_hw_set11n_aggr_last); |
f1dc5600 | 427 | |
cbe61d8a | 428 | void ath9k_hw_clr11n_aggr(struct ath_hw *ah, struct ath_desc *ds) |
f1dc5600 S |
429 | { |
430 | struct ar5416_desc *ads = AR5416DESC(ds); | |
431 | ||
432 | ads->ds_ctl1 &= (~AR_IsAggr & ~AR_MoreAggr); | |
433 | } | |
7322fd19 | 434 | EXPORT_SYMBOL(ath9k_hw_clr11n_aggr); |
f1dc5600 | 435 | |
cbe61d8a | 436 | void ath9k_hw_set11n_burstduration(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
437 | u32 burstDuration) |
438 | { | |
439 | struct ar5416_desc *ads = AR5416DESC(ds); | |
440 | ||
441 | ads->ds_ctl2 &= ~AR_BurstDur; | |
442 | ads->ds_ctl2 |= SM(burstDuration, AR_BurstDur); | |
443 | } | |
7322fd19 | 444 | EXPORT_SYMBOL(ath9k_hw_set11n_burstduration); |
f1dc5600 | 445 | |
cbe61d8a | 446 | void ath9k_hw_set11n_virtualmorefrag(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
447 | u32 vmf) |
448 | { | |
449 | struct ar5416_desc *ads = AR5416DESC(ds); | |
450 | ||
451 | if (vmf) | |
452 | ads->ds_ctl0 |= AR_VirtMoreFrag; | |
453 | else | |
454 | ads->ds_ctl0 &= ~AR_VirtMoreFrag; | |
455 | } | |
456 | ||
cbe61d8a | 457 | void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs) |
f1dc5600 | 458 | { |
2660b81a S |
459 | *txqs &= ah->intr_txqs; |
460 | ah->intr_txqs &= ~(*txqs); | |
f1dc5600 | 461 | } |
7322fd19 | 462 | EXPORT_SYMBOL(ath9k_hw_gettxintrtxqs); |
f1dc5600 | 463 | |
cbe61d8a | 464 | bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, |
f1dc5600 S |
465 | const struct ath9k_tx_queue_info *qinfo) |
466 | { | |
467 | u32 cw; | |
c46917bb | 468 | struct ath_common *common = ath9k_hw_common(ah); |
2660b81a | 469 | struct ath9k_hw_capabilities *pCap = &ah->caps; |
f1dc5600 S |
470 | struct ath9k_tx_queue_info *qi; |
471 | ||
472 | if (q >= pCap->total_queues) { | |
c46917bb LR |
473 | ath_print(common, ATH_DBG_QUEUE, "Set TXQ properties, " |
474 | "invalid queue: %u\n", q); | |
f1dc5600 S |
475 | return false; |
476 | } | |
477 | ||
2660b81a | 478 | qi = &ah->txq[q]; |
f1dc5600 | 479 | if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { |
c46917bb LR |
480 | ath_print(common, ATH_DBG_QUEUE, "Set TXQ properties, " |
481 | "inactive queue: %u\n", q); | |
f1dc5600 S |
482 | return false; |
483 | } | |
484 | ||
c46917bb | 485 | ath_print(common, ATH_DBG_QUEUE, "Set queue properties for: %u\n", q); |
f1dc5600 S |
486 | |
487 | qi->tqi_ver = qinfo->tqi_ver; | |
488 | qi->tqi_subtype = qinfo->tqi_subtype; | |
489 | qi->tqi_qflags = qinfo->tqi_qflags; | |
490 | qi->tqi_priority = qinfo->tqi_priority; | |
491 | if (qinfo->tqi_aifs != ATH9K_TXQ_USEDEFAULT) | |
492 | qi->tqi_aifs = min(qinfo->tqi_aifs, 255U); | |
493 | else | |
494 | qi->tqi_aifs = INIT_AIFS; | |
495 | if (qinfo->tqi_cwmin != ATH9K_TXQ_USEDEFAULT) { | |
496 | cw = min(qinfo->tqi_cwmin, 1024U); | |
497 | qi->tqi_cwmin = 1; | |
498 | while (qi->tqi_cwmin < cw) | |
499 | qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; | |
500 | } else | |
501 | qi->tqi_cwmin = qinfo->tqi_cwmin; | |
502 | if (qinfo->tqi_cwmax != ATH9K_TXQ_USEDEFAULT) { | |
503 | cw = min(qinfo->tqi_cwmax, 1024U); | |
504 | qi->tqi_cwmax = 1; | |
505 | while (qi->tqi_cwmax < cw) | |
506 | qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; | |
507 | } else | |
508 | qi->tqi_cwmax = INIT_CWMAX; | |
509 | ||
510 | if (qinfo->tqi_shretry != 0) | |
511 | qi->tqi_shretry = min((u32) qinfo->tqi_shretry, 15U); | |
512 | else | |
513 | qi->tqi_shretry = INIT_SH_RETRY; | |
514 | if (qinfo->tqi_lgretry != 0) | |
515 | qi->tqi_lgretry = min((u32) qinfo->tqi_lgretry, 15U); | |
516 | else | |
517 | qi->tqi_lgretry = INIT_LG_RETRY; | |
518 | qi->tqi_cbrPeriod = qinfo->tqi_cbrPeriod; | |
519 | qi->tqi_cbrOverflowLimit = qinfo->tqi_cbrOverflowLimit; | |
520 | qi->tqi_burstTime = qinfo->tqi_burstTime; | |
521 | qi->tqi_readyTime = qinfo->tqi_readyTime; | |
522 | ||
523 | switch (qinfo->tqi_subtype) { | |
524 | case ATH9K_WME_UPSD: | |
525 | if (qi->tqi_type == ATH9K_TX_QUEUE_DATA) | |
526 | qi->tqi_intFlags = ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS; | |
527 | break; | |
528 | default: | |
529 | break; | |
530 | } | |
531 | ||
532 | return true; | |
533 | } | |
7322fd19 | 534 | EXPORT_SYMBOL(ath9k_hw_set_txq_props); |
f1dc5600 | 535 | |
cbe61d8a | 536 | bool ath9k_hw_get_txq_props(struct ath_hw *ah, int q, |
f1dc5600 S |
537 | struct ath9k_tx_queue_info *qinfo) |
538 | { | |
c46917bb | 539 | struct ath_common *common = ath9k_hw_common(ah); |
2660b81a | 540 | struct ath9k_hw_capabilities *pCap = &ah->caps; |
f1dc5600 S |
541 | struct ath9k_tx_queue_info *qi; |
542 | ||
543 | if (q >= pCap->total_queues) { | |
c46917bb LR |
544 | ath_print(common, ATH_DBG_QUEUE, "Get TXQ properties, " |
545 | "invalid queue: %u\n", q); | |
f1dc5600 S |
546 | return false; |
547 | } | |
548 | ||
2660b81a | 549 | qi = &ah->txq[q]; |
f1dc5600 | 550 | if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { |
c46917bb LR |
551 | ath_print(common, ATH_DBG_QUEUE, "Get TXQ properties, " |
552 | "inactive queue: %u\n", q); | |
f1dc5600 S |
553 | return false; |
554 | } | |
555 | ||
556 | qinfo->tqi_qflags = qi->tqi_qflags; | |
557 | qinfo->tqi_ver = qi->tqi_ver; | |
558 | qinfo->tqi_subtype = qi->tqi_subtype; | |
559 | qinfo->tqi_qflags = qi->tqi_qflags; | |
560 | qinfo->tqi_priority = qi->tqi_priority; | |
561 | qinfo->tqi_aifs = qi->tqi_aifs; | |
562 | qinfo->tqi_cwmin = qi->tqi_cwmin; | |
563 | qinfo->tqi_cwmax = qi->tqi_cwmax; | |
564 | qinfo->tqi_shretry = qi->tqi_shretry; | |
565 | qinfo->tqi_lgretry = qi->tqi_lgretry; | |
566 | qinfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; | |
567 | qinfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; | |
568 | qinfo->tqi_burstTime = qi->tqi_burstTime; | |
569 | qinfo->tqi_readyTime = qi->tqi_readyTime; | |
570 | ||
571 | return true; | |
572 | } | |
7322fd19 | 573 | EXPORT_SYMBOL(ath9k_hw_get_txq_props); |
f1dc5600 | 574 | |
cbe61d8a | 575 | int ath9k_hw_setuptxqueue(struct ath_hw *ah, enum ath9k_tx_queue type, |
f1dc5600 S |
576 | const struct ath9k_tx_queue_info *qinfo) |
577 | { | |
c46917bb | 578 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 | 579 | struct ath9k_tx_queue_info *qi; |
2660b81a | 580 | struct ath9k_hw_capabilities *pCap = &ah->caps; |
f1dc5600 S |
581 | int q; |
582 | ||
583 | switch (type) { | |
584 | case ATH9K_TX_QUEUE_BEACON: | |
585 | q = pCap->total_queues - 1; | |
586 | break; | |
587 | case ATH9K_TX_QUEUE_CAB: | |
588 | q = pCap->total_queues - 2; | |
589 | break; | |
590 | case ATH9K_TX_QUEUE_PSPOLL: | |
591 | q = 1; | |
592 | break; | |
593 | case ATH9K_TX_QUEUE_UAPSD: | |
594 | q = pCap->total_queues - 3; | |
595 | break; | |
596 | case ATH9K_TX_QUEUE_DATA: | |
597 | for (q = 0; q < pCap->total_queues; q++) | |
2660b81a | 598 | if (ah->txq[q].tqi_type == |
f1dc5600 S |
599 | ATH9K_TX_QUEUE_INACTIVE) |
600 | break; | |
601 | if (q == pCap->total_queues) { | |
c46917bb LR |
602 | ath_print(common, ATH_DBG_FATAL, |
603 | "No available TX queue\n"); | |
f1dc5600 S |
604 | return -1; |
605 | } | |
606 | break; | |
607 | default: | |
c46917bb LR |
608 | ath_print(common, ATH_DBG_FATAL, |
609 | "Invalid TX queue type: %u\n", type); | |
f1dc5600 S |
610 | return -1; |
611 | } | |
612 | ||
c46917bb | 613 | ath_print(common, ATH_DBG_QUEUE, "Setup TX queue: %u\n", q); |
f1dc5600 | 614 | |
2660b81a | 615 | qi = &ah->txq[q]; |
f1dc5600 | 616 | if (qi->tqi_type != ATH9K_TX_QUEUE_INACTIVE) { |
c46917bb LR |
617 | ath_print(common, ATH_DBG_FATAL, |
618 | "TX queue: %u already active\n", q); | |
f1dc5600 S |
619 | return -1; |
620 | } | |
621 | memset(qi, 0, sizeof(struct ath9k_tx_queue_info)); | |
622 | qi->tqi_type = type; | |
623 | if (qinfo == NULL) { | |
624 | qi->tqi_qflags = | |
625 | TXQ_FLAG_TXOKINT_ENABLE | |
626 | | TXQ_FLAG_TXERRINT_ENABLE | |
627 | | TXQ_FLAG_TXDESCINT_ENABLE | TXQ_FLAG_TXURNINT_ENABLE; | |
628 | qi->tqi_aifs = INIT_AIFS; | |
629 | qi->tqi_cwmin = ATH9K_TXQ_USEDEFAULT; | |
630 | qi->tqi_cwmax = INIT_CWMAX; | |
631 | qi->tqi_shretry = INIT_SH_RETRY; | |
632 | qi->tqi_lgretry = INIT_LG_RETRY; | |
633 | qi->tqi_physCompBuf = 0; | |
634 | } else { | |
635 | qi->tqi_physCompBuf = qinfo->tqi_physCompBuf; | |
636 | (void) ath9k_hw_set_txq_props(ah, q, qinfo); | |
637 | } | |
638 | ||
639 | return q; | |
640 | } | |
7322fd19 | 641 | EXPORT_SYMBOL(ath9k_hw_setuptxqueue); |
f1dc5600 | 642 | |
cbe61d8a | 643 | bool ath9k_hw_releasetxqueue(struct ath_hw *ah, u32 q) |
f1dc5600 | 644 | { |
2660b81a | 645 | struct ath9k_hw_capabilities *pCap = &ah->caps; |
c46917bb | 646 | struct ath_common *common = ath9k_hw_common(ah); |
f1dc5600 S |
647 | struct ath9k_tx_queue_info *qi; |
648 | ||
649 | if (q >= pCap->total_queues) { | |
c46917bb LR |
650 | ath_print(common, ATH_DBG_QUEUE, "Release TXQ, " |
651 | "invalid queue: %u\n", q); | |
f1dc5600 S |
652 | return false; |
653 | } | |
2660b81a | 654 | qi = &ah->txq[q]; |
f1dc5600 | 655 | if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { |
c46917bb LR |
656 | ath_print(common, ATH_DBG_QUEUE, "Release TXQ, " |
657 | "inactive queue: %u\n", q); | |
f1dc5600 S |
658 | return false; |
659 | } | |
660 | ||
c46917bb | 661 | ath_print(common, ATH_DBG_QUEUE, "Release TX queue: %u\n", q); |
f1dc5600 S |
662 | |
663 | qi->tqi_type = ATH9K_TX_QUEUE_INACTIVE; | |
2660b81a S |
664 | ah->txok_interrupt_mask &= ~(1 << q); |
665 | ah->txerr_interrupt_mask &= ~(1 << q); | |
666 | ah->txdesc_interrupt_mask &= ~(1 << q); | |
667 | ah->txeol_interrupt_mask &= ~(1 << q); | |
668 | ah->txurn_interrupt_mask &= ~(1 << q); | |
f1dc5600 S |
669 | ath9k_hw_set_txq_interrupts(ah, qi); |
670 | ||
671 | return true; | |
672 | } | |
7322fd19 | 673 | EXPORT_SYMBOL(ath9k_hw_releasetxqueue); |
f1dc5600 | 674 | |
cbe61d8a | 675 | bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) |
f1dc5600 | 676 | { |
2660b81a | 677 | struct ath9k_hw_capabilities *pCap = &ah->caps; |
c46917bb | 678 | struct ath_common *common = ath9k_hw_common(ah); |
2660b81a | 679 | struct ath9k_channel *chan = ah->curchan; |
f1dc5600 S |
680 | struct ath9k_tx_queue_info *qi; |
681 | u32 cwMin, chanCwMin, value; | |
682 | ||
683 | if (q >= pCap->total_queues) { | |
c46917bb LR |
684 | ath_print(common, ATH_DBG_QUEUE, "Reset TXQ, " |
685 | "invalid queue: %u\n", q); | |
f1dc5600 S |
686 | return false; |
687 | } | |
688 | ||
2660b81a | 689 | qi = &ah->txq[q]; |
f1dc5600 | 690 | if (qi->tqi_type == ATH9K_TX_QUEUE_INACTIVE) { |
c46917bb LR |
691 | ath_print(common, ATH_DBG_QUEUE, "Reset TXQ, " |
692 | "inactive queue: %u\n", q); | |
f1dc5600 S |
693 | return true; |
694 | } | |
695 | ||
c46917bb | 696 | ath_print(common, ATH_DBG_QUEUE, "Reset TX queue: %u\n", q); |
f1dc5600 S |
697 | |
698 | if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { | |
699 | if (chan && IS_CHAN_B(chan)) | |
700 | chanCwMin = INIT_CWMIN_11B; | |
701 | else | |
702 | chanCwMin = INIT_CWMIN; | |
703 | ||
704 | for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1); | |
705 | } else | |
706 | cwMin = qi->tqi_cwmin; | |
707 | ||
708 | REG_WRITE(ah, AR_DLCL_IFS(q), | |
709 | SM(cwMin, AR_D_LCL_IFS_CWMIN) | | |
710 | SM(qi->tqi_cwmax, AR_D_LCL_IFS_CWMAX) | | |
711 | SM(qi->tqi_aifs, AR_D_LCL_IFS_AIFS)); | |
712 | ||
713 | REG_WRITE(ah, AR_DRETRY_LIMIT(q), | |
714 | SM(INIT_SSH_RETRY, AR_D_RETRY_LIMIT_STA_SH) | | |
715 | SM(INIT_SLG_RETRY, AR_D_RETRY_LIMIT_STA_LG) | | |
716 | SM(qi->tqi_shretry, AR_D_RETRY_LIMIT_FR_SH)); | |
717 | ||
718 | REG_WRITE(ah, AR_QMISC(q), AR_Q_MISC_DCU_EARLY_TERM_REQ); | |
719 | REG_WRITE(ah, AR_DMISC(q), | |
720 | AR_D_MISC_CW_BKOFF_EN | AR_D_MISC_FRAG_WAIT_EN | 0x2); | |
721 | ||
722 | if (qi->tqi_cbrPeriod) { | |
723 | REG_WRITE(ah, AR_QCBRCFG(q), | |
724 | SM(qi->tqi_cbrPeriod, AR_Q_CBRCFG_INTERVAL) | | |
725 | SM(qi->tqi_cbrOverflowLimit, AR_Q_CBRCFG_OVF_THRESH)); | |
726 | REG_WRITE(ah, AR_QMISC(q), | |
727 | REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_FSP_CBR | | |
728 | (qi->tqi_cbrOverflowLimit ? | |
729 | AR_Q_MISC_CBR_EXP_CNTR_LIMIT_EN : 0)); | |
730 | } | |
731 | if (qi->tqi_readyTime && (qi->tqi_type != ATH9K_TX_QUEUE_CAB)) { | |
732 | REG_WRITE(ah, AR_QRDYTIMECFG(q), | |
733 | SM(qi->tqi_readyTime, AR_Q_RDYTIMECFG_DURATION) | | |
734 | AR_Q_RDYTIMECFG_EN); | |
735 | } | |
736 | ||
737 | REG_WRITE(ah, AR_DCHNTIME(q), | |
738 | SM(qi->tqi_burstTime, AR_D_CHNTIME_DUR) | | |
739 | (qi->tqi_burstTime ? AR_D_CHNTIME_EN : 0)); | |
740 | ||
741 | if (qi->tqi_burstTime | |
742 | && (qi->tqi_qflags & TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)) { | |
743 | REG_WRITE(ah, AR_QMISC(q), | |
744 | REG_READ(ah, AR_QMISC(q)) | | |
745 | AR_Q_MISC_RDYTIME_EXP_POLICY); | |
746 | ||
747 | } | |
748 | ||
749 | if (qi->tqi_qflags & TXQ_FLAG_BACKOFF_DISABLE) { | |
750 | REG_WRITE(ah, AR_DMISC(q), | |
751 | REG_READ(ah, AR_DMISC(q)) | | |
752 | AR_D_MISC_POST_FR_BKOFF_DIS); | |
753 | } | |
754 | if (qi->tqi_qflags & TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) { | |
755 | REG_WRITE(ah, AR_DMISC(q), | |
756 | REG_READ(ah, AR_DMISC(q)) | | |
757 | AR_D_MISC_FRAG_BKOFF_EN); | |
758 | } | |
759 | switch (qi->tqi_type) { | |
760 | case ATH9K_TX_QUEUE_BEACON: | |
761 | REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q)) | |
762 | | AR_Q_MISC_FSP_DBA_GATED | |
763 | | AR_Q_MISC_BEACON_USE | |
764 | | AR_Q_MISC_CBR_INCR_DIS1); | |
765 | ||
766 | REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) | |
767 | | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << | |
768 | AR_D_MISC_ARB_LOCKOUT_CNTRL_S) | |
769 | | AR_D_MISC_BEACON_USE | |
770 | | AR_D_MISC_POST_FR_BKOFF_DIS); | |
771 | break; | |
772 | case ATH9K_TX_QUEUE_CAB: | |
773 | REG_WRITE(ah, AR_QMISC(q), REG_READ(ah, AR_QMISC(q)) | |
774 | | AR_Q_MISC_FSP_DBA_GATED | |
775 | | AR_Q_MISC_CBR_INCR_DIS1 | |
776 | | AR_Q_MISC_CBR_INCR_DIS0); | |
777 | value = (qi->tqi_readyTime - | |
2660b81a S |
778 | (ah->config.sw_beacon_response_time - |
779 | ah->config.dma_beacon_response_time) - | |
780 | ah->config.additional_swba_backoff) * 1024; | |
f1dc5600 S |
781 | REG_WRITE(ah, AR_QRDYTIMECFG(q), |
782 | value | AR_Q_RDYTIMECFG_EN); | |
783 | REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) | |
784 | | (AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL << | |
785 | AR_D_MISC_ARB_LOCKOUT_CNTRL_S)); | |
786 | break; | |
787 | case ATH9K_TX_QUEUE_PSPOLL: | |
788 | REG_WRITE(ah, AR_QMISC(q), | |
789 | REG_READ(ah, AR_QMISC(q)) | AR_Q_MISC_CBR_INCR_DIS1); | |
790 | break; | |
791 | case ATH9K_TX_QUEUE_UAPSD: | |
792 | REG_WRITE(ah, AR_DMISC(q), REG_READ(ah, AR_DMISC(q)) | | |
793 | AR_D_MISC_POST_FR_BKOFF_DIS); | |
794 | break; | |
795 | default: | |
796 | break; | |
797 | } | |
798 | ||
799 | if (qi->tqi_intFlags & ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS) { | |
800 | REG_WRITE(ah, AR_DMISC(q), | |
801 | REG_READ(ah, AR_DMISC(q)) | | |
802 | SM(AR_D_MISC_ARB_LOCKOUT_CNTRL_GLOBAL, | |
803 | AR_D_MISC_ARB_LOCKOUT_CNTRL) | | |
804 | AR_D_MISC_POST_FR_BKOFF_DIS); | |
805 | } | |
806 | ||
807 | if (qi->tqi_qflags & TXQ_FLAG_TXOKINT_ENABLE) | |
2660b81a | 808 | ah->txok_interrupt_mask |= 1 << q; |
f1dc5600 | 809 | else |
2660b81a | 810 | ah->txok_interrupt_mask &= ~(1 << q); |
f1dc5600 | 811 | if (qi->tqi_qflags & TXQ_FLAG_TXERRINT_ENABLE) |
2660b81a | 812 | ah->txerr_interrupt_mask |= 1 << q; |
f1dc5600 | 813 | else |
2660b81a | 814 | ah->txerr_interrupt_mask &= ~(1 << q); |
f1dc5600 | 815 | if (qi->tqi_qflags & TXQ_FLAG_TXDESCINT_ENABLE) |
2660b81a | 816 | ah->txdesc_interrupt_mask |= 1 << q; |
f1dc5600 | 817 | else |
2660b81a | 818 | ah->txdesc_interrupt_mask &= ~(1 << q); |
f1dc5600 | 819 | if (qi->tqi_qflags & TXQ_FLAG_TXEOLINT_ENABLE) |
2660b81a | 820 | ah->txeol_interrupt_mask |= 1 << q; |
f1dc5600 | 821 | else |
2660b81a | 822 | ah->txeol_interrupt_mask &= ~(1 << q); |
f1dc5600 | 823 | if (qi->tqi_qflags & TXQ_FLAG_TXURNINT_ENABLE) |
2660b81a | 824 | ah->txurn_interrupt_mask |= 1 << q; |
f1dc5600 | 825 | else |
2660b81a | 826 | ah->txurn_interrupt_mask &= ~(1 << q); |
f1dc5600 S |
827 | ath9k_hw_set_txq_interrupts(ah, qi); |
828 | ||
829 | return true; | |
830 | } | |
7322fd19 | 831 | EXPORT_SYMBOL(ath9k_hw_resettxqueue); |
f1dc5600 | 832 | |
cbe61d8a | 833 | int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
834 | u32 pa, struct ath_desc *nds, u64 tsf) |
835 | { | |
836 | struct ar5416_desc ads; | |
837 | struct ar5416_desc *adsp = AR5416DESC(ds); | |
838 | u32 phyerr; | |
839 | ||
840 | if ((adsp->ds_rxstatus8 & AR_RxDone) == 0) | |
841 | return -EINPROGRESS; | |
842 | ||
843 | ads.u.rx = adsp->u.rx; | |
844 | ||
845 | ds->ds_rxstat.rs_status = 0; | |
846 | ds->ds_rxstat.rs_flags = 0; | |
847 | ||
848 | ds->ds_rxstat.rs_datalen = ads.ds_rxstatus1 & AR_DataLen; | |
849 | ds->ds_rxstat.rs_tstamp = ads.AR_RcvTimestamp; | |
850 | ||
dd8b15b0 SB |
851 | if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) { |
852 | ds->ds_rxstat.rs_rssi = ATH9K_RSSI_BAD; | |
853 | ds->ds_rxstat.rs_rssi_ctl0 = ATH9K_RSSI_BAD; | |
854 | ds->ds_rxstat.rs_rssi_ctl1 = ATH9K_RSSI_BAD; | |
855 | ds->ds_rxstat.rs_rssi_ctl2 = ATH9K_RSSI_BAD; | |
856 | ds->ds_rxstat.rs_rssi_ext0 = ATH9K_RSSI_BAD; | |
857 | ds->ds_rxstat.rs_rssi_ext1 = ATH9K_RSSI_BAD; | |
858 | ds->ds_rxstat.rs_rssi_ext2 = ATH9K_RSSI_BAD; | |
859 | } else { | |
860 | ds->ds_rxstat.rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); | |
861 | ds->ds_rxstat.rs_rssi_ctl0 = MS(ads.ds_rxstatus0, | |
862 | AR_RxRSSIAnt00); | |
863 | ds->ds_rxstat.rs_rssi_ctl1 = MS(ads.ds_rxstatus0, | |
864 | AR_RxRSSIAnt01); | |
865 | ds->ds_rxstat.rs_rssi_ctl2 = MS(ads.ds_rxstatus0, | |
866 | AR_RxRSSIAnt02); | |
867 | ds->ds_rxstat.rs_rssi_ext0 = MS(ads.ds_rxstatus4, | |
868 | AR_RxRSSIAnt10); | |
869 | ds->ds_rxstat.rs_rssi_ext1 = MS(ads.ds_rxstatus4, | |
870 | AR_RxRSSIAnt11); | |
871 | ds->ds_rxstat.rs_rssi_ext2 = MS(ads.ds_rxstatus4, | |
872 | AR_RxRSSIAnt12); | |
873 | } | |
f1dc5600 S |
874 | if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) |
875 | ds->ds_rxstat.rs_keyix = MS(ads.ds_rxstatus8, AR_KeyIdx); | |
876 | else | |
877 | ds->ds_rxstat.rs_keyix = ATH9K_RXKEYIX_INVALID; | |
878 | ||
879 | ds->ds_rxstat.rs_rate = RXSTATUS_RATE(ah, (&ads)); | |
880 | ds->ds_rxstat.rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0; | |
881 | ||
882 | ds->ds_rxstat.rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0; | |
883 | ds->ds_rxstat.rs_moreaggr = | |
884 | (ads.ds_rxstatus8 & AR_RxMoreAggr) ? 1 : 0; | |
885 | ds->ds_rxstat.rs_antenna = MS(ads.ds_rxstatus3, AR_RxAntenna); | |
886 | ds->ds_rxstat.rs_flags = | |
887 | (ads.ds_rxstatus3 & AR_GI) ? ATH9K_RX_GI : 0; | |
888 | ds->ds_rxstat.rs_flags |= | |
889 | (ads.ds_rxstatus3 & AR_2040) ? ATH9K_RX_2040 : 0; | |
890 | ||
891 | if (ads.ds_rxstatus8 & AR_PreDelimCRCErr) | |
892 | ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_PRE; | |
893 | if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) | |
894 | ds->ds_rxstat.rs_flags |= ATH9K_RX_DELIM_CRC_POST; | |
895 | if (ads.ds_rxstatus8 & AR_DecryptBusyErr) | |
896 | ds->ds_rxstat.rs_flags |= ATH9K_RX_DECRYPT_BUSY; | |
897 | ||
898 | if ((ads.ds_rxstatus8 & AR_RxFrameOK) == 0) { | |
899 | if (ads.ds_rxstatus8 & AR_CRCErr) | |
900 | ds->ds_rxstat.rs_status |= ATH9K_RXERR_CRC; | |
901 | else if (ads.ds_rxstatus8 & AR_PHYErr) { | |
902 | ds->ds_rxstat.rs_status |= ATH9K_RXERR_PHY; | |
903 | phyerr = MS(ads.ds_rxstatus8, AR_PHYErrCode); | |
904 | ds->ds_rxstat.rs_phyerr = phyerr; | |
905 | } else if (ads.ds_rxstatus8 & AR_DecryptCRCErr) | |
906 | ds->ds_rxstat.rs_status |= ATH9K_RXERR_DECRYPT; | |
907 | else if (ads.ds_rxstatus8 & AR_MichaelErr) | |
908 | ds->ds_rxstat.rs_status |= ATH9K_RXERR_MIC; | |
909 | } | |
910 | ||
911 | return 0; | |
912 | } | |
7322fd19 | 913 | EXPORT_SYMBOL(ath9k_hw_rxprocdesc); |
f1dc5600 | 914 | |
54e4cec6 | 915 | void ath9k_hw_setuprxdesc(struct ath_hw *ah, struct ath_desc *ds, |
f1dc5600 S |
916 | u32 size, u32 flags) |
917 | { | |
918 | struct ar5416_desc *ads = AR5416DESC(ds); | |
2660b81a | 919 | struct ath9k_hw_capabilities *pCap = &ah->caps; |
f1dc5600 S |
920 | |
921 | ads->ds_ctl1 = size & AR_BufLen; | |
922 | if (flags & ATH9K_RXDESC_INTREQ) | |
923 | ads->ds_ctl1 |= AR_RxIntrReq; | |
924 | ||
925 | ads->ds_rxstatus8 &= ~AR_RxDone; | |
926 | if (!(pCap->hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) | |
927 | memset(&(ads->u), 0, sizeof(ads->u)); | |
f1dc5600 | 928 | } |
7322fd19 | 929 | EXPORT_SYMBOL(ath9k_hw_setuprxdesc); |
f1dc5600 | 930 | |
e7824a50 LR |
931 | /* |
932 | * This can stop or re-enables RX. | |
933 | * | |
934 | * If bool is set this will kill any frame which is currently being | |
935 | * transferred between the MAC and baseband and also prevent any new | |
936 | * frames from getting started. | |
937 | */ | |
cbe61d8a | 938 | bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set) |
f1dc5600 S |
939 | { |
940 | u32 reg; | |
941 | ||
942 | if (set) { | |
943 | REG_SET_BIT(ah, AR_DIAG_SW, | |
944 | (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); | |
945 | ||
0caa7b14 S |
946 | if (!ath9k_hw_wait(ah, AR_OBS_BUS_1, AR_OBS_BUS_1_RX_STATE, |
947 | 0, AH_WAIT_TIMEOUT)) { | |
f1dc5600 S |
948 | REG_CLR_BIT(ah, AR_DIAG_SW, |
949 | (AR_DIAG_RX_DIS | | |
950 | AR_DIAG_RX_ABORT)); | |
951 | ||
952 | reg = REG_READ(ah, AR_OBS_BUS_1); | |
c46917bb LR |
953 | ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, |
954 | "RX failed to go idle in 10 ms RXSM=0x%x\n", | |
955 | reg); | |
f1dc5600 S |
956 | |
957 | return false; | |
958 | } | |
959 | } else { | |
960 | REG_CLR_BIT(ah, AR_DIAG_SW, | |
961 | (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); | |
962 | } | |
963 | ||
964 | return true; | |
965 | } | |
7322fd19 | 966 | EXPORT_SYMBOL(ath9k_hw_setrxabort); |
f1dc5600 | 967 | |
cbe61d8a | 968 | void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp) |
f1dc5600 S |
969 | { |
970 | REG_WRITE(ah, AR_RXDP, rxdp); | |
971 | } | |
7322fd19 | 972 | EXPORT_SYMBOL(ath9k_hw_putrxbuf); |
f1dc5600 | 973 | |
cbe61d8a | 974 | void ath9k_hw_rxena(struct ath_hw *ah) |
f1dc5600 S |
975 | { |
976 | REG_WRITE(ah, AR_CR, AR_CR_RXE); | |
977 | } | |
7322fd19 | 978 | EXPORT_SYMBOL(ath9k_hw_rxena); |
f1dc5600 | 979 | |
cbe61d8a | 980 | void ath9k_hw_startpcureceive(struct ath_hw *ah) |
f1dc5600 | 981 | { |
f1dc5600 S |
982 | ath9k_enable_mib_counters(ah); |
983 | ||
984 | ath9k_ani_reset(ah); | |
e7594072 | 985 | |
8aa15e15 | 986 | REG_CLR_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); |
f1dc5600 | 987 | } |
7322fd19 | 988 | EXPORT_SYMBOL(ath9k_hw_startpcureceive); |
f1dc5600 | 989 | |
cbe61d8a | 990 | void ath9k_hw_stoppcurecv(struct ath_hw *ah) |
f1dc5600 S |
991 | { |
992 | REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); | |
993 | ||
994 | ath9k_hw_disable_mib_counters(ah); | |
995 | } | |
7322fd19 | 996 | EXPORT_SYMBOL(ath9k_hw_stoppcurecv); |
f1dc5600 | 997 | |
cbe61d8a | 998 | bool ath9k_hw_stopdmarecv(struct ath_hw *ah) |
f1dc5600 | 999 | { |
0caa7b14 S |
1000 | #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ |
1001 | #define AH_RX_TIME_QUANTUM 100 /* usec */ | |
c46917bb | 1002 | struct ath_common *common = ath9k_hw_common(ah); |
0caa7b14 S |
1003 | int i; |
1004 | ||
f1dc5600 S |
1005 | REG_WRITE(ah, AR_CR, AR_CR_RXD); |
1006 | ||
0caa7b14 S |
1007 | /* Wait for rx enable bit to go low */ |
1008 | for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { | |
1009 | if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) | |
1010 | break; | |
1011 | udelay(AH_TIME_QUANTUM); | |
1012 | } | |
1013 | ||
1014 | if (i == 0) { | |
c46917bb LR |
1015 | ath_print(common, ATH_DBG_FATAL, |
1016 | "DMA failed to stop in %d ms " | |
1017 | "AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", | |
1018 | AH_RX_STOP_DMA_TIMEOUT / 1000, | |
1019 | REG_READ(ah, AR_CR), | |
1020 | REG_READ(ah, AR_DIAG_SW)); | |
f1dc5600 S |
1021 | return false; |
1022 | } else { | |
1023 | return true; | |
1024 | } | |
0caa7b14 S |
1025 | |
1026 | #undef AH_RX_TIME_QUANTUM | |
1027 | #undef AH_RX_STOP_DMA_TIMEOUT | |
f1dc5600 | 1028 | } |
7322fd19 | 1029 | EXPORT_SYMBOL(ath9k_hw_stopdmarecv); |
536b3a7a LR |
1030 | |
1031 | int ath9k_hw_beaconq_setup(struct ath_hw *ah) | |
1032 | { | |
1033 | struct ath9k_tx_queue_info qi; | |
1034 | ||
1035 | memset(&qi, 0, sizeof(qi)); | |
1036 | qi.tqi_aifs = 1; | |
1037 | qi.tqi_cwmin = 0; | |
1038 | qi.tqi_cwmax = 0; | |
1039 | /* NB: don't enable any interrupts */ | |
1040 | return ath9k_hw_setuptxqueue(ah, ATH9K_TX_QUEUE_BEACON, &qi); | |
1041 | } | |
1042 | EXPORT_SYMBOL(ath9k_hw_beaconq_setup); |