/* process pending interrupts synchronously */ static int proc_pending_irqs(struct ath6kl_device *dev, bool *done) { struct ath6kl_irq_proc_registers *rg; int status = 0; u8 host_int_status = 0; u32 lk_ahd = 0; u8 htc_mbox = 1 << HTC_MAILBOX; ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev); /* * NOTE: 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 * contexts. This is a fully schedulable context. */ /* * Process pending intr only when int_status_en is clear, it may * result in unnecessary bus transaction otherwise. Target may be * unresponsive at the time. */ if (dev->irq_en_reg.int_status_en) { /* * 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_lkahd_valid) * + sizeof(hole) + sizeof(rx_lkahd) + * sizeof(int_status_en) + * sizeof(cpu_int_status_en) + * sizeof(err_int_status_en) + * sizeof(cntr_int_status_en); */ status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, (u8 *) &dev->irq_proc_reg, sizeof(dev->irq_proc_reg), HIF_RD_SYNC_BYTE_INC); if (status) goto out; ath6kl_dump_registers(dev, &dev->irq_proc_reg, &dev->irq_en_reg); /* Update only those registers that are enabled */ host_int_status = dev->irq_proc_reg.host_int_status & dev->irq_en_reg.int_status_en; /* Look at mbox status */ if (host_int_status & htc_mbox) { /* * Mask out pending mbox value, we use "lookAhead as * the real flag for mbox processing. */ host_int_status &= ~htc_mbox; if (dev->irq_proc_reg.rx_lkahd_valid & htc_mbox) { rg = &dev->irq_proc_reg; lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); if (!lk_ahd) ath6kl_err("lookAhead is zero!\n"); } } } if (!host_int_status && !lk_ahd) { *done = true; goto out; } if (lk_ahd) { int fetched = 0; ath6kl_dbg(ATH6KL_DBG_IRQ, "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd); /* * 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 = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt, lk_ahd, &fetched); if (status) goto out; if (!fetched) /* * HTC could not pull any messages out due to lack * of resources. */ dev->htc_cnxt->chk_irq_status_cnt = 0; } /* now handle the rest of them */ ath6kl_dbg(ATH6KL_DBG_IRQ, "valid interrupt source(s) for other interrupts: 0x%x\n", host_int_status); if (MS(HOST_INT_STATUS_CPU, host_int_status)) { /* CPU Interrupt */ status = ath6kl_hif_proc_cpu_intr(dev); if (status) goto out; } if (MS(HOST_INT_STATUS_ERROR, host_int_status)) { /* Error Interrupt */ status = ath6kl_hif_proc_err_intr(dev); if (status) goto out; } if (MS(HOST_INT_STATUS_COUNTER, host_int_status)) /* Counter Interrupt */ status = ath6kl_hif_proc_counter_intr(dev); out: /* * 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 makes use of detecting pending * mbox messages at hif can not use this optimization due to * possible side effects, SPI requires the host to drain all * messages from the mailbox before exiting the ISR routine. */ ath6kl_dbg(ATH6KL_DBG_IRQ, "bypassing irq status re-check, forcing done\n"); if (!dev->htc_cnxt->chk_irq_status_cnt) *done = true; ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (done:%d, status=%d\n", *done, status); return status; }
static int proc_pending_irqs(struct ath6kl_device *dev, bool *done) { struct ath6kl_irq_proc_registers *rg; int status = 0; u8 host_int_status = 0; u32 lk_ahd = 0; u8 htc_mbox = 1 << HTC_MAILBOX; ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev); if (dev->irq_en_reg.int_status_en) { status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, (u8 *) &dev->irq_proc_reg, sizeof(dev->irq_proc_reg), HIF_RD_SYNC_BYTE_INC); if (status) goto out; if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ)) ath6kl_dump_registers(dev, &dev->irq_proc_reg, &dev->irq_en_reg); host_int_status = dev->irq_proc_reg.host_int_status & dev->irq_en_reg.int_status_en; if (host_int_status & htc_mbox) { host_int_status &= ~htc_mbox; if (dev->irq_proc_reg.rx_lkahd_valid & htc_mbox) { rg = &dev->irq_proc_reg; lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); if (!lk_ahd) ath6kl_err("lookAhead is zero!\n"); } } } if (!host_int_status && !lk_ahd) { *done = true; goto out; } if (lk_ahd) { int fetched = 0; ath6kl_dbg(ATH6KL_DBG_IRQ, "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd); status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt, lk_ahd, &fetched); if (status) goto out; if (!fetched) dev->htc_cnxt->chk_irq_status_cnt = 0; } ath6kl_dbg(ATH6KL_DBG_IRQ, "valid interrupt source(s) for other interrupts: 0x%x\n", host_int_status); if (MS(HOST_INT_STATUS_CPU, host_int_status)) { status = ath6kl_hif_proc_cpu_intr(dev); if (status) goto out; } if (MS(HOST_INT_STATUS_ERROR, host_int_status)) { status = ath6kl_hif_proc_err_intr(dev); if (status) goto out; } if (MS(HOST_INT_STATUS_COUNTER, host_int_status)) status = ath6kl_hif_proc_counter_intr(dev); out: ath6kl_dbg(ATH6KL_DBG_IRQ, "bypassing irq status re-check, forcing done\n"); if (!dev->htc_cnxt->chk_irq_status_cnt) *done = true; ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (done:%d, status=%d\n", *done, status); return status; }