blob: c422d5c92d46430ee472d165170b690d63c1f45c [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
Nick Kossifidis9320b5c42010-11-23 20:36:45 +020028
29/******************\
30* Helper functions *
31\******************/
32
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030033/*
Nick Kossifidis9320b5c42010-11-23 20:36:45 +020034 * Get number of pending frames
35 * for a specific queue [5211+]
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030036 */
Nick Kossifidis9320b5c42010-11-23 20:36:45 +020037u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030038{
Nick Kossifidis9320b5c42010-11-23 20:36:45 +020039 u32 pending;
40 AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
41
42 /* Return if queue is declared inactive */
43 if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE)
44 return false;
45
46 /* XXX: How about AR5K_CFG_TXCNT ? */
47 if (ah->ah_version == AR5K_AR5210)
48 return false;
49
50 pending = ath5k_hw_reg_read(ah, AR5K_QUEUE_STATUS(queue));
51 pending &= AR5K_QCU_STS_FRMPENDCNT;
52
53 /* It's possible to have no frames pending even if TXE
54 * is set. To indicate that q has not stopped return
55 * true */
56 if (!pending && AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, queue))
57 return true;
58
59 return pending;
60}
61
62/*
63 * Set a transmit queue inactive
64 */
65void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue)
66{
67 if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num))
68 return;
69
70 /* This queue will be skipped in further operations */
71 ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE;
72 /*For SIMR setup*/
73 AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +030074}
75
76/*
Bruno Randolfde8af452010-09-17 11:37:12 +090077 * Make sure cw is a power of 2 minus 1 and smaller than 1024
78 */
79static u16 ath5k_cw_validate(u16 cw_req)
80{
81 u32 cw = 1;
82 cw_req = min(cw_req, (u16)1023);
83
84 while (cw < cw_req)
85 cw = (cw << 1) | 1;
86
87 return cw;
88}
89
90/*
Nick Kossifidis9320b5c42010-11-23 20:36:45 +020091 * Get properties for a transmit queue
92 */
93int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue,
94 struct ath5k_txq_info *queue_info)
95{
96 memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info));
97 return 0;
98}
99
100/*
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300101 * Set properties for a transmit queue
102 */
103int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue,
Bruno Randolfde8af452010-09-17 11:37:12 +0900104 const struct ath5k_txq_info *qinfo)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300105{
Bruno Randolfde8af452010-09-17 11:37:12 +0900106 struct ath5k_txq_info *qi;
107
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300108 AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
109
Bruno Randolfde8af452010-09-17 11:37:12 +0900110 qi = &ah->ah_txq[queue];
111
112 if (qi->tqi_type == AR5K_TX_QUEUE_INACTIVE)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300113 return -EIO;
114
Bruno Randolfde8af452010-09-17 11:37:12 +0900115 /* copy and validate values */
116 qi->tqi_type = qinfo->tqi_type;
117 qi->tqi_subtype = qinfo->tqi_subtype;
118 qi->tqi_flags = qinfo->tqi_flags;
119 /*
120 * According to the docs: Although the AIFS field is 8 bit wide,
121 * the maximum supported value is 0xFC. Setting it higher than that
122 * will cause the DCU to hang.
123 */
124 qi->tqi_aifs = min(qinfo->tqi_aifs, (u8)0xFC);
125 qi->tqi_cw_min = ath5k_cw_validate(qinfo->tqi_cw_min);
126 qi->tqi_cw_max = ath5k_cw_validate(qinfo->tqi_cw_max);
127 qi->tqi_cbr_period = qinfo->tqi_cbr_period;
128 qi->tqi_cbr_overflow_limit = qinfo->tqi_cbr_overflow_limit;
129 qi->tqi_burst_time = qinfo->tqi_burst_time;
130 qi->tqi_ready_time = qinfo->tqi_ready_time;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300131
132 /*XXX: Is this supported on 5210 ?*/
Bruno Randolfde8af452010-09-17 11:37:12 +0900133 /*XXX: Is this correct for AR5K_WME_AC_VI,VO ???*/
134 if ((qinfo->tqi_type == AR5K_TX_QUEUE_DATA &&
135 ((qinfo->tqi_subtype == AR5K_WME_AC_VI) ||
136 (qinfo->tqi_subtype == AR5K_WME_AC_VO))) ||
137 qinfo->tqi_type == AR5K_TX_QUEUE_UAPSD)
138 qi->tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300139
140 return 0;
141}
142
143/*
144 * Initialize a transmit queue
145 */
146int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type,
147 struct ath5k_txq_info *queue_info)
148{
149 unsigned int queue;
150 int ret;
151
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300152 /*
153 * Get queue by type
154 */
155 /*5210 only has 2 queues*/
156 if (ah->ah_version == AR5K_AR5210) {
157 switch (queue_type) {
158 case AR5K_TX_QUEUE_DATA:
159 queue = AR5K_TX_QUEUE_ID_NOQCU_DATA;
160 break;
161 case AR5K_TX_QUEUE_BEACON:
162 case AR5K_TX_QUEUE_CAB:
163 queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON;
164 break;
165 default:
166 return -EINVAL;
167 }
168 } else {
169 switch (queue_type) {
170 case AR5K_TX_QUEUE_DATA:
171 for (queue = AR5K_TX_QUEUE_ID_DATA_MIN;
172 ah->ah_txq[queue].tqi_type !=
173 AR5K_TX_QUEUE_INACTIVE; queue++) {
174
175 if (queue > AR5K_TX_QUEUE_ID_DATA_MAX)
176 return -EINVAL;
177 }
178 break;
179 case AR5K_TX_QUEUE_UAPSD:
180 queue = AR5K_TX_QUEUE_ID_UAPSD;
181 break;
182 case AR5K_TX_QUEUE_BEACON:
183 queue = AR5K_TX_QUEUE_ID_BEACON;
184 break;
185 case AR5K_TX_QUEUE_CAB:
186 queue = AR5K_TX_QUEUE_ID_CAB;
187 break;
188 case AR5K_TX_QUEUE_XR_DATA:
189 if (ah->ah_version != AR5K_AR5212)
190 ATH5K_ERR(ah->ah_sc,
191 "XR data queues only supported in"
192 " 5212!\n");
193 queue = AR5K_TX_QUEUE_ID_XR_DATA;
194 break;
195 default:
196 return -EINVAL;
197 }
198 }
199
200 /*
201 * Setup internal queue structure
202 */
203 memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info));
204 ah->ah_txq[queue].tqi_type = queue_type;
205
206 if (queue_info != NULL) {
207 queue_info->tqi_type = queue_type;
208 ret = ath5k_hw_set_tx_queueprops(ah, queue, queue_info);
209 if (ret)
210 return ret;
211 }
212
213 /*
214 * We use ah_txq_status to hold a temp value for
215 * the Secondary interrupt mask registers on 5211+
216 * check out ath5k_hw_reset_tx_queue
217 */
218 AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue);
219
220 return queue;
221}
222
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300223
Nick Kossifidis9320b5c42010-11-23 20:36:45 +0200224/*******************************\
225* Single QCU/DCU initialization *
226\*******************************/
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300227
228/*
Nick Kossifidis25ddfa12010-11-23 21:07:04 +0200229 * Set tx retry limits on DCU
230 */
231static void ath5k_hw_set_tx_retry_limits(struct ath5k_hw *ah,
232 unsigned int queue)
233{
234 u32 retry_lg, retry_sh;
235
236 /*
237 * Calculate and set retry limits
238 */
239 if (ah->ah_software_retry) {
240 /* XXX Need to test this */
241 retry_lg = ah->ah_limit_tx_retries;
242 retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ?
243 AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg;
244 } else {
245 retry_lg = AR5K_INIT_LG_RETRY;
246 retry_sh = AR5K_INIT_SH_RETRY;
247 }
248
249 /* Single data queue on AR5210 */
250 if (ah->ah_version == AR5K_AR5210) {
251 struct ath5k_txq_info *tq = &ah->ah_txq[queue];
252
253 if (queue > 0)
254 return;
255
256 ath5k_hw_reg_write(ah,
257 (tq->tqi_cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S)
258 | AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
259 AR5K_NODCU_RETRY_LMT_SLG_RETRY)
260 | AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
261 AR5K_NODCU_RETRY_LMT_SSH_RETRY)
262 | AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY)
263 | AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY),
264 AR5K_NODCU_RETRY_LMT);
265 /* DCU on AR5211+ */
266 } else {
267 ath5k_hw_reg_write(ah,
268 AR5K_REG_SM(AR5K_INIT_SLG_RETRY,
269 AR5K_DCU_RETRY_LMT_SLG_RETRY) |
270 AR5K_REG_SM(AR5K_INIT_SSH_RETRY,
271 AR5K_DCU_RETRY_LMT_SSH_RETRY) |
272 AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) |
273 AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY),
274 AR5K_QUEUE_DFS_RETRY_LIMIT(queue));
275 }
276 return;
277}
278
279/*
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300280 * Set DFS properties for a transmit queue on DCU
281 */
282int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue)
283{
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300284 struct ath5k_txq_info *tq = &ah->ah_txq[queue];
285
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300286 AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num);
287
288 tq = &ah->ah_txq[queue];
289
290 if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE)
291 return 0;
292
293 if (ah->ah_version == AR5K_AR5210) {
294 /* Only handle data queues, others will be ignored */
295 if (tq->tqi_type != AR5K_TX_QUEUE_DATA)
296 return 0;
297
298 /* Set Slot time */
Nick Kossifidisfa3d2fe2010-11-23 20:58:34 +0200299 ath5k_hw_reg_write(ah, (ah->ah_bwmode == AR5K_BWMODE_40MHZ) ?
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300300 AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME,
301 AR5K_SLOT_TIME);
302 /* Set ACK_CTS timeout */
Nick Kossifidisfa3d2fe2010-11-23 20:58:34 +0200303 ath5k_hw_reg_write(ah, (ah->ah_bwmode == AR5K_BWMODE_40MHZ) ?
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300304 AR5K_INIT_ACK_CTS_TIMEOUT_TURBO :
305 AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300306
307 /* Set IFS0 */
Nick Kossifidisfa3d2fe2010-11-23 20:58:34 +0200308 if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) {
309 ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO +
Bruno Randolfde8af452010-09-17 11:37:12 +0900310 tq->tqi_aifs * AR5K_INIT_SLOT_TIME_TURBO) <<
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300311 AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO,
312 AR5K_IFS0);
313 } else {
314 ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS +
Bruno Randolfde8af452010-09-17 11:37:12 +0900315 tq->tqi_aifs * AR5K_INIT_SLOT_TIME) <<
316 AR5K_IFS0_DIFS_S) |
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300317 AR5K_INIT_SIFS, AR5K_IFS0);
318 }
319
320 /* Set IFS1 */
Nick Kossifidisfa3d2fe2010-11-23 20:58:34 +0200321 ath5k_hw_reg_write(ah, (ah->ah_bwmode == AR5K_BWMODE_40MHZ) ?
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300322 AR5K_INIT_PROTO_TIME_CNTRL_TURBO :
323 AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300324 } else {
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300325
326 /*===Rest is also for QCU/DCU only [5211+]===*/
327
328 /*
Bruno Randolfde8af452010-09-17 11:37:12 +0900329 * Set contention window (cw_min/cw_max)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300330 * and arbitrated interframe space (aifs)...
331 */
332 ath5k_hw_reg_write(ah,
Bruno Randolfde8af452010-09-17 11:37:12 +0900333 AR5K_REG_SM(tq->tqi_cw_min, AR5K_DCU_LCL_IFS_CW_MIN) |
334 AR5K_REG_SM(tq->tqi_cw_max, AR5K_DCU_LCL_IFS_CW_MAX) |
335 AR5K_REG_SM(tq->tqi_aifs, AR5K_DCU_LCL_IFS_AIFS),
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300336 AR5K_QUEUE_DFS_LOCAL_IFS(queue));
337
338 /*
Nick Kossifidis25ddfa12010-11-23 21:07:04 +0200339 * Set tx retry limits for this queue
340 */
341 ath5k_hw_set_tx_retry_limits(ah, queue);
342
343 /*
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300344 * Set misc registers
345 */
Nick Kossifidis846567622009-01-06 17:27:06 +0200346
347 /* Enable DCU to wait for next fragment from QCU */
348 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
349 AR5K_DCU_MISC_FRAG_WAIT);
350
351 /* On Maui and Spirit use the global seqnum on DCU */
352 if (ah->ah_mac_version < AR5K_SREV_AR5211)
353 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
354 AR5K_DCU_MISC_SEQNUM_CTL);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300355
356 if (tq->tqi_cbr_period) {
357 ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period,
358 AR5K_QCU_CBRCFG_INTVAL) |
359 AR5K_REG_SM(tq->tqi_cbr_overflow_limit,
360 AR5K_QCU_CBRCFG_ORN_THRES),
361 AR5K_QUEUE_CBRCFG(queue));
362 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
363 AR5K_QCU_MISC_FRSHED_CBR);
364 if (tq->tqi_cbr_overflow_limit)
365 AR5K_REG_ENABLE_BITS(ah,
366 AR5K_QUEUE_MISC(queue),
367 AR5K_QCU_MISC_CBR_THRES_ENABLE);
368 }
369
Nick Kossifidis846567622009-01-06 17:27:06 +0200370 if (tq->tqi_ready_time &&
Julia Lawall4d30d302009-08-08 15:22:26 +0200371 (tq->tqi_type != AR5K_TX_QUEUE_CAB))
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300372 ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time,
373 AR5K_QCU_RDYTIMECFG_INTVAL) |
374 AR5K_QCU_RDYTIMECFG_ENABLE,
375 AR5K_QUEUE_RDYTIMECFG(queue));
376
377 if (tq->tqi_burst_time) {
378 ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time,
379 AR5K_DCU_CHAN_TIME_DUR) |
380 AR5K_DCU_CHAN_TIME_ENABLE,
381 AR5K_QUEUE_DFS_CHANNEL_TIME(queue));
382
383 if (tq->tqi_flags
384 & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE)
385 AR5K_REG_ENABLE_BITS(ah,
386 AR5K_QUEUE_MISC(queue),
387 AR5K_QCU_MISC_RDY_VEOL_POLICY);
388 }
389
390 if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE)
391 ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS,
392 AR5K_QUEUE_DFS_MISC(queue));
393
394 if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE)
395 ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG,
396 AR5K_QUEUE_DFS_MISC(queue));
397
398 /*
399 * Set registers by queue type
400 */
401 switch (tq->tqi_type) {
402 case AR5K_TX_QUEUE_BEACON:
403 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
404 AR5K_QCU_MISC_FRSHED_DBA_GT |
Nick Kossifidis1bef0162008-09-29 02:09:09 +0300405 AR5K_QCU_MISC_CBREXP_BCN_DIS |
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300406 AR5K_QCU_MISC_BCN_ENABLE);
407
408 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
409 (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
410 AR5K_DCU_MISC_ARBLOCK_CTL_S) |
Nick Kossifidis428cbd42009-04-30 15:55:47 -0400411 AR5K_DCU_MISC_ARBLOCK_IGNORE |
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300412 AR5K_DCU_MISC_POST_FR_BKOFF_DIS |
413 AR5K_DCU_MISC_BCN_ENABLE);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300414 break;
415
416 case AR5K_TX_QUEUE_CAB:
Bob Copelanda951ae22010-01-20 23:51:04 -0500417 /* XXX: use BCN_SENT_GT, if we can figure out how */
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300418 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
Bob Copelanda951ae22010-01-20 23:51:04 -0500419 AR5K_QCU_MISC_FRSHED_DBA_GT |
Nick Kossifidis1bef0162008-09-29 02:09:09 +0300420 AR5K_QCU_MISC_CBREXP_DIS |
421 AR5K_QCU_MISC_CBREXP_BCN_DIS);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300422
Bob Copelanda951ae22010-01-20 23:51:04 -0500423 ath5k_hw_reg_write(ah, ((tq->tqi_ready_time -
Nick Kossifidis846567622009-01-06 17:27:06 +0200424 (AR5K_TUNE_SW_BEACON_RESP -
425 AR5K_TUNE_DMA_BEACON_RESP) -
426 AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) |
427 AR5K_QCU_RDYTIMECFG_ENABLE,
428 AR5K_QUEUE_RDYTIMECFG(queue));
429
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300430 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue),
431 (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL <<
432 AR5K_DCU_MISC_ARBLOCK_CTL_S));
433 break;
434
435 case AR5K_TX_QUEUE_UAPSD:
436 AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue),
Nick Kossifidis1bef0162008-09-29 02:09:09 +0300437 AR5K_QCU_MISC_CBREXP_DIS);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300438 break;
439
440 case AR5K_TX_QUEUE_DATA:
441 default:
442 break;
443 }
444
Nick Kossifidis846567622009-01-06 17:27:06 +0200445 /* TODO: Handle frame compression */
446
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300447 /*
448 * Enable interrupts for this tx queue
449 * in the secondary interrupt mask registers
450 */
451 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE)
452 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue);
453
454 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE)
455 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue);
456
457 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE)
458 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue);
459
460 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE)
461 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue);
462
463 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE)
464 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue);
465
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200466 if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRORNINT_ENABLE)
467 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrorn, queue);
468
469 if (tq->tqi_flags & AR5K_TXQ_FLAG_CBRURNINT_ENABLE)
470 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_cbrurn, queue);
471
472 if (tq->tqi_flags & AR5K_TXQ_FLAG_QTRIGINT_ENABLE)
473 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_qtrig, queue);
474
475 if (tq->tqi_flags & AR5K_TXQ_FLAG_TXNOFRMINT_ENABLE)
476 AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_nofrm, queue);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300477
478 /* Update secondary interrupt mask registers */
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200479
480 /* Filter out inactive queues */
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300481 ah->ah_txq_imr_txok &= ah->ah_txq_status;
482 ah->ah_txq_imr_txerr &= ah->ah_txq_status;
483 ah->ah_txq_imr_txurn &= ah->ah_txq_status;
484 ah->ah_txq_imr_txdesc &= ah->ah_txq_status;
485 ah->ah_txq_imr_txeol &= ah->ah_txq_status;
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200486 ah->ah_txq_imr_cbrorn &= ah->ah_txq_status;
487 ah->ah_txq_imr_cbrurn &= ah->ah_txq_status;
488 ah->ah_txq_imr_qtrig &= ah->ah_txq_status;
489 ah->ah_txq_imr_nofrm &= ah->ah_txq_status;
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300490
491 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok,
492 AR5K_SIMR0_QCU_TXOK) |
493 AR5K_REG_SM(ah->ah_txq_imr_txdesc,
494 AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0);
495 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr,
496 AR5K_SIMR1_QCU_TXERR) |
497 AR5K_REG_SM(ah->ah_txq_imr_txeol,
498 AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1);
Nick Kossifidis4c674c62008-10-26 20:40:25 +0200499 /* Update simr2 but don't overwrite rest simr2 settings */
500 AR5K_REG_DISABLE_BITS(ah, AR5K_SIMR2, AR5K_SIMR2_QCU_TXURN);
501 AR5K_REG_ENABLE_BITS(ah, AR5K_SIMR2,
502 AR5K_REG_SM(ah->ah_txq_imr_txurn,
503 AR5K_SIMR2_QCU_TXURN));
504 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_cbrorn,
505 AR5K_SIMR3_QCBRORN) |
506 AR5K_REG_SM(ah->ah_txq_imr_cbrurn,
507 AR5K_SIMR3_QCBRURN), AR5K_SIMR3);
508 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_qtrig,
509 AR5K_SIMR4_QTRIG), AR5K_SIMR4);
510 /* Set TXNOFRM_QCU for the queues with TXNOFRM enabled */
511 ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_nofrm,
512 AR5K_TXNOFRM_QCU), AR5K_TXNOFRM);
513 /* No queue has TXNOFRM enabled, disable the interrupt
514 * by setting AR5K_TXNOFRM to zero */
515 if (ah->ah_txq_imr_nofrm == 0)
516 ath5k_hw_reg_write(ah, 0, AR5K_TXNOFRM);
Nick Kossifidis846567622009-01-06 17:27:06 +0200517
518 /* Set QCU mask for this DCU to save power */
519 AR5K_REG_WRITE_Q(ah, AR5K_QUEUE_QCUMASK(queue), queue);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300520 }
521
522 return 0;
523}
524
Nick Kossifidis9320b5c42010-11-23 20:36:45 +0200525
526/**************************\
527* Global QCU/DCU functions *
528\**************************/
529
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300530/*
531 * Set slot time on DCU
532 */
533int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time)
534{
Lukáš Turek3578e6e2009-12-21 22:50:50 +0100535 u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time);
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100536
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100537 if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX)
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300538 return -EINVAL;
539
540 if (ah->ah_version == AR5K_AR5210)
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100541 ath5k_hw_reg_write(ah, slot_time_clock, AR5K_SLOT_TIME);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300542 else
Lukáš Tureke1aa3692009-12-21 22:50:49 +0100543 ath5k_hw_reg_write(ah, slot_time_clock, AR5K_DCU_GBL_IFS_SLOT);
Nick Kossifidisc6e387a2008-08-29 22:45:39 +0300544
545 return 0;
546}
547
Nick Kossifidis9320b5c42010-11-23 20:36:45 +0200548int ath5k_hw_init_queues(struct ath5k_hw *ah)
549{
550 int i, ret;
551
552 /* TODO: HW Compression support for data queues */
553 /* TODO: Burst prefetch for data queues */
554
555 /*
556 * Reset queues and start beacon timers at the end of the reset routine
557 * This also sets QCU mask on each DCU for 1:1 qcu to dcu mapping
558 * Note: If we want we can assign multiple qcus on one dcu.
559 */
560 for (i = 0; i < ah->ah_capabilities.cap_queues.q_tx_num; i++) {
561 ret = ath5k_hw_reset_tx_queue(ah, i);
562 if (ret) {
563 ATH5K_ERR(ah->ah_sc,
564 "failed to reset TX queue #%d\n", i);
565 return ret;
566 }
567 }
568
569 return 0;
570}