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