Esempio n. 1
0
/* 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;
}
Esempio n. 2
0
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;
}