Commit | Line | Data |
---|---|---|
30295c89 VM |
1 | //------------------------------------------------------------------------------ |
2 | // <copyright file="ar6k_events.c" company="Atheros"> | |
3 | // Copyright (c) 2007-2010 Atheros Corporation. All rights reserved. | |
4 | // | |
5 | // | |
6 | // Permission to use, copy, modify, and/or distribute this software for any | |
7 | // purpose with or without fee is hereby granted, provided that the above | |
8 | // copyright notice and this permission notice appear in all copies. | |
9 | // | |
10 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
11 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
12 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
13 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
14 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
15 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
16 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
17 | // | |
18 | // | |
19 | //------------------------------------------------------------------------------ | |
20 | //============================================================================== | |
21 | // AR6K Driver layer event handling (i.e. interrupts, message polling) | |
22 | // | |
23 | // Author(s): ="Atheros" | |
24 | //============================================================================== | |
25 | ||
26 | #include "a_config.h" | |
27 | #include "athdefs.h" | |
28 | #include "a_types.h" | |
29 | #include "AR6002/hw2.0/hw/mbox_host_reg.h" | |
30 | #include "a_osapi.h" | |
31 | #include "../htc_debug.h" | |
32 | #include "hif.h" | |
33 | #include "htc_packet.h" | |
34 | #include "ar6k.h" | |
35 | ||
495abc79 LR |
36 | extern void AR6KFreeIOPacket(struct ar6k_device *pDev, HTC_PACKET *pPacket); |
37 | extern HTC_PACKET *AR6KAllocIOPacket(struct ar6k_device *pDev); | |
30295c89 | 38 | |
495abc79 | 39 | static int DevServiceDebugInterrupt(struct ar6k_device *pDev); |
30295c89 VM |
40 | |
41 | #define DELAY_PER_INTERVAL_MS 10 /* 10 MS delay per polling interval */ | |
42 | ||
43 | /* completion routine for ALL HIF layer async I/O */ | |
1f4c34bd | 44 | int DevRWCompletionHandler(void *context, int status) |
30295c89 VM |
45 | { |
46 | HTC_PACKET *pPacket = (HTC_PACKET *)context; | |
47 | ||
48 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, | |
49 | ("+DevRWCompletionHandler (Pkt:0x%lX) , Status: %d \n", | |
50 | (unsigned long)pPacket, | |
51 | status)); | |
52 | ||
53 | COMPLETE_HTC_PACKET(pPacket,status); | |
54 | ||
55 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV, | |
56 | ("-DevRWCompletionHandler\n")); | |
57 | ||
4f69cef0 | 58 | return 0; |
30295c89 VM |
59 | } |
60 | ||
61 | /* mailbox recv message polling */ | |
495abc79 | 62 | int DevPollMboxMsgRecv(struct ar6k_device *pDev, |
e1ce2a3a | 63 | u32 *pLookAhead, |
30295c89 VM |
64 | int TimeoutMS) |
65 | { | |
4f69cef0 | 66 | int status = 0; |
30295c89 VM |
67 | int timeout = TimeoutMS/DELAY_PER_INTERVAL_MS; |
68 | ||
69 | A_ASSERT(timeout > 0); | |
70 | ||
71 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("+DevPollMboxMsgRecv \n")); | |
72 | ||
1071a134 | 73 | while (true) { |
30295c89 VM |
74 | |
75 | if (pDev->GetPendingEventsFunc != NULL) { | |
76 | ||
77 | HIF_PENDING_EVENTS_INFO events; | |
78 | ||
79 | #ifdef THREAD_X | |
80 | events.Polling =1; | |
81 | #endif | |
82 | ||
83 | /* the HIF layer uses a special mechanism to get events, do this | |
84 | * synchronously */ | |
85 | status = pDev->GetPendingEventsFunc(pDev->HIFDevice, | |
86 | &events, | |
87 | NULL); | |
391bb211 | 88 | if (status) |
30295c89 VM |
89 | { |
90 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to get pending events \n")); | |
91 | break; | |
92 | } | |
93 | ||
94 | if (events.Events & HIF_RECV_MSG_AVAIL) | |
95 | { | |
96 | /* there is a message available, the lookahead should be valid now */ | |
97 | *pLookAhead = events.LookAhead; | |
98 | ||
99 | break; | |
100 | } | |
101 | } else { | |
102 | ||
103 | /* this is the standard HIF way.... */ | |
104 | /* load the register table */ | |
105 | status = HIFReadWrite(pDev->HIFDevice, | |
106 | HOST_INT_STATUS_ADDRESS, | |
ab3655da | 107 | (u8 *)&pDev->IrqProcRegisters, |
30295c89 VM |
108 | AR6K_IRQ_PROC_REGS_SIZE, |
109 | HIF_RD_SYNC_BYTE_INC, | |
110 | NULL); | |
111 | ||
391bb211 | 112 | if (status){ |
30295c89 VM |
113 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("Failed to read register table \n")); |
114 | break; | |
115 | } | |
116 | ||
117 | /* check for MBOX data and valid lookahead */ | |
118 | if (pDev->IrqProcRegisters.host_int_status & (1 << HTC_MAILBOX)) { | |
119 | if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX)) | |
120 | { | |
121 | /* mailbox has a message and the look ahead is valid */ | |
122 | *pLookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX]; | |
123 | break; | |
124 | } | |
125 | } | |
126 | ||
127 | } | |
128 | ||
129 | timeout--; | |
130 | ||
131 | if (timeout <= 0) { | |
132 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, (" Timeout waiting for recv message \n")); | |
133 | status = A_ERROR; | |
134 | ||
135 | /* check if the target asserted */ | |
136 | if ( pDev->IrqProcRegisters.counter_int_status & AR6K_TARGET_DEBUG_INTR_MASK) { | |
137 | /* target signaled an assert, process this pending interrupt | |
138 | * this will call the target failure handler */ | |
139 | DevServiceDebugInterrupt(pDev); | |
140 | } | |
141 | ||
142 | break; | |
143 | } | |
144 | ||
145 | /* delay a little */ | |
146 | A_MDELAY(DELAY_PER_INTERVAL_MS); | |
147 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV,(" Retry Mbox Poll : %d \n",timeout)); | |
148 | } | |
149 | ||
150 | AR_DEBUG_PRINTF(ATH_DEBUG_RECV,("-DevPollMboxMsgRecv \n")); | |
151 | ||
152 | return status; | |
153 | } | |
154 | ||
495abc79 | 155 | static int DevServiceCPUInterrupt(struct ar6k_device *pDev) |
30295c89 | 156 | { |
1f4c34bd | 157 | int status; |
ab3655da JP |
158 | u8 cpu_int_status; |
159 | u8 regBuffer[4]; | |
30295c89 VM |
160 | |
161 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("CPU Interrupt\n")); | |
162 | cpu_int_status = pDev->IrqProcRegisters.cpu_int_status & | |
163 | pDev->IrqEnableRegisters.cpu_int_status_enable; | |
164 | A_ASSERT(cpu_int_status); | |
165 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, | |
166 | ("Valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", | |
167 | cpu_int_status)); | |
168 | ||
169 | /* Clear the interrupt */ | |
170 | pDev->IrqProcRegisters.cpu_int_status &= ~cpu_int_status; /* W1C */ | |
171 | ||
172 | /* set up the register transfer buffer to hit the register 4 times , this is done | |
173 | * to make the access 4-byte aligned to mitigate issues with host bus interconnects that | |
174 | * restrict bus transfer lengths to be a multiple of 4-bytes */ | |
175 | ||
176 | /* set W1C value to clear the interrupt, this hits the register first */ | |
177 | regBuffer[0] = cpu_int_status; | |
178 | /* the remaining 4 values are set to zero which have no-effect */ | |
179 | regBuffer[1] = 0; | |
180 | regBuffer[2] = 0; | |
181 | regBuffer[3] = 0; | |
182 | ||
183 | status = HIFReadWrite(pDev->HIFDevice, | |
184 | CPU_INT_STATUS_ADDRESS, | |
185 | regBuffer, | |
186 | 4, | |
187 | HIF_WR_SYNC_BYTE_FIX, | |
188 | NULL); | |
189 | ||
4f69cef0 | 190 | A_ASSERT(status == 0); |
30295c89 VM |
191 | return status; |
192 | } | |
193 | ||
194 | ||
495abc79 | 195 | static int DevServiceErrorInterrupt(struct ar6k_device *pDev) |
30295c89 | 196 | { |
1f4c34bd | 197 | int status; |
ab3655da JP |
198 | u8 error_int_status; |
199 | u8 regBuffer[4]; | |
30295c89 VM |
200 | |
201 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Error Interrupt\n")); | |
202 | error_int_status = pDev->IrqProcRegisters.error_int_status & 0x0F; | |
203 | A_ASSERT(error_int_status); | |
204 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, | |
205 | ("Valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n", | |
206 | error_int_status)); | |
207 | ||
208 | if (ERROR_INT_STATUS_WAKEUP_GET(error_int_status)) { | |
209 | /* Wakeup */ | |
210 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Error : Wakeup\n")); | |
211 | } | |
212 | ||
213 | if (ERROR_INT_STATUS_RX_UNDERFLOW_GET(error_int_status)) { | |
214 | /* Rx Underflow */ | |
215 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Error : Rx Underflow\n")); | |
216 | } | |
217 | ||
218 | if (ERROR_INT_STATUS_TX_OVERFLOW_GET(error_int_status)) { | |
219 | /* Tx Overflow */ | |
220 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Error : Tx Overflow\n")); | |
221 | } | |
222 | ||
223 | /* Clear the interrupt */ | |
224 | pDev->IrqProcRegisters.error_int_status &= ~error_int_status; /* W1C */ | |
225 | ||
226 | /* set up the register transfer buffer to hit the register 4 times , this is done | |
227 | * to make the access 4-byte aligned to mitigate issues with host bus interconnects that | |
228 | * restrict bus transfer lengths to be a multiple of 4-bytes */ | |
229 | ||
230 | /* set W1C value to clear the interrupt, this hits the register first */ | |
231 | regBuffer[0] = error_int_status; | |
232 | /* the remaining 4 values are set to zero which have no-effect */ | |
233 | regBuffer[1] = 0; | |
234 | regBuffer[2] = 0; | |
235 | regBuffer[3] = 0; | |
236 | ||
237 | status = HIFReadWrite(pDev->HIFDevice, | |
238 | ERROR_INT_STATUS_ADDRESS, | |
239 | regBuffer, | |
240 | 4, | |
241 | HIF_WR_SYNC_BYTE_FIX, | |
242 | NULL); | |
243 | ||
4f69cef0 | 244 | A_ASSERT(status == 0); |
30295c89 VM |
245 | return status; |
246 | } | |
247 | ||
495abc79 | 248 | static int DevServiceDebugInterrupt(struct ar6k_device *pDev) |
30295c89 | 249 | { |
e1ce2a3a | 250 | u32 dummy; |
1f4c34bd | 251 | int status; |
30295c89 VM |
252 | |
253 | /* Send a target failure event to the application */ | |
254 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Target debug interrupt\n")); | |
255 | ||
256 | if (pDev->TargetFailureCallback != NULL) { | |
257 | pDev->TargetFailureCallback(pDev->HTCContext); | |
258 | } | |
259 | ||
260 | if (pDev->GMboxEnabled) { | |
261 | DevNotifyGMboxTargetFailure(pDev); | |
262 | } | |
263 | ||
264 | /* clear the interrupt , the debug error interrupt is | |
265 | * counter 0 */ | |
266 | /* read counter to clear interrupt */ | |
267 | status = HIFReadWrite(pDev->HIFDevice, | |
268 | COUNT_DEC_ADDRESS, | |
ab3655da | 269 | (u8 *)&dummy, |
30295c89 VM |
270 | 4, |
271 | HIF_RD_SYNC_BYTE_INC, | |
272 | NULL); | |
273 | ||
4f69cef0 | 274 | A_ASSERT(status == 0); |
30295c89 VM |
275 | return status; |
276 | } | |
277 | ||
495abc79 | 278 | static int DevServiceCounterInterrupt(struct ar6k_device *pDev) |
30295c89 | 279 | { |
ab3655da | 280 | u8 counter_int_status; |
30295c89 VM |
281 | |
282 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, ("Counter Interrupt\n")); | |
283 | ||
284 | counter_int_status = pDev->IrqProcRegisters.counter_int_status & | |
285 | pDev->IrqEnableRegisters.counter_int_status_enable; | |
286 | ||
287 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, | |
288 | ("Valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", | |
289 | counter_int_status)); | |
290 | ||
291 | /* Check if the debug interrupt is pending | |
292 | * NOTE: other modules like GMBOX may use the counter interrupt for | |
293 | * credit flow control on other counters, we only need to check for the debug assertion | |
294 | * counter interrupt */ | |
295 | if (counter_int_status & AR6K_TARGET_DEBUG_INTR_MASK) { | |
296 | return DevServiceDebugInterrupt(pDev); | |
297 | } | |
298 | ||
4f69cef0 | 299 | return 0; |
30295c89 VM |
300 | } |
301 | ||
302 | /* callback when our fetch to get interrupt status registers completes */ | |
303 | static void DevGetEventAsyncHandler(void *Context, HTC_PACKET *pPacket) | |
304 | { | |
495abc79 | 305 | struct ar6k_device *pDev = (struct ar6k_device *)Context; |
e1ce2a3a | 306 | u32 lookAhead = 0; |
1071a134 | 307 | bool otherInts = false; |
30295c89 VM |
308 | |
309 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevGetEventAsyncHandler: (dev: 0x%lX)\n", (unsigned long)pDev)); | |
310 | ||
311 | do { | |
312 | ||
391bb211 | 313 | if (pPacket->Status) { |
30295c89 VM |
314 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
315 | (" GetEvents I/O request failed, status:%d \n", pPacket->Status)); | |
316 | /* bail out, don't unmask HIF interrupt */ | |
317 | break; | |
318 | } | |
319 | ||
320 | if (pDev->GetPendingEventsFunc != NULL) { | |
321 | /* the HIF layer collected the information for us */ | |
322 | HIF_PENDING_EVENTS_INFO *pEvents = (HIF_PENDING_EVENTS_INFO *)pPacket->pBuffer; | |
323 | if (pEvents->Events & HIF_RECV_MSG_AVAIL) { | |
324 | lookAhead = pEvents->LookAhead; | |
325 | if (0 == lookAhead) { | |
326 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" DevGetEventAsyncHandler1, lookAhead is zero! \n")); | |
327 | } | |
328 | } | |
329 | if (pEvents->Events & HIF_OTHER_EVENTS) { | |
1071a134 | 330 | otherInts = true; |
30295c89 VM |
331 | } |
332 | } else { | |
333 | /* standard interrupt table handling.... */ | |
334 | AR6K_IRQ_PROC_REGISTERS *pReg = (AR6K_IRQ_PROC_REGISTERS *)pPacket->pBuffer; | |
ab3655da | 335 | u8 host_int_status; |
30295c89 VM |
336 | |
337 | host_int_status = pReg->host_int_status & pDev->IrqEnableRegisters.int_status_enable; | |
338 | ||
339 | if (host_int_status & (1 << HTC_MAILBOX)) { | |
340 | host_int_status &= ~(1 << HTC_MAILBOX); | |
341 | if (pReg->rx_lookahead_valid & (1 << HTC_MAILBOX)) { | |
342 | /* mailbox has a message and the look ahead is valid */ | |
343 | lookAhead = pReg->rx_lookahead[HTC_MAILBOX]; | |
344 | if (0 == lookAhead) { | |
345 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" DevGetEventAsyncHandler2, lookAhead is zero! \n")); | |
346 | } | |
347 | } | |
348 | } | |
349 | ||
350 | if (host_int_status) { | |
351 | /* there are other interrupts to handle */ | |
1071a134 | 352 | otherInts = true; |
30295c89 VM |
353 | } |
354 | } | |
355 | ||
356 | if (otherInts || (lookAhead == 0)) { | |
357 | /* if there are other interrupts to process, we cannot do this in the async handler so | |
358 | * ack the interrupt which will cause our sync handler to run again | |
359 | * if however there are no more messages, we can now ack the interrupt */ | |
360 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, | |
361 | (" Acking interrupt from DevGetEventAsyncHandler (otherints:%d, lookahead:0x%X)\n", | |
362 | otherInts, lookAhead)); | |
363 | HIFAckInterrupt(pDev->HIFDevice); | |
364 | } else { | |
365 | int fetched = 0; | |
1f4c34bd | 366 | int status; |
30295c89 VM |
367 | |
368 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, | |
369 | (" DevGetEventAsyncHandler : detected another message, lookahead :0x%X \n", | |
370 | lookAhead)); | |
371 | /* lookahead is non-zero and there are no other interrupts to service, | |
372 | * go get the next message */ | |
373 | status = pDev->MessagePendingCallback(pDev->HTCContext, &lookAhead, 1, NULL, &fetched); | |
374 | ||
509c9d97 | 375 | if (!status && !fetched) { |
30295c89 VM |
376 | /* HTC layer could not pull out messages due to lack of resources, stop IRQ processing */ |
377 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("MessagePendingCallback did not pull any messages, force-ack \n")); | |
378 | DevAsyncIrqProcessComplete(pDev); | |
379 | } | |
380 | } | |
381 | ||
1071a134 | 382 | } while (false); |
30295c89 VM |
383 | |
384 | /* free this IO packet */ | |
385 | AR6KFreeIOPacket(pDev,pPacket); | |
386 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevGetEventAsyncHandler \n")); | |
387 | } | |
388 | ||
389 | /* called by the HTC layer when it wants us to check if the device has any more pending | |
390 | * recv messages, this starts off a series of async requests to read interrupt registers */ | |
1f4c34bd | 391 | int DevCheckPendingRecvMsgsAsync(void *context) |
30295c89 | 392 | { |
495abc79 | 393 | struct ar6k_device *pDev = (struct ar6k_device *)context; |
4f69cef0 | 394 | int status = 0; |
30295c89 VM |
395 | HTC_PACKET *pIOPacket; |
396 | ||
397 | /* this is called in an ASYNC only context, we may NOT block, sleep or call any apis that can | |
398 | * cause us to switch contexts */ | |
399 | ||
400 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevCheckPendingRecvMsgsAsync: (dev: 0x%lX)\n", (unsigned long)pDev)); | |
401 | ||
402 | do { | |
403 | ||
404 | if (HIF_DEVICE_IRQ_SYNC_ONLY == pDev->HifIRQProcessingMode) { | |
405 | /* break the async processing chain right here, no need to continue. | |
406 | * The DevDsrHandler() will handle things in a loop when things are driven | |
407 | * synchronously */ | |
408 | break; | |
409 | } | |
410 | ||
411 | /* an optimization to bypass reading the IRQ status registers unecessarily which can re-wake | |
412 | * the target, if upper layers determine that we are in a low-throughput mode, we can | |
413 | * rely on taking another interrupt rather than re-checking the status registers which can | |
414 | * re-wake the target */ | |
415 | if (pDev->RecheckIRQStatusCnt == 0) { | |
416 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Bypassing IRQ Status re-check, re-acking HIF interrupts\n")); | |
417 | /* ack interrupt */ | |
418 | HIFAckInterrupt(pDev->HIFDevice); | |
419 | break; | |
420 | } | |
421 | ||
422 | /* first allocate one of our HTC packets we created for async I/O | |
423 | * we reuse HTC packet definitions so that we can use the completion mechanism | |
424 | * in DevRWCompletionHandler() */ | |
425 | pIOPacket = AR6KAllocIOPacket(pDev); | |
426 | ||
427 | if (NULL == pIOPacket) { | |
428 | /* there should be only 1 asynchronous request out at a time to read these registers | |
429 | * so this should actually never happen */ | |
430 | status = A_NO_MEMORY; | |
1071a134 | 431 | A_ASSERT(false); |
30295c89 VM |
432 | break; |
433 | } | |
434 | ||
435 | /* stick in our completion routine when the I/O operation completes */ | |
436 | pIOPacket->Completion = DevGetEventAsyncHandler; | |
437 | pIOPacket->pContext = pDev; | |
438 | ||
439 | if (pDev->GetPendingEventsFunc) { | |
440 | /* HIF layer has it's own mechanism, pass the IO to it.. */ | |
441 | status = pDev->GetPendingEventsFunc(pDev->HIFDevice, | |
442 | (HIF_PENDING_EVENTS_INFO *)pIOPacket->pBuffer, | |
443 | pIOPacket); | |
444 | ||
445 | } else { | |
446 | /* standard way, read the interrupt register table asynchronously again */ | |
447 | status = HIFReadWrite(pDev->HIFDevice, | |
448 | HOST_INT_STATUS_ADDRESS, | |
449 | pIOPacket->pBuffer, | |
450 | AR6K_IRQ_PROC_REGS_SIZE, | |
451 | HIF_RD_ASYNC_BYTE_INC, | |
452 | pIOPacket); | |
453 | } | |
454 | ||
455 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,(" Async IO issued to get interrupt status...\n")); | |
1071a134 | 456 | } while (false); |
30295c89 VM |
457 | |
458 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevCheckPendingRecvMsgsAsync \n")); | |
459 | ||
460 | return status; | |
461 | } | |
462 | ||
495abc79 | 463 | void DevAsyncIrqProcessComplete(struct ar6k_device *pDev) |
30295c89 VM |
464 | { |
465 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("DevAsyncIrqProcessComplete - forcing HIF IRQ ACK \n")); | |
466 | HIFAckInterrupt(pDev->HIFDevice); | |
467 | } | |
468 | ||
469 | /* process pending interrupts synchronously */ | |
495abc79 | 470 | static int ProcessPendingIRQs(struct ar6k_device *pDev, bool *pDone, bool *pASyncProcessing) |
30295c89 | 471 | { |
4f69cef0 | 472 | int status = 0; |
ab3655da | 473 | u8 host_int_status = 0; |
e1ce2a3a | 474 | u32 lookAhead = 0; |
30295c89 VM |
475 | |
476 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+ProcessPendingIRQs: (dev: 0x%lX)\n", (unsigned long)pDev)); | |
477 | ||
478 | /*** NOTE: the HIF implementation guarantees that the context of this call allows | |
479 | * us to perform SYNCHRONOUS I/O, that is we can block, sleep or call any API that | |
480 | * can block or switch thread/task ontexts. | |
481 | * This is a fully schedulable context. | |
482 | * */ | |
483 | do { | |
484 | ||
485 | if (pDev->IrqEnableRegisters.int_status_enable == 0) { | |
486 | /* interrupt enables have been cleared, do not try to process any pending interrupts that | |
487 | * may result in more bus transactions. The target may be unresponsive at this | |
488 | * point. */ | |
489 | break; | |
490 | } | |
491 | ||
492 | if (pDev->GetPendingEventsFunc != NULL) { | |
493 | HIF_PENDING_EVENTS_INFO events; | |
494 | ||
495 | #ifdef THREAD_X | |
496 | events.Polling= 0; | |
497 | #endif | |
498 | /* the HIF layer uses a special mechanism to get events | |
499 | * get this synchronously */ | |
500 | status = pDev->GetPendingEventsFunc(pDev->HIFDevice, | |
501 | &events, | |
502 | NULL); | |
503 | ||
391bb211 | 504 | if (status) { |
30295c89 VM |
505 | break; |
506 | } | |
507 | ||
508 | if (events.Events & HIF_RECV_MSG_AVAIL) { | |
509 | lookAhead = events.LookAhead; | |
510 | if (0 == lookAhead) { | |
511 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs1 lookAhead is zero! \n")); | |
512 | } | |
513 | } | |
514 | ||
515 | if (!(events.Events & HIF_OTHER_EVENTS) || | |
516 | !(pDev->IrqEnableRegisters.int_status_enable & OTHER_INTS_ENABLED)) { | |
517 | /* no need to read the register table, no other interesting interrupts. | |
518 | * Some interfaces (like SPI) can shadow interrupt sources without | |
519 | * requiring the host to do a full table read */ | |
520 | break; | |
521 | } | |
522 | ||
523 | /* otherwise fall through and read the register table */ | |
524 | } | |
525 | ||
526 | /* | |
527 | * Read the first 28 bytes of the HTC register table. This will yield us | |
528 | * the value of different int status registers and the lookahead | |
529 | * registers. | |
530 | * length = sizeof(int_status) + sizeof(cpu_int_status) + | |
531 | * sizeof(error_int_status) + sizeof(counter_int_status) + | |
532 | * sizeof(mbox_frame) + sizeof(rx_lookahead_valid) + | |
533 | * sizeof(hole) + sizeof(rx_lookahead) + | |
534 | * sizeof(int_status_enable) + sizeof(cpu_int_status_enable) + | |
535 | * sizeof(error_status_enable) + | |
536 | * sizeof(counter_int_status_enable); | |
537 | * | |
538 | */ | |
539 | #ifdef CONFIG_MMC_SDHCI_S3C | |
540 | pDev->IrqProcRegisters.host_int_status = 0; | |
541 | pDev->IrqProcRegisters.rx_lookahead_valid = 0; | |
542 | pDev->IrqProcRegisters.host_int_status2 = 0; | |
543 | pDev->IrqProcRegisters.rx_lookahead[0] = 0; | |
544 | pDev->IrqProcRegisters.rx_lookahead[1] = 0xaaa5555; | |
545 | #endif /* CONFIG_MMC_SDHCI_S3C */ | |
546 | status = HIFReadWrite(pDev->HIFDevice, | |
547 | HOST_INT_STATUS_ADDRESS, | |
ab3655da | 548 | (u8 *)&pDev->IrqProcRegisters, |
30295c89 VM |
549 | AR6K_IRQ_PROC_REGS_SIZE, |
550 | HIF_RD_SYNC_BYTE_INC, | |
551 | NULL); | |
552 | ||
391bb211 | 553 | if (status) { |
30295c89 VM |
554 | break; |
555 | } | |
556 | ||
557 | #ifdef ATH_DEBUG_MODULE | |
558 | if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_IRQ)) { | |
559 | DevDumpRegisters(pDev, | |
560 | &pDev->IrqProcRegisters, | |
561 | &pDev->IrqEnableRegisters); | |
562 | } | |
563 | #endif | |
564 | ||
565 | /* Update only those registers that are enabled */ | |
566 | host_int_status = pDev->IrqProcRegisters.host_int_status & | |
567 | pDev->IrqEnableRegisters.int_status_enable; | |
568 | ||
569 | if (NULL == pDev->GetPendingEventsFunc) { | |
570 | /* only look at mailbox status if the HIF layer did not provide this function, | |
571 | * on some HIF interfaces reading the RX lookahead is not valid to do */ | |
572 | if (host_int_status & (1 << HTC_MAILBOX)) { | |
573 | /* mask out pending mailbox value, we use "lookAhead" as the real flag for | |
574 | * mailbox processing below */ | |
575 | host_int_status &= ~(1 << HTC_MAILBOX); | |
576 | if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX)) { | |
577 | /* mailbox has a message and the look ahead is valid */ | |
578 | lookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX]; | |
579 | if (0 == lookAhead) { | |
580 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs2, lookAhead is zero! \n")); | |
581 | } | |
582 | } | |
583 | } | |
584 | } else { | |
585 | /* not valid to check if the HIF has another mechanism for reading mailbox pending status*/ | |
586 | host_int_status &= ~(1 << HTC_MAILBOX); | |
587 | } | |
588 | ||
589 | if (pDev->GMboxEnabled) { | |
590 | /*call GMBOX layer to process any interrupts of interest */ | |
591 | status = DevCheckGMboxInterrupts(pDev); | |
592 | } | |
593 | ||
1071a134 | 594 | } while (false); |
30295c89 VM |
595 | |
596 | ||
597 | do { | |
598 | ||
599 | /* did the interrupt status fetches succeed? */ | |
391bb211 | 600 | if (status) { |
30295c89 VM |
601 | break; |
602 | } | |
603 | ||
604 | if ((0 == host_int_status) && (0 == lookAhead)) { | |
605 | /* nothing to process, the caller can use this to break out of a loop */ | |
1071a134 | 606 | *pDone = true; |
30295c89 VM |
607 | break; |
608 | } | |
609 | ||
610 | if (lookAhead != 0) { | |
611 | int fetched = 0; | |
612 | ||
613 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Pending mailbox message, LookAhead: 0x%X\n",lookAhead)); | |
614 | /* Mailbox Interrupt, the HTC layer may issue async requests to empty the | |
615 | * mailbox... | |
616 | * When emptying the recv mailbox we use the async handler above called from the | |
617 | * completion routine of the callers read request. This can improve performance | |
618 | * by reducing context switching when we rapidly pull packets */ | |
619 | status = pDev->MessagePendingCallback(pDev->HTCContext, &lookAhead, 1, pASyncProcessing, &fetched); | |
391bb211 | 620 | if (status) { |
30295c89 VM |
621 | break; |
622 | } | |
623 | ||
624 | if (!fetched) { | |
625 | /* HTC could not pull any messages out due to lack of resources */ | |
626 | /* force DSR handler to ack the interrupt */ | |
1071a134 | 627 | *pASyncProcessing = false; |
30295c89 VM |
628 | pDev->RecheckIRQStatusCnt = 0; |
629 | } | |
630 | } | |
631 | ||
632 | /* now handle the rest of them */ | |
633 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, | |
634 | (" Valid interrupt source(s) for OTHER interrupts: 0x%x\n", | |
635 | host_int_status)); | |
636 | ||
637 | if (HOST_INT_STATUS_CPU_GET(host_int_status)) { | |
638 | /* CPU Interrupt */ | |
639 | status = DevServiceCPUInterrupt(pDev); | |
391bb211 | 640 | if (status){ |
30295c89 VM |
641 | break; |
642 | } | |
643 | } | |
644 | ||
645 | if (HOST_INT_STATUS_ERROR_GET(host_int_status)) { | |
646 | /* Error Interrupt */ | |
647 | status = DevServiceErrorInterrupt(pDev); | |
391bb211 | 648 | if (status){ |
30295c89 VM |
649 | break; |
650 | } | |
651 | } | |
652 | ||
653 | if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) { | |
654 | /* Counter Interrupt */ | |
655 | status = DevServiceCounterInterrupt(pDev); | |
391bb211 | 656 | if (status){ |
30295c89 VM |
657 | break; |
658 | } | |
659 | } | |
660 | ||
1071a134 | 661 | } while (false); |
30295c89 VM |
662 | |
663 | /* an optimization to bypass reading the IRQ status registers unecessarily which can re-wake | |
664 | * the target, if upper layers determine that we are in a low-throughput mode, we can | |
665 | * rely on taking another interrupt rather than re-checking the status registers which can | |
666 | * re-wake the target. | |
667 | * | |
668 | * NOTE : for host interfaces that use the special GetPendingEventsFunc, this optimization cannot | |
669 | * be used due to possible side-effects. For example, SPI requires the host to drain all | |
670 | * messages from the mailbox before exiting the ISR routine. */ | |
671 | if (!(*pASyncProcessing) && (pDev->RecheckIRQStatusCnt == 0) && (pDev->GetPendingEventsFunc == NULL)) { | |
672 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Bypassing IRQ Status re-check, forcing done \n")); | |
1071a134 | 673 | *pDone = true; |
30295c89 VM |
674 | } |
675 | ||
676 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-ProcessPendingIRQs: (done:%d, async:%d) status=%d \n", | |
677 | *pDone, *pASyncProcessing, status)); | |
678 | ||
679 | return status; | |
680 | } | |
681 | ||
682 | ||
683 | /* Synchronousinterrupt handler, this handler kicks off all interrupt processing.*/ | |
1f4c34bd | 684 | int DevDsrHandler(void *context) |
30295c89 | 685 | { |
495abc79 | 686 | struct ar6k_device *pDev = (struct ar6k_device *)context; |
4f69cef0 | 687 | int status = 0; |
1071a134 JP |
688 | bool done = false; |
689 | bool asyncProc = false; | |
30295c89 VM |
690 | |
691 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+DevDsrHandler: (dev: 0x%lX)\n", (unsigned long)pDev)); | |
692 | ||
693 | /* reset the recv counter that tracks when we need to yield from the DSR */ | |
694 | pDev->CurrentDSRRecvCount = 0; | |
695 | /* reset counter used to flag a re-scan of IRQ status registers on the target */ | |
696 | pDev->RecheckIRQStatusCnt = 0; | |
697 | ||
698 | while (!done) { | |
699 | status = ProcessPendingIRQs(pDev, &done, &asyncProc); | |
391bb211 | 700 | if (status) { |
30295c89 VM |
701 | break; |
702 | } | |
703 | ||
704 | if (HIF_DEVICE_IRQ_SYNC_ONLY == pDev->HifIRQProcessingMode) { | |
705 | /* the HIF layer does not allow async IRQ processing, override the asyncProc flag */ | |
1071a134 | 706 | asyncProc = false; |
30295c89 VM |
707 | /* this will cause us to re-enter ProcessPendingIRQ() and re-read interrupt status registers. |
708 | * this has a nice side effect of blocking us until all async read requests are completed. | |
709 | * This behavior is required on some HIF implementations that do not allow ASYNC | |
710 | * processing in interrupt handlers (like Windows CE) */ | |
711 | ||
712 | if (pDev->DSRCanYield && DEV_CHECK_RECV_YIELD(pDev)) { | |
713 | /* ProcessPendingIRQs() pulled enough recv messages to satisfy the yield count, stop | |
714 | * checking for more messages and return */ | |
715 | break; | |
716 | } | |
717 | } | |
718 | ||
719 | if (asyncProc) { | |
720 | /* the function performed some async I/O for performance, we | |
721 | need to exit the ISR immediately, the check below will prevent the interrupt from being | |
722 | Ack'd while we handle it asynchronously */ | |
723 | break; | |
724 | } | |
725 | ||
726 | } | |
727 | ||
509c9d97 | 728 | if (!status && !asyncProc) { |
30295c89 VM |
729 | /* Ack the interrupt only if : |
730 | * 1. we did not get any errors in processing interrupts | |
731 | * 2. there are no outstanding async processing requests */ | |
732 | if (pDev->DSRCanYield) { | |
733 | /* if the DSR can yield do not ACK the interrupt, there could be more pending messages. | |
734 | * The HIF layer must ACK the interrupt on behalf of HTC */ | |
735 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,(" Yield in effect (cur RX count: %d) \n", pDev->CurrentDSRRecvCount)); | |
736 | } else { | |
737 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,(" Acking interrupt from DevDsrHandler \n")); | |
738 | HIFAckInterrupt(pDev->HIFDevice); | |
739 | } | |
740 | } | |
741 | ||
742 | AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-DevDsrHandler \n")); | |
743 | return status; | |
744 | } | |
745 | ||
746 | #ifdef ATH_DEBUG_MODULE | |
495abc79 | 747 | void DumpAR6KDevState(struct ar6k_device *pDev) |
30295c89 | 748 | { |
1f4c34bd | 749 | int status; |
30295c89 VM |
750 | AR6K_IRQ_ENABLE_REGISTERS regs; |
751 | AR6K_IRQ_PROC_REGISTERS procRegs; | |
752 | ||
753 | LOCK_AR6K(pDev); | |
754 | /* copy into our temp area */ | |
05209262 | 755 | memcpy(®s,&pDev->IrqEnableRegisters,AR6K_IRQ_ENABLE_REGS_SIZE); |
30295c89 VM |
756 | UNLOCK_AR6K(pDev); |
757 | ||
758 | /* load the register table from the device */ | |
759 | status = HIFReadWrite(pDev->HIFDevice, | |
760 | HOST_INT_STATUS_ADDRESS, | |
ab3655da | 761 | (u8 *)&procRegs, |
30295c89 VM |
762 | AR6K_IRQ_PROC_REGS_SIZE, |
763 | HIF_RD_SYNC_BYTE_INC, | |
764 | NULL); | |
765 | ||
391bb211 | 766 | if (status) { |
30295c89 VM |
767 | AR_DEBUG_PRINTF(ATH_DEBUG_ERR, |
768 | ("DumpAR6KDevState : Failed to read register table (%d) \n",status)); | |
769 | return; | |
770 | } | |
771 | ||
772 | DevDumpRegisters(pDev,&procRegs,®s); | |
773 | ||
774 | if (pDev->GMboxInfo.pStateDumpCallback != NULL) { | |
775 | pDev->GMboxInfo.pStateDumpCallback(pDev->GMboxInfo.pProtocolContext); | |
776 | } | |
777 | ||
778 | /* dump any bus state at the HIF layer */ | |
779 | HIFConfigureDevice(pDev->HIFDevice,HIF_DEVICE_DEBUG_BUS_STATE,NULL,0); | |
780 | ||
781 | } | |
782 | #endif | |
783 | ||
784 |