/* process pending interrupts synchronously */ static int ProcessPendingIRQs(AR6K_DEVICE *pDev, bool *pDone, bool *pASyncProcessing) { int status = 0; u8 host_int_status = 0; u32 lookAhead = 0; AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+ProcessPendingIRQs: (dev: 0x%lX)\n", (unsigned long)pDev)); /*** NOTE: the HIF implementation guarantees that the context of this call allows * us to perform SYNCHRONOUS I/O, that is we can block, sleep or call any API that * can block or switch thread/task ontexts. * This is a fully schedulable context. * */ do { if (pDev->IrqEnableRegisters.int_status_enable == 0) { /* interrupt enables have been cleared, do not try to process any pending interrupts that * may result in more bus transactions. The target may be unresponsive at this * point. */ break; } if (pDev->GetPendingEventsFunc != NULL) { HIF_PENDING_EVENTS_INFO events; #ifdef THREAD_X events.Polling= 0; #endif /* the HIF layer uses a special mechanism to get events * get this synchronously */ status = pDev->GetPendingEventsFunc(pDev->HIFDevice, &events, NULL); if (status) { break; } if (events.Events & HIF_RECV_MSG_AVAIL) { lookAhead = events.LookAhead; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs1 lookAhead is zero! \n")); } } if (!(events.Events & HIF_OTHER_EVENTS) || !(pDev->IrqEnableRegisters.int_status_enable & OTHER_INTS_ENABLED)) { /* no need to read the register table, no other interesting interrupts. * Some interfaces (like SPI) can shadow interrupt sources without * requiring the host to do a full table read */ break; } /* otherwise fall through and read the register table */ } /* * Read the first 28 bytes of the HTC register table. This will yield us * the value of different int status registers and the lookahead * registers. * length = sizeof(int_status) + sizeof(cpu_int_status) + * sizeof(error_int_status) + sizeof(counter_int_status) + * sizeof(mbox_frame) + sizeof(rx_lookahead_valid) + * sizeof(hole) + sizeof(rx_lookahead) + * sizeof(int_status_enable) + sizeof(cpu_int_status_enable) + * sizeof(error_status_enable) + * sizeof(counter_int_status_enable); * */ #ifdef CONFIG_MMC_SDHCI_S3C pDev->IrqProcRegisters.host_int_status = 0; pDev->IrqProcRegisters.rx_lookahead_valid = 0; pDev->IrqProcRegisters.host_int_status2 = 0; pDev->IrqProcRegisters.rx_lookahead[0] = 0; pDev->IrqProcRegisters.rx_lookahead[1] = 0xaaa5555; #endif /* CONFIG_MMC_SDHCI_S3C */ status = HIFReadWrite(pDev->HIFDevice, HOST_INT_STATUS_ADDRESS, (u8 *)&pDev->IrqProcRegisters, AR6K_IRQ_PROC_REGS_SIZE, HIF_RD_SYNC_BYTE_INC, NULL); if (status) { break; } #ifdef ATH_DEBUG_MODULE if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_IRQ)) { DevDumpRegisters(pDev, &pDev->IrqProcRegisters, &pDev->IrqEnableRegisters); } #endif /* Update only those registers that are enabled */ host_int_status = pDev->IrqProcRegisters.host_int_status & pDev->IrqEnableRegisters.int_status_enable; if (NULL == pDev->GetPendingEventsFunc) { /* only look at mailbox status if the HIF layer did not provide this function, * on some HIF interfaces reading the RX lookahead is not valid to do */ if (host_int_status & (1 << HTC_MAILBOX)) { /* mask out pending mailbox value, we use "lookAhead" as the real flag for * mailbox processing below */ host_int_status &= ~(1 << HTC_MAILBOX); if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX)) { /* mailbox has a message and the look ahead is valid */ lookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX]; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs2, lookAhead is zero! \n")); } } } } else { /* not valid to check if the HIF has another mechanism for reading mailbox pending status*/ host_int_status &= ~(1 << HTC_MAILBOX); } if (pDev->GMboxEnabled) { /*call GMBOX layer to process any interrupts of interest */ status = DevCheckGMboxInterrupts(pDev); } } while (false); do { /* did the interrupt status fetches succeed? */ if (status) { break; } if ((0 == host_int_status) && (0 == lookAhead)) { /* nothing to process, the caller can use this to break out of a loop */ *pDone = true; break; } if (lookAhead != 0) { int fetched = 0; AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Pending mailbox message, LookAhead: 0x%X\n",lookAhead)); /* Mailbox Interrupt, the HTC layer may issue async requests to empty the * mailbox... * When emptying the recv mailbox we use the async handler above called from the * completion routine of the callers read request. This can improve performance * by reducing context switching when we rapidly pull packets */ status = pDev->MessagePendingCallback(pDev->HTCContext, &lookAhead, 1, pASyncProcessing, &fetched); if (status) { break; } if (!fetched) { /* HTC could not pull any messages out due to lack of resources */ /* force DSR handler to ack the interrupt */ *pASyncProcessing = false; pDev->RecheckIRQStatusCnt = 0; } } /* now handle the rest of them */ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, (" Valid interrupt source(s) for OTHER interrupts: 0x%x\n", host_int_status)); if (HOST_INT_STATUS_CPU_GET(host_int_status)) { /* CPU Interrupt */ status = DevServiceCPUInterrupt(pDev); if (status){ break; } } if (HOST_INT_STATUS_ERROR_GET(host_int_status)) { /* Error Interrupt */ status = DevServiceErrorInterrupt(pDev); if (status){ break; } } if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) { /* Counter Interrupt */ status = DevServiceCounterInterrupt(pDev); if (status){ break; } } } while (false); /* an optimization to bypass reading the IRQ status registers unecessarily which can re-wake * the target, if upper layers determine that we are in a low-throughput mode, we can * rely on taking another interrupt rather than re-checking the status registers which can * re-wake the target. * * NOTE : for host interfaces that use the special GetPendingEventsFunc, this optimization cannot * be used due to possible side-effects. For example, SPI requires the host to drain all * messages from the mailbox before exiting the ISR routine. */ if (!(*pASyncProcessing) && (pDev->RecheckIRQStatusCnt == 0) && (pDev->GetPendingEventsFunc == NULL)) { AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Bypassing IRQ Status re-check, forcing done \n")); *pDone = true; } AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-ProcessPendingIRQs: (done:%d, async:%d) status=%d \n", *pDone, *pASyncProcessing, status)); return status; }
/* process pending interrupts synchronously */ static A_STATUS ProcessPendingIRQs(AR6K_DEVICE *pDev, A_BOOL *pDone, A_BOOL *pASyncProcessing) { A_STATUS status = A_OK; A_UINT8 host_int_status = 0; A_UINT32 lookAhead = 0; AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("+ProcessPendingIRQs: (dev: 0x%X)\n", (A_UINT32)pDev)); /*** NOTE: the HIF implementation guarantees that the context of this call allows * us to perform SYNCHRONOUS I/O, that is we can block, sleep or call any API that * can block or switch thread/task ontexts. * This is a fully schedulable context. * */ do { if (pDev->IrqEnableRegisters.int_status_enable == 0) { /* interrupt enables have been cleared, do not try to process any pending interrupts that * may result in more bus transactions. The target may be unresponsive at this * point. */ break; } if (pDev->GetPendingEventsFunc != NULL) { HIF_PENDING_EVENTS_INFO events; /* the HIF layer uses a special mechanism to get events * get this synchronously */ status = pDev->GetPendingEventsFunc(pDev->HIFDevice, &events, NULL); if (A_FAILED(status)) { break; } if (events.Events & HIF_RECV_MSG_AVAIL) { lookAhead = events.LookAhead; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs1 lookAhead is zero! \n")); } } if (!(events.Events & HIF_OTHER_EVENTS) || !(pDev->IrqEnableRegisters.int_status_enable & OTHER_INTS_ENABLED)) { /* no need to read the register table, no other interesting interrupts. * Some interfaces (like SPI) can shadow interrupt sources without * requiring the host to do a full table read */ break; } /* otherwise fall through and read the register table */ } /* * Read the first 28 bytes of the HTC register table. This will yield us * the value of different int status registers and the lookahead * registers. * length = sizeof(int_status) + sizeof(cpu_int_status) + * sizeof(error_int_status) + sizeof(counter_int_status) + * sizeof(mbox_frame) + sizeof(rx_lookahead_valid) + * sizeof(hole) + sizeof(rx_lookahead) + * sizeof(int_status_enable) + sizeof(cpu_int_status_enable) + * sizeof(error_status_enable) + * sizeof(counter_int_status_enable); * */ status = HIFReadWrite(pDev->HIFDevice, HOST_INT_STATUS_ADDRESS, (A_UINT8 *)&pDev->IrqProcRegisters, AR6K_IRQ_PROC_REGS_SIZE, HIF_RD_SYNC_BYTE_INC, NULL); if (A_FAILED(status)) { break; } if (AR_DEBUG_LVL_CHECK(ATH_DEBUG_IRQ)) { DevDumpRegisters(&pDev->IrqProcRegisters, &pDev->IrqEnableRegisters); } /* Update only those registers that are enabled */ host_int_status = pDev->IrqProcRegisters.host_int_status & pDev->IrqEnableRegisters.int_status_enable; if (NULL == pDev->GetPendingEventsFunc) { /* only look at mailbox status if the HIF layer did not provide this function, * on some HIF interfaces reading the RX lookahead is not valid to do */ if (host_int_status & (1 << HTC_MAILBOX)) { /* mask out pending mailbox value, we use "lookAhead" as the real flag for * mailbox processing below */ host_int_status &= ~(1 << HTC_MAILBOX); if (pDev->IrqProcRegisters.rx_lookahead_valid & (1 << HTC_MAILBOX)) { /* mailbox has a message and the look ahead is valid */ lookAhead = pDev->IrqProcRegisters.rx_lookahead[HTC_MAILBOX]; if (0 == lookAhead) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR,(" ProcessPendingIRQs2, lookAhead is zero! \n")); } } } } else { /* not valid to check if the HIF has another mechanism for reading mailbox pending status*/ host_int_status &= ~(1 << HTC_MAILBOX); } } while (FALSE); do { /* did the interrupt status fetches succeed? */ if (A_FAILED(status)) { break; } if ((0 == host_int_status) && (0 == lookAhead)) { /* nothing to process, the caller can use this to break out of a loop */ *pDone = TRUE; break; } if (lookAhead != 0) { AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("Pending mailbox message, LookAhead: 0x%X\n",lookAhead)); /* Mailbox Interrupt, the HTC layer may issue async requests to empty the * mailbox... * When emptying the recv mailbox we use the async handler above called from the * completion routine of the callers read request. This can improve performance * by reducing context switching when we rapidly pull packets */ status = pDev->MessagePendingCallback(pDev->HTCContext, lookAhead, pASyncProcessing); if (A_FAILED(status)) { break; } } /* now handle the rest of them */ AR_DEBUG_PRINTF(ATH_DEBUG_IRQ, (" Valid interrupt source(s) for OTHER interrupts: 0x%x\n", host_int_status)); if (HOST_INT_STATUS_CPU_GET(host_int_status)) { /* CPU Interrupt */ status = DevServiceCPUInterrupt(pDev); if (A_FAILED(status)){ break; } } if (HOST_INT_STATUS_ERROR_GET(host_int_status)) { /* Error Interrupt */ status = DevServiceErrorInterrupt(pDev); if (A_FAILED(status)){ break; } } if (HOST_INT_STATUS_COUNTER_GET(host_int_status)) { /* Counter Interrupt */ status = DevServiceCounterInterrupt(pDev); if (A_FAILED(status)){ break; } } } while (FALSE); AR_DEBUG_PRINTF(ATH_DEBUG_IRQ,("-ProcessPendingIRQs: (done:%d, async:%d) status=%d \n", *pDone, *pASyncProcessing, status)); return status; }