diff options
Diffstat (limited to 'drivers/net/wireless/ath5k/qcu.c')
-rw-r--r-- | drivers/net/wireless/ath5k/qcu.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath5k/qcu.c b/drivers/net/wireless/ath5k/qcu.c new file mode 100644 index 000000000000..01bf09176d23 --- /dev/null +++ b/drivers/net/wireless/ath5k/qcu.c | |||
@@ -0,0 +1,488 @@ | |||
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, 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 | */ | ||
31 | int ath5k_hw_get_tx_queueprops(struct ath5k_hw *ah, int queue, | ||
32 | struct ath5k_txq_info *queue_info) | ||
33 | { | ||
34 | ATH5K_TRACE(ah->ah_sc); | ||
35 | memcpy(queue_info, &ah->ah_txq[queue], sizeof(struct ath5k_txq_info)); | ||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | /* | ||
40 | * Set properties for a transmit queue | ||
41 | */ | ||
42 | int ath5k_hw_set_tx_queueprops(struct ath5k_hw *ah, int queue, | ||
43 | const struct ath5k_txq_info *queue_info) | ||
44 | { | ||
45 | ATH5K_TRACE(ah->ah_sc); | ||
46 | AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); | ||
47 | |||
48 | if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) | ||
49 | return -EIO; | ||
50 | |||
51 | memcpy(&ah->ah_txq[queue], queue_info, sizeof(struct ath5k_txq_info)); | ||
52 | |||
53 | /*XXX: Is this supported on 5210 ?*/ | ||
54 | if ((queue_info->tqi_type == AR5K_TX_QUEUE_DATA && | ||
55 | ((queue_info->tqi_subtype == AR5K_WME_AC_VI) || | ||
56 | (queue_info->tqi_subtype == AR5K_WME_AC_VO))) || | ||
57 | queue_info->tqi_type == AR5K_TX_QUEUE_UAPSD) | ||
58 | ah->ah_txq[queue].tqi_flags |= AR5K_TXQ_FLAG_POST_FR_BKOFF_DIS; | ||
59 | |||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | /* | ||
64 | * Initialize a transmit queue | ||
65 | */ | ||
66 | int ath5k_hw_setup_tx_queue(struct ath5k_hw *ah, enum ath5k_tx_queue queue_type, | ||
67 | struct ath5k_txq_info *queue_info) | ||
68 | { | ||
69 | unsigned int queue; | ||
70 | int ret; | ||
71 | |||
72 | ATH5K_TRACE(ah->ah_sc); | ||
73 | |||
74 | /* | ||
75 | * Get queue by type | ||
76 | */ | ||
77 | /*5210 only has 2 queues*/ | ||
78 | if (ah->ah_version == AR5K_AR5210) { | ||
79 | switch (queue_type) { | ||
80 | case AR5K_TX_QUEUE_DATA: | ||
81 | queue = AR5K_TX_QUEUE_ID_NOQCU_DATA; | ||
82 | break; | ||
83 | case AR5K_TX_QUEUE_BEACON: | ||
84 | case AR5K_TX_QUEUE_CAB: | ||
85 | queue = AR5K_TX_QUEUE_ID_NOQCU_BEACON; | ||
86 | break; | ||
87 | default: | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | } else { | ||
91 | switch (queue_type) { | ||
92 | case AR5K_TX_QUEUE_DATA: | ||
93 | for (queue = AR5K_TX_QUEUE_ID_DATA_MIN; | ||
94 | ah->ah_txq[queue].tqi_type != | ||
95 | AR5K_TX_QUEUE_INACTIVE; queue++) { | ||
96 | |||
97 | if (queue > AR5K_TX_QUEUE_ID_DATA_MAX) | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | break; | ||
101 | case AR5K_TX_QUEUE_UAPSD: | ||
102 | queue = AR5K_TX_QUEUE_ID_UAPSD; | ||
103 | break; | ||
104 | case AR5K_TX_QUEUE_BEACON: | ||
105 | queue = AR5K_TX_QUEUE_ID_BEACON; | ||
106 | break; | ||
107 | case AR5K_TX_QUEUE_CAB: | ||
108 | queue = AR5K_TX_QUEUE_ID_CAB; | ||
109 | break; | ||
110 | case AR5K_TX_QUEUE_XR_DATA: | ||
111 | if (ah->ah_version != AR5K_AR5212) | ||
112 | ATH5K_ERR(ah->ah_sc, | ||
113 | "XR data queues only supported in" | ||
114 | " 5212!\n"); | ||
115 | queue = AR5K_TX_QUEUE_ID_XR_DATA; | ||
116 | break; | ||
117 | default: | ||
118 | return -EINVAL; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * Setup internal queue structure | ||
124 | */ | ||
125 | memset(&ah->ah_txq[queue], 0, sizeof(struct ath5k_txq_info)); | ||
126 | ah->ah_txq[queue].tqi_type = queue_type; | ||
127 | |||
128 | if (queue_info != NULL) { | ||
129 | queue_info->tqi_type = queue_type; | ||
130 | ret = ath5k_hw_set_tx_queueprops(ah, queue, queue_info); | ||
131 | if (ret) | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * We use ah_txq_status to hold a temp value for | ||
137 | * the Secondary interrupt mask registers on 5211+ | ||
138 | * check out ath5k_hw_reset_tx_queue | ||
139 | */ | ||
140 | AR5K_Q_ENABLE_BITS(ah->ah_txq_status, queue); | ||
141 | |||
142 | return queue; | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Get number of pending frames | ||
147 | * for a specific queue [5211+] | ||
148 | */ | ||
149 | u32 ath5k_hw_num_tx_pending(struct ath5k_hw *ah, unsigned int queue) | ||
150 | { | ||
151 | ATH5K_TRACE(ah->ah_sc); | ||
152 | AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); | ||
153 | |||
154 | /* Return if queue is declared inactive */ | ||
155 | if (ah->ah_txq[queue].tqi_type == AR5K_TX_QUEUE_INACTIVE) | ||
156 | return false; | ||
157 | |||
158 | /* XXX: How about AR5K_CFG_TXCNT ? */ | ||
159 | if (ah->ah_version == AR5K_AR5210) | ||
160 | return false; | ||
161 | |||
162 | return AR5K_QUEUE_STATUS(queue) & AR5K_QCU_STS_FRMPENDCNT; | ||
163 | } | ||
164 | |||
165 | /* | ||
166 | * Set a transmit queue inactive | ||
167 | */ | ||
168 | void ath5k_hw_release_tx_queue(struct ath5k_hw *ah, unsigned int queue) | ||
169 | { | ||
170 | ATH5K_TRACE(ah->ah_sc); | ||
171 | if (WARN_ON(queue >= ah->ah_capabilities.cap_queues.q_tx_num)) | ||
172 | return; | ||
173 | |||
174 | /* This queue will be skipped in further operations */ | ||
175 | ah->ah_txq[queue].tqi_type = AR5K_TX_QUEUE_INACTIVE; | ||
176 | /*For SIMR setup*/ | ||
177 | AR5K_Q_DISABLE_BITS(ah->ah_txq_status, queue); | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * Set DFS properties for a transmit queue on DCU | ||
182 | */ | ||
183 | int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) | ||
184 | { | ||
185 | u32 cw_min, cw_max, retry_lg, retry_sh; | ||
186 | struct ath5k_txq_info *tq = &ah->ah_txq[queue]; | ||
187 | |||
188 | ATH5K_TRACE(ah->ah_sc); | ||
189 | AR5K_ASSERT_ENTRY(queue, ah->ah_capabilities.cap_queues.q_tx_num); | ||
190 | |||
191 | tq = &ah->ah_txq[queue]; | ||
192 | |||
193 | if (tq->tqi_type == AR5K_TX_QUEUE_INACTIVE) | ||
194 | return 0; | ||
195 | |||
196 | if (ah->ah_version == AR5K_AR5210) { | ||
197 | /* Only handle data queues, others will be ignored */ | ||
198 | if (tq->tqi_type != AR5K_TX_QUEUE_DATA) | ||
199 | return 0; | ||
200 | |||
201 | /* Set Slot time */ | ||
202 | ath5k_hw_reg_write(ah, ah->ah_turbo ? | ||
203 | AR5K_INIT_SLOT_TIME_TURBO : AR5K_INIT_SLOT_TIME, | ||
204 | AR5K_SLOT_TIME); | ||
205 | /* Set ACK_CTS timeout */ | ||
206 | ath5k_hw_reg_write(ah, ah->ah_turbo ? | ||
207 | AR5K_INIT_ACK_CTS_TIMEOUT_TURBO : | ||
208 | AR5K_INIT_ACK_CTS_TIMEOUT, AR5K_SLOT_TIME); | ||
209 | /* Set Transmit Latency */ | ||
210 | ath5k_hw_reg_write(ah, ah->ah_turbo ? | ||
211 | AR5K_INIT_TRANSMIT_LATENCY_TURBO : | ||
212 | AR5K_INIT_TRANSMIT_LATENCY, AR5K_USEC_5210); | ||
213 | |||
214 | /* Set IFS0 */ | ||
215 | if (ah->ah_turbo) { | ||
216 | ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS_TURBO + | ||
217 | (ah->ah_aifs + tq->tqi_aifs) * | ||
218 | AR5K_INIT_SLOT_TIME_TURBO) << | ||
219 | AR5K_IFS0_DIFS_S) | AR5K_INIT_SIFS_TURBO, | ||
220 | AR5K_IFS0); | ||
221 | } else { | ||
222 | ath5k_hw_reg_write(ah, ((AR5K_INIT_SIFS + | ||
223 | (ah->ah_aifs + tq->tqi_aifs) * | ||
224 | AR5K_INIT_SLOT_TIME) << AR5K_IFS0_DIFS_S) | | ||
225 | AR5K_INIT_SIFS, AR5K_IFS0); | ||
226 | } | ||
227 | |||
228 | /* Set IFS1 */ | ||
229 | ath5k_hw_reg_write(ah, ah->ah_turbo ? | ||
230 | AR5K_INIT_PROTO_TIME_CNTRL_TURBO : | ||
231 | AR5K_INIT_PROTO_TIME_CNTRL, AR5K_IFS1); | ||
232 | /* Set AR5K_PHY_SETTLING */ | ||
233 | ath5k_hw_reg_write(ah, ah->ah_turbo ? | ||
234 | (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F) | ||
235 | | 0x38 : | ||
236 | (ath5k_hw_reg_read(ah, AR5K_PHY_SETTLING) & ~0x7F) | ||
237 | | 0x1C, | ||
238 | AR5K_PHY_SETTLING); | ||
239 | /* Set Frame Control Register */ | ||
240 | ath5k_hw_reg_write(ah, ah->ah_turbo ? | ||
241 | (AR5K_PHY_FRAME_CTL_INI | AR5K_PHY_TURBO_MODE | | ||
242 | AR5K_PHY_TURBO_SHORT | 0x2020) : | ||
243 | (AR5K_PHY_FRAME_CTL_INI | 0x1020), | ||
244 | AR5K_PHY_FRAME_CTL_5210); | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Calculate cwmin/max by channel mode | ||
249 | */ | ||
250 | cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN; | ||
251 | cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX; | ||
252 | ah->ah_aifs = AR5K_TUNE_AIFS; | ||
253 | /*XR is only supported on 5212*/ | ||
254 | if (IS_CHAN_XR(ah->ah_current_channel) && | ||
255 | ah->ah_version == AR5K_AR5212) { | ||
256 | cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_XR; | ||
257 | cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_XR; | ||
258 | ah->ah_aifs = AR5K_TUNE_AIFS_XR; | ||
259 | /*B mode is not supported on 5210*/ | ||
260 | } else if (IS_CHAN_B(ah->ah_current_channel) && | ||
261 | ah->ah_version != AR5K_AR5210) { | ||
262 | cw_min = ah->ah_cw_min = AR5K_TUNE_CWMIN_11B; | ||
263 | cw_max = ah->ah_cw_max = AR5K_TUNE_CWMAX_11B; | ||
264 | ah->ah_aifs = AR5K_TUNE_AIFS_11B; | ||
265 | } | ||
266 | |||
267 | cw_min = 1; | ||
268 | while (cw_min < ah->ah_cw_min) | ||
269 | cw_min = (cw_min << 1) | 1; | ||
270 | |||
271 | cw_min = tq->tqi_cw_min < 0 ? (cw_min >> (-tq->tqi_cw_min)) : | ||
272 | ((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1); | ||
273 | cw_max = tq->tqi_cw_max < 0 ? (cw_max >> (-tq->tqi_cw_max)) : | ||
274 | ((cw_max << tq->tqi_cw_max) + (1 << tq->tqi_cw_max) - 1); | ||
275 | |||
276 | /* | ||
277 | * Calculate and set retry limits | ||
278 | */ | ||
279 | if (ah->ah_software_retry) { | ||
280 | /* XXX Need to test this */ | ||
281 | retry_lg = ah->ah_limit_tx_retries; | ||
282 | retry_sh = retry_lg = retry_lg > AR5K_DCU_RETRY_LMT_SH_RETRY ? | ||
283 | AR5K_DCU_RETRY_LMT_SH_RETRY : retry_lg; | ||
284 | } else { | ||
285 | retry_lg = AR5K_INIT_LG_RETRY; | ||
286 | retry_sh = AR5K_INIT_SH_RETRY; | ||
287 | } | ||
288 | |||
289 | /*No QCU/DCU [5210]*/ | ||
290 | if (ah->ah_version == AR5K_AR5210) { | ||
291 | ath5k_hw_reg_write(ah, | ||
292 | (cw_min << AR5K_NODCU_RETRY_LMT_CW_MIN_S) | ||
293 | | AR5K_REG_SM(AR5K_INIT_SLG_RETRY, | ||
294 | AR5K_NODCU_RETRY_LMT_SLG_RETRY) | ||
295 | | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, | ||
296 | AR5K_NODCU_RETRY_LMT_SSH_RETRY) | ||
297 | | AR5K_REG_SM(retry_lg, AR5K_NODCU_RETRY_LMT_LG_RETRY) | ||
298 | | AR5K_REG_SM(retry_sh, AR5K_NODCU_RETRY_LMT_SH_RETRY), | ||
299 | AR5K_NODCU_RETRY_LMT); | ||
300 | } else { | ||
301 | /*QCU/DCU [5211+]*/ | ||
302 | ath5k_hw_reg_write(ah, | ||
303 | AR5K_REG_SM(AR5K_INIT_SLG_RETRY, | ||
304 | AR5K_DCU_RETRY_LMT_SLG_RETRY) | | ||
305 | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, | ||
306 | AR5K_DCU_RETRY_LMT_SSH_RETRY) | | ||
307 | AR5K_REG_SM(retry_lg, AR5K_DCU_RETRY_LMT_LG_RETRY) | | ||
308 | AR5K_REG_SM(retry_sh, AR5K_DCU_RETRY_LMT_SH_RETRY), | ||
309 | AR5K_QUEUE_DFS_RETRY_LIMIT(queue)); | ||
310 | |||
311 | /*===Rest is also for QCU/DCU only [5211+]===*/ | ||
312 | |||
313 | /* | ||
314 | * Set initial content window (cw_min/cw_max) | ||
315 | * and arbitrated interframe space (aifs)... | ||
316 | */ | ||
317 | ath5k_hw_reg_write(ah, | ||
318 | AR5K_REG_SM(cw_min, AR5K_DCU_LCL_IFS_CW_MIN) | | ||
319 | AR5K_REG_SM(cw_max, AR5K_DCU_LCL_IFS_CW_MAX) | | ||
320 | AR5K_REG_SM(ah->ah_aifs + tq->tqi_aifs, | ||
321 | AR5K_DCU_LCL_IFS_AIFS), | ||
322 | AR5K_QUEUE_DFS_LOCAL_IFS(queue)); | ||
323 | |||
324 | /* | ||
325 | * Set misc registers | ||
326 | */ | ||
327 | ath5k_hw_reg_write(ah, AR5K_QCU_MISC_DCU_EARLY, | ||
328 | AR5K_QUEUE_MISC(queue)); | ||
329 | |||
330 | if (tq->tqi_cbr_period) { | ||
331 | ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_cbr_period, | ||
332 | AR5K_QCU_CBRCFG_INTVAL) | | ||
333 | AR5K_REG_SM(tq->tqi_cbr_overflow_limit, | ||
334 | AR5K_QCU_CBRCFG_ORN_THRES), | ||
335 | AR5K_QUEUE_CBRCFG(queue)); | ||
336 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), | ||
337 | AR5K_QCU_MISC_FRSHED_CBR); | ||
338 | if (tq->tqi_cbr_overflow_limit) | ||
339 | AR5K_REG_ENABLE_BITS(ah, | ||
340 | AR5K_QUEUE_MISC(queue), | ||
341 | AR5K_QCU_MISC_CBR_THRES_ENABLE); | ||
342 | } | ||
343 | |||
344 | if (tq->tqi_ready_time) | ||
345 | ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_ready_time, | ||
346 | AR5K_QCU_RDYTIMECFG_INTVAL) | | ||
347 | AR5K_QCU_RDYTIMECFG_ENABLE, | ||
348 | AR5K_QUEUE_RDYTIMECFG(queue)); | ||
349 | |||
350 | if (tq->tqi_burst_time) { | ||
351 | ath5k_hw_reg_write(ah, AR5K_REG_SM(tq->tqi_burst_time, | ||
352 | AR5K_DCU_CHAN_TIME_DUR) | | ||
353 | AR5K_DCU_CHAN_TIME_ENABLE, | ||
354 | AR5K_QUEUE_DFS_CHANNEL_TIME(queue)); | ||
355 | |||
356 | if (tq->tqi_flags | ||
357 | & AR5K_TXQ_FLAG_RDYTIME_EXP_POLICY_ENABLE) | ||
358 | AR5K_REG_ENABLE_BITS(ah, | ||
359 | AR5K_QUEUE_MISC(queue), | ||
360 | AR5K_QCU_MISC_RDY_VEOL_POLICY); | ||
361 | } | ||
362 | |||
363 | if (tq->tqi_flags & AR5K_TXQ_FLAG_BACKOFF_DISABLE) | ||
364 | ath5k_hw_reg_write(ah, AR5K_DCU_MISC_POST_FR_BKOFF_DIS, | ||
365 | AR5K_QUEUE_DFS_MISC(queue)); | ||
366 | |||
367 | if (tq->tqi_flags & AR5K_TXQ_FLAG_FRAG_BURST_BACKOFF_ENABLE) | ||
368 | ath5k_hw_reg_write(ah, AR5K_DCU_MISC_BACKOFF_FRAG, | ||
369 | AR5K_QUEUE_DFS_MISC(queue)); | ||
370 | |||
371 | /* | ||
372 | * Set registers by queue type | ||
373 | */ | ||
374 | switch (tq->tqi_type) { | ||
375 | case AR5K_TX_QUEUE_BEACON: | ||
376 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), | ||
377 | AR5K_QCU_MISC_FRSHED_DBA_GT | | ||
378 | AR5K_QCU_MISC_CBREXP_BCN_DIS | | ||
379 | AR5K_QCU_MISC_BCN_ENABLE); | ||
380 | |||
381 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), | ||
382 | (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << | ||
383 | AR5K_DCU_MISC_ARBLOCK_CTL_S) | | ||
384 | AR5K_DCU_MISC_POST_FR_BKOFF_DIS | | ||
385 | AR5K_DCU_MISC_BCN_ENABLE); | ||
386 | |||
387 | ath5k_hw_reg_write(ah, ((AR5K_TUNE_BEACON_INTERVAL - | ||
388 | (AR5K_TUNE_SW_BEACON_RESP - | ||
389 | AR5K_TUNE_DMA_BEACON_RESP) - | ||
390 | AR5K_TUNE_ADDITIONAL_SWBA_BACKOFF) * 1024) | | ||
391 | AR5K_QCU_RDYTIMECFG_ENABLE, | ||
392 | AR5K_QUEUE_RDYTIMECFG(queue)); | ||
393 | break; | ||
394 | |||
395 | case AR5K_TX_QUEUE_CAB: | ||
396 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), | ||
397 | AR5K_QCU_MISC_FRSHED_DBA_GT | | ||
398 | AR5K_QCU_MISC_CBREXP_DIS | | ||
399 | AR5K_QCU_MISC_CBREXP_BCN_DIS); | ||
400 | |||
401 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_DFS_MISC(queue), | ||
402 | (AR5K_DCU_MISC_ARBLOCK_CTL_GLOBAL << | ||
403 | AR5K_DCU_MISC_ARBLOCK_CTL_S)); | ||
404 | break; | ||
405 | |||
406 | case AR5K_TX_QUEUE_UAPSD: | ||
407 | AR5K_REG_ENABLE_BITS(ah, AR5K_QUEUE_MISC(queue), | ||
408 | AR5K_QCU_MISC_CBREXP_DIS); | ||
409 | break; | ||
410 | |||
411 | case AR5K_TX_QUEUE_DATA: | ||
412 | default: | ||
413 | break; | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * Enable interrupts for this tx queue | ||
418 | * in the secondary interrupt mask registers | ||
419 | */ | ||
420 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXOKINT_ENABLE) | ||
421 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txok, queue); | ||
422 | |||
423 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXERRINT_ENABLE) | ||
424 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txerr, queue); | ||
425 | |||
426 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXURNINT_ENABLE) | ||
427 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txurn, queue); | ||
428 | |||
429 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXDESCINT_ENABLE) | ||
430 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txdesc, queue); | ||
431 | |||
432 | if (tq->tqi_flags & AR5K_TXQ_FLAG_TXEOLINT_ENABLE) | ||
433 | AR5K_Q_ENABLE_BITS(ah->ah_txq_imr_txeol, queue); | ||
434 | |||
435 | |||
436 | /* Update secondary interrupt mask registers */ | ||
437 | ah->ah_txq_imr_txok &= ah->ah_txq_status; | ||
438 | ah->ah_txq_imr_txerr &= ah->ah_txq_status; | ||
439 | ah->ah_txq_imr_txurn &= ah->ah_txq_status; | ||
440 | ah->ah_txq_imr_txdesc &= ah->ah_txq_status; | ||
441 | ah->ah_txq_imr_txeol &= ah->ah_txq_status; | ||
442 | |||
443 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txok, | ||
444 | AR5K_SIMR0_QCU_TXOK) | | ||
445 | AR5K_REG_SM(ah->ah_txq_imr_txdesc, | ||
446 | AR5K_SIMR0_QCU_TXDESC), AR5K_SIMR0); | ||
447 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txerr, | ||
448 | AR5K_SIMR1_QCU_TXERR) | | ||
449 | AR5K_REG_SM(ah->ah_txq_imr_txeol, | ||
450 | AR5K_SIMR1_QCU_TXEOL), AR5K_SIMR1); | ||
451 | ath5k_hw_reg_write(ah, AR5K_REG_SM(ah->ah_txq_imr_txurn, | ||
452 | AR5K_SIMR2_QCU_TXURN), AR5K_SIMR2); | ||
453 | } | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | /* | ||
459 | * Get slot time from DCU | ||
460 | */ | ||
461 | unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah) | ||
462 | { | ||
463 | ATH5K_TRACE(ah->ah_sc); | ||
464 | if (ah->ah_version == AR5K_AR5210) | ||
465 | return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah, | ||
466 | AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo); | ||
467 | else | ||
468 | return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff; | ||
469 | } | ||
470 | |||
471 | /* | ||
472 | * Set slot time on DCU | ||
473 | */ | ||
474 | int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time) | ||
475 | { | ||
476 | ATH5K_TRACE(ah->ah_sc); | ||
477 | if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX) | ||
478 | return -EINVAL; | ||
479 | |||
480 | if (ah->ah_version == AR5K_AR5210) | ||
481 | ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time, | ||
482 | ah->ah_turbo), AR5K_SLOT_TIME); | ||
483 | else | ||
484 | ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT); | ||
485 | |||
486 | return 0; | ||
487 | } | ||
488 | |||