1 | /* |
2 | * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> |
3 | * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> |
4 | * |
5 | * Permission to use, copy, modify, and distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | * |
17 | */ |
18 | |
19 | /********************************************\ |
20 | Queue Control Unit, DCF Control Unit Functions |
21 | \********************************************/ |
22 | |
23 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
24 | |
25 | #include "ath5k.h" |
26 | #include "reg.h" |
27 | #include "debug.h" |
28 | #include <linux/log2.h> |
29 | |
30 | /** |
31 | * DOC: Queue Control Unit (QCU)/DCF Control Unit (DCU) functions |
32 | * |
33 | * Here we setup parameters for the 12 available TX queues. Note that |
34 | * on the various registers we can usually only map the first 10 of them so |
35 | * basically we have 10 queues to play with. Each queue has a matching |
36 | * QCU that controls when the queue will get triggered and multiple QCUs |
37 | * can be mapped to a single DCU that controls the various DFS parameters |
38 | * for the various queues. In our setup we have a 1:1 mapping between QCUs |
39 | * and DCUs allowing us to have different DFS settings for each queue. |
40 | * |
41 | * When a frame goes into a TX queue, QCU decides when it'll trigger a |
42 | * transmission based on various criteria (such as how many data we have inside |
43 | * it's buffer or -if it's a beacon queue- if it's time to fire up the queue |
44 | * based on TSF etc), DCU adds backoff, IFSes etc and then a scheduler |
45 | * (arbitrator) decides the priority of each QCU based on it's configuration |
46 | * (e.g. beacons are always transmitted when they leave DCU bypassing all other |
47 | * frames from other queues waiting to be transmitted). After a frame leaves |
48 | * the DCU it goes to PCU for further processing and then to PHY for |
49 | * the actual transmission. |
50 | */ |
51 | |
52 | |
53 | /******************\ |
54 | * Helper functions * |
55 | \******************/ |
56 | |
57 | /** |
58 | * ath5k_hw_num_tx_pending() - Get number of pending frames for a given queue |
59 | * @ah: The &struct ath5k_hw |
60 | * @queue: One of enum ath5k_tx_queue_id |
61 | */ |
62 | u32 |
63 | ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) |
64 | { |
65 | u32 pending; |
66 | AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); |
67 | |
68 | /* Return if queue is declared inactive */ |
69 | if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) |
70 | return false; |
71 | |
72 | /* XXX: How about AR5K_CFG_TXCNT ? */ |
73 | if (ah->ah_version == AR5K_AR5210) |
74 | return false; |
75 | |
76 | pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue)); |
77 | pending &= AR5K_QCU_STS_FRMPENDCNT; |
78 | |
79 | /* It's possible to have no frames pending even if TXE |
80 | * is set. To indicate that q has not stopped return |
81 | * true */ |
82 | if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue)) |
83 | return true; |
84 | |
85 | return pending; |
86 | } |
87 | |
88 | /** |
89 | * ath5k_hw_release_tx_queue() - Set a transmit queue inactive |
90 | * @ah: The &struct ath5k_hw |
91 | * @queue: One of enum ath5k_tx_queue_id |
92 | */ |
93 | void |
94 | ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) |
95 | { |
96 | if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num)) |
97 | return; |
98 | |
99 | /* This queue will be skipped in further operations */ |
100 | ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE; |
101 | /*For SIMR setup*/ |
102 | AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue); |
103 | } |
104 | |
105 | /** |
106 | * ath5k_cw_validate() - Make sure the given cw is valid |
107 | * @cw_req: The contention window value to check |
108 | * |
109 | * Make sure cw is a power of 2 minus 1 and smaller than 1024 |
110 | */ |
111 | static u16 |
112 | ath5k_cw_validate(u16 cw_req) |
113 | { |
114 | cw_req = min(cw_req, (u16)1023); |
115 | |
116 | /* Check if cw_req + 1 a power of 2 */ |
117 | if (is_power_of_2(n: cw_req + 1)) |
118 | return cw_req; |
119 | |
120 | /* Check if cw_req is a power of 2 */ |
121 | if (is_power_of_2(n: cw_req)) |
122 | return cw_req - 1; |
123 | |
124 | /* If none of the above is correct |
125 | * find the closest power of 2 */ |
126 | cw_req = (u16) roundup_pow_of_two(cw_req) - 1; |
127 | |
128 | return cw_req; |
129 | } |
130 | |
131 | /** |
132 | * ath5k_hw_get_tx_queueprops() - Get properties for a transmit queue |
133 | * @ah: The &struct ath5k_hw |
134 | * @queue: One of enum ath5k_tx_queue_id |
135 | * @queue_info: The &struct ath5k_txq_info to fill |
136 | */ |
137 | int |
138 | ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, |
139 | struct ath5k_txq_info *queue_info) |
140 | { |
141 | memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info)); |
142 | return 0; |
143 | } |
144 | |
145 | /** |
146 | * ath5k_hw_set_tx_queueprops() - Set properties for a transmit queue |
147 | * @ah: The &struct ath5k_hw |
148 | * @queue: One of enum ath5k_tx_queue_id |
149 | * @qinfo: The &struct ath5k_txq_info to use |
150 | * |
151 | * Returns 0 on success or -EIO if queue is inactive |
152 | */ |
153 | int |
154 | ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, |
155 | const struct ath5k_txq_info *qinfo) |
156 | { |
157 | struct ath5k_txq_info *qi; |
158 | |
159 | AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); |
160 | |
161 | qi = &ah->ah_txq[queue]; |
162 | |
163 | if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE) |
164 | return -EIO; |
165 | |
166 | /* copy and validate values */ |
167 | qi->tqi_type = qinfo->tqi_type; |
168 | qi->tqi_subtype = qinfo->tqi_subtype; |
169 | qi->tqi_flags = qinfo->tqi_flags; |
170 | /* |
171 | * According to the docs: Although the AIFS field is 8 bit wide, |
172 | * the maximum supported value is 0xFC. Setting it higher than that |
173 | * will cause the DCU to hang. |
174 | */ |
175 | qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC); |
176 | qi->tqi_cw_min = ath5k_cw_validate(cw_req: qinfo->tqi_cw_min); |
177 | qi->tqi_cw_max = ath5k_cw_validate(cw_req: qinfo->tqi_cw_max); |
178 | qi->tqi_cbr_period = qinfo->tqi_cbr_period; |
179 | qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit; |
180 | qi->tqi_burst_time = qinfo->tqi_burst_time; |
181 | qi->tqi_ready_time = qinfo->tqi_ready_time; |
182 | |
183 | /*XXX: Is this supported on 5210 ?*/ |
184 | /*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/ |
185 | if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA && |
186 | ((qinfo->tqi_subtype == AR5K_WME_AC_VI) || |
187 | (qinfo->tqi_subtype == AR5K_WME_AC_VO))) || |
188 | qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD) |
189 | qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | /** |
195 | * ath5k_hw_setup_tx_queue() - Initialize a transmit queue |
196 | * @ah: The &struct ath5k_hw |
197 | * @queue_type: One of enum ath5k_tx_queue |
198 | * @queue_info: The &struct ath5k_txq_info to use |
199 | * |
200 | * Returns 0 on success, -EINVAL on invalid arguments |
201 | */ |
202 | int |
203 | ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, |
204 | struct ath5k_txq_info *queue_info) |
205 | { |
206 | unsigned int queue; |
207 | int ret; |
208 | |
209 | /* |
210 | * Get queue by type |
211 | */ |
212 | /* 5210 only has 2 queues */ |
213 | if (ah->ah_capabilities.cap_queues.q_tx_num == 2) { |
214 | switch (queue_type) { |
215 | case AR5K_TX_QUEUE_DATA: |
216 | queue = AR5K_TX_QUEUE_ID_NOQCU_DATA; |
217 | break; |
218 | case AR5K_TX_QUEUE_BEACON: |
219 | case AR5K_TX_QUEUE_CAB: |
220 | queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON; |
221 | break; |
222 | default: |
223 | return -EINVAL; |
224 | } |
225 | } else { |
226 | switch (queue_type) { |
227 | case AR5K_TX_QUEUE_DATA: |
228 | queue = queue_info->tqi_subtype; |
229 | break; |
230 | case AR5K_TX_QUEUE_UAPSD: |
231 | queue = AR5K_TX_QUEUE_ID_UAPSD; |
232 | break; |
233 | case AR5K_TX_QUEUE_BEACON: |
234 | queue = AR5K_TX_QUEUE_ID_BEACON; |
235 | break; |
236 | case AR5K_TX_QUEUE_CAB: |
237 | queue = AR5K_TX_QUEUE_ID_CAB; |
238 | break; |
239 | default: |
240 | return -EINVAL; |
241 | } |
242 | } |
243 | |
244 | /* |
245 | * Setup internal queue structure |
246 | */ |
247 | memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info)); |
248 | ah->ah_txq[queue].tqi_type = queue_type; |
249 | |
250 | if (queue_info != NULL) { |
251 | queue_info->tqi_type = queue_type; |
252 | ret = ath5k_hw_set_tx_queueprops(ah, queue, qinfo: queue_info); |
253 | if (ret) |
254 | return ret; |
255 | } |
256 | |
257 | /* |
258 | * We use ah_txq_status to hold a temp value for |
259 | * the Secondary interrupt mask registers on 5211+ |
260 | * check out ath5k_hw_reset_tx_queue |
261 | */ |
262 | AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue); |
263 | |
264 | return queue; |
265 | } |
266 | |
267 | |
268 | /*******************************\ |
269 | * Single QCU/DCU initialization * |
270 | \*******************************/ |
271 | |
272 | /** |
273 | * ath5k_hw_set_tx_retry_limits() - Set tx retry limits on DCU |
274 | * @ah: The &struct ath5k_hw |
275 | * @queue: One of enum ath5k_tx_queue_id |
276 | * |
277 | * This function is used when initializing a queue, to set |
278 | * retry limits based on ah->ah_retry_* and the chipset used. |
279 | */ |
280 | void |
281 | ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah, |
282 | unsigned int queue) |
283 | { |
284 | /* Single data queue on AR5210 */ |
285 | if (ah->ah_version == AR5K_AR5210) { |
286 | struct ath5k_txq_info *tq = &ah->ah_txq[queue]; |
287 | |
288 | if (queue > 0) |
289 | return; |
290 | |
291 | ath5k_hw_reg_write(ah, |
292 | val: (tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) |
293 | | AR5K_REG_SM(ah->ah_retry_long, |
294 | AR5K_NODCU_RETRY_LMT_SLG_RETRY) |
295 | | AR5K_REG_SM(ah->ah_retry_short, |
296 | AR5K_NODCU_RETRY_LMT_SSH_RETRY) |
297 | | AR5K_REG_SM(ah->ah_retry_long, |
298 | AR5K_NODCU_RETRY_LMT_LG_RETRY) |
299 | | AR5K_REG_SM(ah->ah_retry_short, |
300 | AR5K_NODCU_RETRY_LMT_SH_RETRY), |
301 | AR5K_NODCU_RETRY_LMT); |
302 | /* DCU on AR5211+ */ |
303 | } else { |
304 | ath5k_hw_reg_write(ah, |
305 | AR5K_REG_SM(ah->ah_retry_long, |
306 | AR5K_DCU_RETRY_LMT_RTS) |
307 | | AR5K_REG_SM(ah->ah_retry_long, |
308 | AR5K_DCU_RETRY_LMT_STA_RTS) |
309 | | AR5K_REG_SM(max(ah->ah_retry_long, ah->ah_retry_short), |
310 | AR5K_DCU_RETRY_LMT_STA_DATA), |
311 | AR5K_QUEUE_DFS_RETRY_LIMIT(queue)); |
312 | } |
313 | } |
314 | |
315 | /** |
316 | * ath5k_hw_reset_tx_queue() - Initialize a single hw queue |
317 | * @ah: The &struct ath5k_hw |
318 | * @queue: One of enum ath5k_tx_queue_id |
319 | * |
320 | * Set DCF properties for the given transmit queue on DCU |
321 | * and configures all queue-specific parameters. |
322 | */ |
323 | int |
324 | ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) |
325 | { |
326 | struct ath5k_txq_info *tq = &ah->ah_txq[queue]; |
327 | |
328 | AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); |
329 | |
330 | /* Skip if queue inactive or if we are on AR5210 |
331 | * that doesn't have QCU/DCU */ |
332 | if ((ah->ah_version == AR5K_AR5210) || |
333 | (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)) |
334 | return 0; |
335 | |
336 | /* |
337 | * Set contention window (cw_min/cw_max) |
338 | * and arbitrated interframe space (aifs)... |
339 | */ |
340 | ath5k_hw_reg_write(ah, |
341 | AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | |
342 | AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | |
343 | AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS), |
344 | AR5K_QUEUE_DFS_LOCAL_IFS(queue)); |
345 | |
346 | /* |
347 | * Set tx retry limits for this queue |
348 | */ |
349 | ath5k_hw_set_tx_retry_limits(ah, queue); |
350 | |
351 | |
352 | /* |
353 | * Set misc registers |
354 | */ |
355 | |
356 | /* Enable DCU to wait for next fragment from QCU */ |
357 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), |
358 | AR5K_DCU_MISC_FRAG_WAIT); |
359 | |
360 | /* On Maui and Spirit use the global seqnum on DCU */ |
361 | if (ah->ah_mac_version < AR5K_SREV_AR5211) |
362 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), |
363 | AR5K_DCU_MISC_SEQNUM_CTL); |
364 | |
365 | /* Constant bit rate period */ |
366 | if (tq->tqi_cbr_period) { |
367 | ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period, |
368 | AR5K_QCU_CBRCFG_INTVAL) | |
369 | AR5K_REG_SM(tq->tqi_cbr_overflow_limit, |
370 | AR5K_QCU_CBRCFG_ORN_THRES), |
371 | AR5K_QUEUE_CBRCFG(queue)); |
372 | |
373 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), |
374 | AR5K_QCU_MISC_FRSHED_CBR); |
375 | |
376 | if (tq->tqi_cbr_overflow_limit) |
377 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), |
378 | AR5K_QCU_MISC_CBR_THRES_ENABLE); |
379 | } |
380 | |
381 | /* Ready time interval */ |
382 | if (tq->tqi_ready_time && (tq->tqi_type != AR5K_TX_QUEUE_CAB)) |
383 | ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time, |
384 | AR5K_QCU_RDYTIMECFG_INTVAL) | |
385 | AR5K_QCU_RDYTIMECFG_ENABLE, |
386 | AR5K_QUEUE_RDYTIMECFG(queue)); |
387 | |
388 | if (tq->tqi_burst_time) { |
389 | ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time, |
390 | AR5K_DCU_CHAN_TIME_DUR) | |
391 | AR5K_DCU_CHAN_TIME_ENABLE, |
392 | AR5K_QUEUE_DFS_CHANNEL_TIME(queue)); |
393 | |
394 | if (tq->tqi_flags & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) |
395 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), |
396 | AR5K_QCU_MISC_RDY_VEOL_POLICY); |
397 | } |
398 | |
399 | /* Enable/disable Post frame backoff */ |
400 | if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) |
401 | ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS, |
402 | AR5K_QUEUE_DFS_MISC(queue)); |
403 | |
404 | /* Enable/disable fragmentation burst backoff */ |
405 | if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) |
406 | ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG, |
407 | AR5K_QUEUE_DFS_MISC(queue)); |
408 | |
409 | /* |
410 | * Set registers by queue type |
411 | */ |
412 | switch (tq->tqi_type) { |
413 | case AR5K_TX_QUEUE_BEACON: |
414 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), |
415 | AR5K_QCU_MISC_FRSHED_DBA_GT | |
416 | AR5K_QCU_MISC_CBREXP_BCN_DIS | |
417 | AR5K_QCU_MISC_BCN_ENABLE); |
418 | |
419 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), |
420 | (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << |
421 | AR5K_DCU_MISC_ARBLOCK_CTL_S) | |
422 | AR5K_DCU_MISC_ARBLOCK_IGNORE | |
423 | AR5K_DCU_MISC_POST_FR_BKOFF_DIS | |
424 | AR5K_DCU_MISC_BCN_ENABLE); |
425 | break; |
426 | |
427 | case AR5K_TX_QUEUE_CAB: |
428 | /* XXX: use BCN_SENT_GT, if we can figure out how */ |
429 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), |
430 | AR5K_QCU_MISC_FRSHED_DBA_GT | |
431 | AR5K_QCU_MISC_CBREXP_DIS | |
432 | AR5K_QCU_MISC_CBREXP_BCN_DIS); |
433 | |
434 | ath5k_hw_reg_write(ah, val: ((tq->tqi_ready_time - |
435 | (AR5K_TUNE_SW_BEACON_RESP - |
436 | AR5K_TUNE_DMA_BEACON_RESP) - |
437 | AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | |
438 | AR5K_QCU_RDYTIMECFG_ENABLE, |
439 | AR5K_QUEUE_RDYTIMECFG(queue)); |
440 | |
441 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), |
442 | (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << |
443 | AR5K_DCU_MISC_ARBLOCK_CTL_S)); |
444 | break; |
445 | |
446 | case AR5K_TX_QUEUE_UAPSD: |
447 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), |
448 | AR5K_QCU_MISC_CBREXP_DIS); |
449 | break; |
450 | |
451 | case AR5K_TX_QUEUE_DATA: |
452 | default: |
453 | break; |
454 | } |
455 | |
456 | /* TODO: Handle frame compression */ |
457 | |
458 | /* |
459 | * Enable interrupts for this tx queue |
460 | * in the secondary interrupt mask registers |
461 | */ |
462 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE) |
463 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue); |
464 | |
465 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE) |
466 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue); |
467 | |
468 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE) |
469 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue); |
470 | |
471 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE) |
472 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue); |
473 | |
474 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE) |
475 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue); |
476 | |
477 | if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE) |
478 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue); |
479 | |
480 | if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE) |
481 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue); |
482 | |
483 | if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE) |
484 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue); |
485 | |
486 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE) |
487 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue); |
488 | |
489 | /* Update secondary interrupt mask registers */ |
490 | |
491 | /* Filter out inactive queues */ |
492 | ah->ah_txq_imr_txok &= ah->ah_txq_status; |
493 | ah->ah_txq_imr_txerr &= ah->ah_txq_status; |
494 | ah->ah_txq_imr_txurn &= ah->ah_txq_status; |
495 | ah->ah_txq_imr_txdesc &= ah->ah_txq_status; |
496 | ah->ah_txq_imr_txeol &= ah->ah_txq_status; |
497 | ah->ah_txq_imr_cbrorn &= ah->ah_txq_status; |
498 | ah->ah_txq_imr_cbrurn &= ah->ah_txq_status; |
499 | ah->ah_txq_imr_qtrig &= ah->ah_txq_status; |
500 | ah->ah_txq_imr_nofrm &= ah->ah_txq_status; |
501 | |
502 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok, |
503 | AR5K_SIMR0_QCU_TXOK) | |
504 | AR5K_REG_SM(ah->ah_txq_imr_txdesc, |
505 | AR5K_SIMR0_QCU_TXDESC), |
506 | AR5K_SIMR0); |
507 | |
508 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr, |
509 | AR5K_SIMR1_QCU_TXERR) | |
510 | AR5K_REG_SM(ah->ah_txq_imr_txeol, |
511 | AR5K_SIMR1_QCU_TXEOL), |
512 | AR5K_SIMR1); |
513 | |
514 | /* Update SIMR2 but don't overwrite rest simr2 settings */ |
515 | AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN); |
516 | AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2, |
517 | AR5K_REG_SM(ah->ah_txq_imr_txurn, |
518 | AR5K_SIMR2_QCU_TXURN)); |
519 | |
520 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn, |
521 | AR5K_SIMR3_QCBRORN) | |
522 | AR5K_REG_SM(ah->ah_txq_imr_cbrurn, |
523 | AR5K_SIMR3_QCBRURN), |
524 | AR5K_SIMR3); |
525 | |
526 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig, |
527 | AR5K_SIMR4_QTRIG), AR5K_SIMR4); |
528 | |
529 | /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */ |
530 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm, |
531 | AR5K_TXNOFRM_QCU), AR5K_TXNOFRM); |
532 | |
533 | /* No queue has TXNOFRM enabled, disable the interrupt |
534 | * by setting AR5K_TXNOFRM to zero */ |
535 | if (ah->ah_txq_imr_nofrm == 0) |
536 | ath5k_hw_reg_write(ah, val: 0, AR5K_TXNOFRM); |
537 | |
538 | /* Set QCU mask for this DCU to save power */ |
539 | AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue); |
540 | |
541 | return 0; |
542 | } |
543 | |
544 | |
545 | /**************************\ |
546 | * Global QCU/DCU functions * |
547 | \**************************/ |
548 | |
549 | /** |
550 | * ath5k_hw_set_ifs_intervals() - Set global inter-frame spaces on DCU |
551 | * @ah: The &struct ath5k_hw |
552 | * @slot_time: Slot time in us |
553 | * |
554 | * Sets the global IFS intervals on DCU (also works on AR5210) for |
555 | * the given slot time and the current bwmode. |
556 | */ |
557 | int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) |
558 | { |
559 | struct ieee80211_channel *channel = ah->ah_current_channel; |
560 | enum nl80211_band band; |
561 | struct ieee80211_supported_band *sband; |
562 | struct ieee80211_rate *rate; |
563 | u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock; |
564 | u32 slot_time_clock = ath5k_hw_htoclock(ah, usec: slot_time); |
565 | u32 rate_flags, i; |
566 | |
567 | if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX) |
568 | return -EINVAL; |
569 | |
570 | sifs = ath5k_hw_get_default_sifs(ah); |
571 | sifs_clock = ath5k_hw_htoclock(ah, usec: sifs - 2); |
572 | |
573 | /* EIFS |
574 | * Txtime of ack at lowest rate + SIFS + DIFS |
575 | * (DIFS = SIFS + 2 * Slot time) |
576 | * |
577 | * Note: HAL has some predefined values for EIFS |
578 | * Turbo: (37 + 2 * 6) |
579 | * Default: (74 + 2 * 9) |
580 | * Half: (149 + 2 * 13) |
581 | * Quarter: (298 + 2 * 21) |
582 | * |
583 | * (74 + 2 * 6) for AR5210 default and turbo ! |
584 | * |
585 | * According to the formula we have |
586 | * ack_tx_time = 25 for turbo and |
587 | * ack_tx_time = 42.5 * clock multiplier |
588 | * for default/half/quarter. |
589 | * |
590 | * This can't be right, 42 is what we would get |
591 | * from ath5k_hw_get_frame_dur_for_bwmode or |
592 | * ieee80211_generic_frame_duration for zero frame |
593 | * length and without SIFS ! |
594 | * |
595 | * Also we have different lowest rate for 802.11a |
596 | */ |
597 | if (channel->band == NL80211_BAND_5GHZ) |
598 | band = NL80211_BAND_5GHZ; |
599 | else |
600 | band = NL80211_BAND_2GHZ; |
601 | |
602 | switch (ah->ah_bwmode) { |
603 | case AR5K_BWMODE_5MHZ: |
604 | rate_flags = IEEE80211_RATE_SUPPORTS_5MHZ; |
605 | break; |
606 | case AR5K_BWMODE_10MHZ: |
607 | rate_flags = IEEE80211_RATE_SUPPORTS_10MHZ; |
608 | break; |
609 | default: |
610 | rate_flags = 0; |
611 | break; |
612 | } |
613 | sband = &ah->sbands[band]; |
614 | rate = NULL; |
615 | for (i = 0; i < sband->n_bitrates; i++) { |
616 | if ((rate_flags & sband->bitrates[i].flags) != rate_flags) |
617 | continue; |
618 | rate = &sband->bitrates[i]; |
619 | break; |
620 | } |
621 | if (WARN_ON(!rate)) |
622 | return -EINVAL; |
623 | |
624 | ack_tx_time = ath5k_hw_get_frame_duration(ah, band, len: 10, rate, shortpre: false); |
625 | |
626 | /* ack_tx_time includes an SIFS already */ |
627 | eifs = ack_tx_time + sifs + 2 * slot_time; |
628 | eifs_clock = ath5k_hw_htoclock(ah, usec: eifs); |
629 | |
630 | /* Set IFS settings on AR5210 */ |
631 | if (ah->ah_version == AR5K_AR5210) { |
632 | u32 pifs, pifs_clock, difs, difs_clock; |
633 | |
634 | /* Set slot time */ |
635 | ath5k_hw_reg_write(ah, val: slot_time_clock, AR5K_SLOT_TIME); |
636 | |
637 | /* Set EIFS */ |
638 | eifs_clock = AR5K_REG_SM(eifs_clock, AR5K_IFS1_EIFS); |
639 | |
640 | /* PIFS = Slot time + SIFS */ |
641 | pifs = slot_time + sifs; |
642 | pifs_clock = ath5k_hw_htoclock(ah, usec: pifs); |
643 | pifs_clock = AR5K_REG_SM(pifs_clock, AR5K_IFS1_PIFS); |
644 | |
645 | /* DIFS = SIFS + 2 * Slot time */ |
646 | difs = sifs + 2 * slot_time; |
647 | difs_clock = ath5k_hw_htoclock(ah, usec: difs); |
648 | |
649 | /* Set SIFS/DIFS */ |
650 | ath5k_hw_reg_write(ah, val: (difs_clock << |
651 | AR5K_IFS0_DIFS_S) | sifs_clock, |
652 | AR5K_IFS0); |
653 | |
654 | /* Set PIFS/EIFS and preserve AR5K_INIT_CARR_SENSE_EN */ |
655 | ath5k_hw_reg_write(ah, val: pifs_clock | eifs_clock | |
656 | (AR5K_INIT_CARR_SENSE_EN << AR5K_IFS1_CS_EN_S), |
657 | AR5K_IFS1); |
658 | |
659 | return 0; |
660 | } |
661 | |
662 | /* Set IFS slot time */ |
663 | ath5k_hw_reg_write(ah, val: slot_time_clock, AR5K_DCU_GBL_IFS_SLOT); |
664 | |
665 | /* Set EIFS interval */ |
666 | ath5k_hw_reg_write(ah, val: eifs_clock, AR5K_DCU_GBL_IFS_EIFS); |
667 | |
668 | /* Set SIFS interval in usecs */ |
669 | AR5K_REG_WRITE_BITS(ah, AR5K_DCU_GBL_IFS_MISC, |
670 | AR5K_DCU_GBL_IFS_MISC_SIFS_DUR_USEC, |
671 | sifs); |
672 | |
673 | /* Set SIFS interval in clock cycles */ |
674 | ath5k_hw_reg_write(ah, val: sifs_clock, AR5K_DCU_GBL_IFS_SIFS); |
675 | |
676 | return 0; |
677 | } |
678 | |
679 | |
680 | /** |
681 | * ath5k_hw_init_queues() - Initialize tx queues |
682 | * @ah: The &struct ath5k_hw |
683 | * |
684 | * Initializes all tx queues based on information on |
685 | * ah->ah_txq* set by the driver |
686 | */ |
687 | int |
688 | ath5k_hw_init_queues(struct ath5k_hw *ah) |
689 | { |
690 | int i, ret; |
691 | |
692 | /* TODO: HW Compression support for data queues */ |
693 | /* TODO: Burst prefetch for data queues */ |
694 | |
695 | /* |
696 | * Reset queues and start beacon timers at the end of the reset routine |
697 | * This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping |
698 | * Note: If we want we can assign multiple qcus on one dcu. |
699 | */ |
700 | if (ah->ah_version != AR5K_AR5210) |
701 | for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) { |
702 | ret = ath5k_hw_reset_tx_queue(ah, queue: i); |
703 | if (ret) { |
704 | ATH5K_ERR(ah, |
705 | "failed to reset TX queue #%d\n" , i); |
706 | return ret; |
707 | } |
708 | } |
709 | else |
710 | /* No QCU/DCU on AR5210, just set tx |
711 | * retry limits. We set IFS parameters |
712 | * on ath5k_hw_set_ifs_intervals */ |
713 | ath5k_hw_set_tx_retry_limits(ah, queue: 0); |
714 | |
715 | /* Set the turbo flag when operating on 40MHz */ |
716 | if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) |
717 | AR5K_REG_ENABLE_BITS(ah, AR5K_DCU_GBL_IFS_MISC, |
718 | AR5K_DCU_GBL_IFS_MISC_TURBO_MODE); |
719 | |
720 | /* If we didn't set IFS timings through |
721 | * ath5k_hw_set_coverage_class make sure |
722 | * we set them here */ |
723 | if (!ah->ah_coverage_class) { |
724 | unsigned int slot_time = ath5k_hw_get_default_slottime(ah); |
725 | ath5k_hw_set_ifs_intervals(ah, slot_time); |
726 | } |
727 | |
728 | return 0; |
729 | } |
730 | |