blob: 84c717ded1c5de8aad2a952f4d6afb507da8d261 [file] [log] [blame]
Nick Kossifidisc6e387a2008-08-29 22:45:39 +03001/*
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/********************************************\
20Queue Control Unit, DFS Control Unit Functions
21\********************************************/
22
23#include "ath5k.h"
24#include "reg.h"
25#include "debug.h"
26#include "base.h"
27
28/*
29 * Get properties for a transmit queue
30 */
31int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
32 struct ath5k_txq_info *queue_info)
33{
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030034 memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info));
35 return 0;
36}
37
38/*
Bruno Randolfde8af452010-09-17 11:37:12 +090039 * Make sure cw is a power of 2 minus 1 and smaller than 1024
40 */
41static u16 ath5k_cw_validate(u16 cw_req)
42{
43 u32 cw = 1;
44 cw_req = min(cw_req, (u16)1023);
45
46 while (cw < cw_req)
47 cw = (cw << 1) | 1;
48
49 return cw;
50}
51
52/*
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030053 * Set properties for a transmit queue
54 */
55int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
Bruno Randolfde8af452010-09-17 11:37:12 +090056 const struct ath5k_txq_info *qinfo)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030057{
Bruno Randolfde8af452010-09-17 11:37:12 +090058 struct ath5k_txq_info *qi;
59
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030060 AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
61
Bruno Randolfde8af452010-09-17 11:37:12 +090062 qi = &ah->ah_txq[queue];
63
64 if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030065 return -EIO;
66
Bruno Randolfde8af452010-09-17 11:37:12 +090067 /* copy and validate values */
68 qi->tqi_type = qinfo->tqi_type;
69 qi->tqi_subtype = qinfo->tqi_subtype;
70 qi->tqi_flags = qinfo->tqi_flags;
71 /*
72 * According to the docs: Although the AIFS field is 8 bit wide,
73 * the maximum supported value is 0xFC. Setting it higher than that
74 * will cause the DCU to hang.
75 */
76 qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC);
77 qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min);
78 qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max);
79 qi->tqi_cbr_period = qinfo->tqi_cbr_period;
80 qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit;
81 qi->tqi_burst_time = qinfo->tqi_burst_time;
82 qi->tqi_ready_time = qinfo->tqi_ready_time;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030083
84 /*XXX: Is this supported on 5210 ?*/
Bruno Randolfde8af452010-09-17 11:37:12 +090085 /*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/
86 if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA &&
87 ((qinfo->tqi_subtype == AR5K_WME_AC_VI) ||
88 (qinfo->tqi_subtype == AR5K_WME_AC_VO))) ||
89 qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD)
90 qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030091
92 return 0;
93}
94
95/*
96 * Initialize a transmit queue
97 */
98int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
99 struct ath5k_txq_info *queue_info)
100{
101 unsigned int queue;
102 int ret;
103
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300104 /*
105 * Get queue by type
106 */
107 /*5210 only has 2 queues*/
108 if (ah->ah_version == AR5K_AR5210) {
109 switch (queue_type) {
110 case AR5K_TX_QUEUE_DATA:
111 queue = AR5K_TX_QUEUE_ID_NOQCU_DATA;
112 break;
113 case AR5K_TX_QUEUE_BEACON:
114 case AR5K_TX_QUEUE_CAB:
115 queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON;
116 break;
117 default:
118 return -EINVAL;
119 }
120 } else {
121 switch (queue_type) {
122 case AR5K_TX_QUEUE_DATA:
123 for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
124 ah->ah_txq[queue].tqi_type !=
125 AR5K_TX_QUEUE_INACTIVE; queue++) {
126
127 if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
128 return -EINVAL;
129 }
130 break;
131 case AR5K_TX_QUEUE_UAPSD:
132 queue = AR5K_TX_QUEUE_ID_UAPSD;
133 break;
134 case AR5K_TX_QUEUE_BEACON:
135 queue = AR5K_TX_QUEUE_ID_BEACON;
136 break;
137 case AR5K_TX_QUEUE_CAB:
138 queue = AR5K_TX_QUEUE_ID_CAB;
139 break;
140 case AR5K_TX_QUEUE_XR_DATA:
141 if (ah->ah_version != AR5K_AR5212)
142 ATH5K_ERR(ah->ah_sc,
143 "XR data queues only supported in"
144 " 5212!\n");
145 queue = AR5K_TX_QUEUE_ID_XR_DATA;
146 break;
147 default:
148 return -EINVAL;
149 }
150 }
151
152 /*
153 * Setup internal queue structure
154 */
155 memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info));
156 ah->ah_txq[queue].tqi_type = queue_type;
157
158 if (queue_info != NULL) {
159 queue_info->tqi_type = queue_type;
160 ret = ath5k_hw_set_tx_queueprops(ah, queue, queue_info);
161 if (ret)
162 return ret;
163 }
164
165 /*
166 * We use ah_txq_status to hold a temp value for
167 * the Secondary interrupt mask registers on 5211+
168 * check out ath5k_hw_reset_tx_queue
169 */
170 AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue);
171
172 return queue;
173}
174
175/*
176 * Get number of pending frames
177 * for a specific queue [5211+]
178 */
179u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue)
180{
Nick Kossifidis846567622009-01-06 17:27:06 +0200181 u32 pending;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300182 AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
183
184 /* Return if queue is declared inactive */
185 if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
186 return false;
187
188 /* XXX: How about AR5K_CFG_TXCNT ? */
189 if (ah->ah_version == AR5K_AR5210)
190 return false;
191
Nick Kossifidis428cbd42009-04-30 15:55:47 -0400192 pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue));
193 pending &= AR5K_QCU_STS_FRMPENDCNT;
Nick Kossifidis846567622009-01-06 17:27:06 +0200194
195 /* It's possible to have no frames pending even if TXE
196 * is set. To indicate that q has not stopped return
197 * true */
198 if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
199 return true;
200
201 return pending;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300202}
203
204/*
205 * Set a transmit queue inactive
206 */
207void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
208{
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300209 if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num))
210 return;
211
212 /* This queue will be skipped in further operations */
213 ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE;
214 /*For SIMR setup*/
215 AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue);
216}
217
218/*
219 * Set DFS properties for a transmit queue on DCU
220 */
221int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
222{
Bruno Randolfde8af452010-09-17 11:37:12 +0900223 u32 retry_lg, retry_sh;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300224 struct ath5k_txq_info *tq = &ah->ah_txq[queue];
225
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300226 AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
227
228 tq = &ah->ah_txq[queue];
229
230 if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
231 return 0;
232
233 if (ah->ah_version == AR5K_AR5210) {
234 /* Only handle data queues, others will be ignored */
235 if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
236 return 0;
237
238 /* Set Slot time */
239 ath5k_hw_reg_write(ah, ah->ah_turbo ?
240 AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
241 AR5K_SLOT_TIME);
242 /* Set ACK_CTS timeout */
243 ath5k_hw_reg_write(ah, ah->ah_turbo ?
244 AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
245 AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
246 /* Set Transmit Latency */
247 ath5k_hw_reg_write(ah, ah->ah_turbo ?
248 AR5K_INIT_TRANSMIT_LATENCY_TURBO :
249 AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210);
250
251 /* Set IFS0 */
252 if (ah->ah_turbo) {
253 ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
Bruno Randolfde8af452010-09-17 11:37:12 +0900254 tq->tqi_aifs * AR5K_INIT_SLOT_TIME_TURBO) <<
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300255 AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
256 AR5K_IFS0);
257 } else {
258 ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
Bruno Randolfde8af452010-09-17 11:37:12 +0900259 tq->tqi_aifs * AR5K_INIT_SLOT_TIME) <<
260 AR5K_IFS0_DIFS_S) |
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300261 AR5K_INIT_SIFS, AR5K_IFS0);
262 }
263
264 /* Set IFS1 */
265 ath5k_hw_reg_write(ah, ah->ah_turbo ?
266 AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
267 AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
268 /* Set AR5K_PHY_SETTLING */
269 ath5k_hw_reg_write(ah, ah->ah_turbo ?
270 (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
271 | 0x38 :
272 (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F)
273 | 0x1C,
274 AR5K_PHY_SETTLING);
275 /* Set Frame Control Register */
276 ath5k_hw_reg_write(ah, ah->ah_turbo ?
277 (AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE |
278 AR5K_PHY_TURBO_SHORT | 0x2020) :
279 (AR5K_PHY_FRAME_CTL_INI | 0x1020),
280 AR5K_PHY_FRAME_CTL_5210);
281 }
282
283 /*
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300284 * Calculate and set retry limits
285 */
286 if (ah->ah_software_retry) {
287 /* XXX Need to test this */
288 retry_lg = ah->ah_limit_tx_retries;
289 retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
290 AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
291 } else {
292 retry_lg = AR5K_INIT_LG_RETRY;
293 retry_sh = AR5K_INIT_SH_RETRY;
294 }
295
296 /*No QCU/DCU [5210]*/
297 if (ah->ah_version == AR5K_AR5210) {
298 ath5k_hw_reg_write(ah,
Bruno Randolfde8af452010-09-17 11:37:12 +0900299 (tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300300 | AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
301 AR5K_NODCU_RETRY_LMT_SLG_RETRY)
302 | AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
303 AR5K_NODCU_RETRY_LMT_SSH_RETRY)
304 | AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
305 | AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
306 AR5K_NODCU_RETRY_LMT);
307 } else {
308 /*QCU/DCU [5211+]*/
309 ath5k_hw_reg_write(ah,
310 AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
311 AR5K_DCU_RETRY_LMT_SLG_RETRY) |
312 AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
313 AR5K_DCU_RETRY_LMT_SSH_RETRY) |
314 AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
315 AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
316 AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
317
318 /*===Rest is also for QCU/DCU only [5211+]===*/
319
320 /*
Bruno Randolfde8af452010-09-17 11:37:12 +0900321 * Set contention window (cw_min/cw_max)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300322 * and arbitrated interframe space (aifs)...
323 */
324 ath5k_hw_reg_write(ah,
Bruno Randolfde8af452010-09-17 11:37:12 +0900325 AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
326 AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
327 AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS),
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300328 AR5K_QUEUE_DFS_LOCAL_IFS(queue));
329
330 /*
331 * Set misc registers
332 */
Nick Kossifidis846567622009-01-06 17:27:06 +0200333 /* Enable DCU early termination for this queue */
334 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
335 AR5K_QCU_MISC_DCU_EARLY);
336
337 /* Enable DCU to wait for next fragment from QCU */
338 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
339 AR5K_DCU_MISC_FRAG_WAIT);
340
341 /* On Maui and Spirit use the global seqnum on DCU */
342 if (ah->ah_mac_version < AR5K_SREV_AR5211)
343 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
344 AR5K_DCU_MISC_SEQNUM_CTL);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300345
346 if (tq->tqi_cbr_period) {
347 ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
348 AR5K_QCU_CBRCFG_INTVAL) |
349 AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
350 AR5K_QCU_CBRCFG_ORN_THRES),
351 AR5K_QUEUE_CBRCFG(queue));
352 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
353 AR5K_QCU_MISC_FRSHED_CBR);
354 if (tq->tqi_cbr_overflow_limit)
355 AR5K_REG_ENABLE_BITS(ah,
356 AR5K_QUEUE_MISC(queue),
357 AR5K_QCU_MISC_CBR_THRES_ENABLE);
358 }
359
Nick Kossifidis846567622009-01-06 17:27:06 +0200360 if (tq->tqi_ready_time &&
Julia Lawall4d30d302009-08-08 15:22:26 +0200361 (tq->tqi_type != AR5K_TX_QUEUE_CAB))
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300362 ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time,
363 AR5K_QCU_RDYTIMECFG_INTVAL) |
364 AR5K_QCU_RDYTIMECFG_ENABLE,
365 AR5K_QUEUE_RDYTIMECFG(queue));
366
367 if (tq->tqi_burst_time) {
368 ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time,
369 AR5K_DCU_CHAN_TIME_DUR) |
370 AR5K_DCU_CHAN_TIME_ENABLE,
371 AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
372
373 if (tq->tqi_flags
374 & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
375 AR5K_REG_ENABLE_BITS(ah,
376 AR5K_QUEUE_MISC(queue),
377 AR5K_QCU_MISC_RDY_VEOL_POLICY);
378 }
379
380 if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
381 ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
382 AR5K_QUEUE_DFS_MISC(queue));
383
384 if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
385 ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
386 AR5K_QUEUE_DFS_MISC(queue));
387
388 /*
389 * Set registers by queue type
390 */
391 switch (tq->tqi_type) {
392 case AR5K_TX_QUEUE_BEACON:
393 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
394 AR5K_QCU_MISC_FRSHED_DBA_GT |
Nick Kossifidis1bef0162008-09-29 02:09:09 +0300395 AR5K_QCU_MISC_CBREXP_BCN_DIS |
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300396 AR5K_QCU_MISC_BCN_ENABLE);
397
398 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
399 (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
400 AR5K_DCU_MISC_ARBLOCK_CTL_S) |
Nick Kossifidis428cbd42009-04-30 15:55:47 -0400401 AR5K_DCU_MISC_ARBLOCK_IGNORE |
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300402 AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
403 AR5K_DCU_MISC_BCN_ENABLE);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300404 break;
405
406 case AR5K_TX_QUEUE_CAB:
Bob Copelanda951ae22010-01-20 23:51:04 -0500407 /* XXX: use BCN_SENT_GT, if we can figure out how */
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300408 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
Bob Copelanda951ae22010-01-20 23:51:04 -0500409 AR5K_QCU_MISC_FRSHED_DBA_GT |
Nick Kossifidis1bef0162008-09-29 02:09:09 +0300410 AR5K_QCU_MISC_CBREXP_DIS |
411 AR5K_QCU_MISC_CBREXP_BCN_DIS);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300412
Bob Copelanda951ae22010-01-20 23:51:04 -0500413 ath5k_hw_reg_write(ah, ((tq->tqi_ready_time -
Nick Kossifidis846567622009-01-06 17:27:06 +0200414 (AR5K_TUNE_SW_BEACON_RESP -
415 AR5K_TUNE_DMA_BEACON_RESP) -
416 AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) |
417 AR5K_QCU_RDYTIMECFG_ENABLE,
418 AR5K_QUEUE_RDYTIMECFG(queue));
419
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300420 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
421 (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
422 AR5K_DCU_MISC_ARBLOCK_CTL_S));
423 break;
424
425 case AR5K_TX_QUEUE_UAPSD:
426 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
Nick Kossifidis1bef0162008-09-29 02:09:09 +0300427 AR5K_QCU_MISC_CBREXP_DIS);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300428 break;
429
430 case AR5K_TX_QUEUE_DATA:
431 default:
432 break;
433 }
434
Nick Kossifidis846567622009-01-06 17:27:06 +0200435 /* TODO: Handle frame compression */
436
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300437 /*
438 * Enable interrupts for this tx queue
439 * in the secondary interrupt mask registers
440 */
441 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE)
442 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue);
443
444 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE)
445 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue);
446
447 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE)
448 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue);
449
450 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE)
451 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue);
452
453 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
454 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
455
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200456 if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
457 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
458
459 if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
460 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
461
462 if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
463 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
464
465 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
466 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300467
468 /* Update secondary interrupt mask registers */
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200469
470 /* Filter out inactive queues */
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300471 ah->ah_txq_imr_txok &= ah->ah_txq_status;
472 ah->ah_txq_imr_txerr &= ah->ah_txq_status;
473 ah->ah_txq_imr_txurn &= ah->ah_txq_status;
474 ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
475 ah->ah_txq_imr_txeol &= ah->ah_txq_status;
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200476 ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
477 ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
478 ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
479 ah->ah_txq_imr_nofrm &= ah->ah_txq_status;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300480
481 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
482 AR5K_SIMR0_QCU_TXOK) |
483 AR5K_REG_SM(ah->ah_txq_imr_txdesc,
484 AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0);
485 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr,
486 AR5K_SIMR1_QCU_TXERR) |
487 AR5K_REG_SM(ah->ah_txq_imr_txeol,
488 AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1);
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200489 /* Update simr2 but don't overwrite rest simr2 settings */
490 AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
491 AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
492 AR5K_REG_SM(ah->ah_txq_imr_txurn,
493 AR5K_SIMR2_QCU_TXURN));
494 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
495 AR5K_SIMR3_QCBRORN) |
496 AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
497 AR5K_SIMR3_QCBRURN), AR5K_SIMR3);
498 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
499 AR5K_SIMR4_QTRIG), AR5K_SIMR4);
500 /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
501 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
502 AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
503 /* No queue has TXNOFRM enabled, disable the interrupt
504 * by setting AR5K_TXNOFRM to zero */
505 if (ah->ah_txq_imr_nofrm == 0)
506 ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
Nick Kossifidis846567622009-01-06 17:27:06 +0200507
508 /* Set QCU mask for this DCU to save power */
509 AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300510 }
511
512 return 0;
513}
514
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300515/*
516 * Set slot time on DCU
517 */
518int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time)
519{
Lukáš Turek3578e6e2009-12-21 22:50:50 +0100520 u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100521
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100522 if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300523 return -EINVAL;
524
525 if (ah->ah_version == AR5K_AR5210)
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100526 ath5k_hw_reg_write(ah, slot_time_clock, AR5K_SLOT_TIME);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300527 else
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100528 ath5k_hw_reg_write(ah, slot_time_clock, AR5K_DCU_GBL_IFS_SLOT);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300529
530 return 0;
531}
532