diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/hif.c')
-rwxr-xr-x | drivers/net/wireless/ath/ath6kl/hif.c | 791 |
1 files changed, 0 insertions, 791 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c deleted file mode 100755 index 2793ab6..0000000 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ /dev/null @@ -1,791 +0,0 @@ -/* - * Copyright (c) 2007-2011 Atheros Communications Inc. - * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include "hif.h" - -#include "core.h" -#include "target.h" -#include "hif-ops.h" -#include "debug.h" - -#define MAILBOX_FOR_BLOCK_SIZE 1 - -#define ATH6KL_TIME_QUANTUM 10 /* in ms */ - -static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req, - bool from_dma) -{ - u8 *buf; - int i; - - buf = req->virt_dma_buf; - - for (i = 0; i < req->scat_entries; i++) { - - if (from_dma) - memcpy(req->scat_list[i].buf, buf, - req->scat_list[i].len); - else - memcpy(buf, req->scat_list[i].buf, - req->scat_list[i].len); - - buf += req->scat_list[i].len; - } - - return 0; -} - -int ath6kl_hif_rw_comp_handler(void *context, int status) -{ - struct htc_packet *packet = context; - - ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n", - packet, status); - - packet->status = status; - packet->completion(packet->context, packet); - - return 0; -} -#define REG_DUMP_COUNT_AR6003 60 -#define REGISTER_DUMP_LEN_MAX 60 - -static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) -{ - __le32 regdump_val[REGISTER_DUMP_LEN_MAX]; - u32 i, address, regdump_addr = 0; - int ret; - - if (ar->target_type != TARGET_TYPE_AR6003) - return; - - /* the reg dump pointer is copied to the host interest area */ - address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); - address = TARG_VTOP(ar->target_type, address); - - /* read RAM location through diagnostic window */ - ret = ath6kl_diag_read32(ar, address, ®dump_addr); - - if (ret || !regdump_addr) { - ath6kl_warn("failed to get ptr to register dump area: %d\n", - ret); - return; - } - - ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n", - regdump_addr); - regdump_addr = TARG_VTOP(ar->target_type, regdump_addr); - - /* fetch register dump data */ - ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)®dump_val[0], - REG_DUMP_COUNT_AR6003 * (sizeof(u32))); - if (ret) { - ath6kl_warn("failed to get register dump: %d\n", ret); - return; - } - - ath6kl_info("crash dump:\n"); - ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version, - ar->wiphy->fw_version); - - BUILD_BUG_ON(REG_DUMP_COUNT_AR6003 % 4); - - for (i = 0; i < REG_DUMP_COUNT_AR6003; i += 4) { - ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", - i, - le32_to_cpu(regdump_val[i]), - le32_to_cpu(regdump_val[i + 1]), - le32_to_cpu(regdump_val[i + 2]), - le32_to_cpu(regdump_val[i + 3])); - } - -} - -static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) -{ - u32 dummy; - int ret; - struct ath6kl_vif *vif; - - vif = ath6kl_vif_first(dev->ar); - - ath6kl_warn("firmware crashed\n"); - - /* - * read counter to clear the interrupt, the debug error interrupt is - * counter 0. - */ - ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS, - (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC); - if (ret) - ath6kl_warn("Failed to clear debug interrupt: %d\n", ret); - - ath6kl_hif_dump_fw_crash(dev->ar); - ath6kl_read_fwlogs(dev->ar); - - cfg80211_priv_event(vif->ndev, "HANG", GFP_ATOMIC); - - return ret; -} - -/* mailbox recv message polling */ -int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd, - int timeout) -{ - struct ath6kl_irq_proc_registers *rg; - int status = 0, i; - u8 htc_mbox = 1 << HTC_MAILBOX; - - for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) { - /* this is the standard HIF way, load the reg table */ - 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) { - ath6kl_err("failed to read reg table\n"); - return status; - } - - /* check for MBOX data and valid lookahead */ - if (dev->irq_proc_reg.host_int_status & htc_mbox) { - if (dev->irq_proc_reg.rx_lkahd_valid & - htc_mbox) { - /* - * Mailbox has a message and the look ahead - * is valid. - */ - rg = &dev->irq_proc_reg; - *lk_ahd = - le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); - break; - } - } - - /* delay a little */ - mdelay(ATH6KL_TIME_QUANTUM); - ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i); - } - - if (i == 0) { - ath6kl_err("timeout waiting for recv message\n"); - status = -ETIME; - /* check if the target asserted */ - if (dev->irq_proc_reg.counter_int_status & - ATH6KL_TARGET_DEBUG_INTR_MASK) - /* - * Target failure handler will be called in case of - * an assert. - */ - ath6kl_hif_proc_dbg_intr(dev); - } - - return status; -} - -/* - * Disable packet reception (used in case the host runs out of buffers) - * using the interrupt enable registers through the host I/F - */ -int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx) -{ - struct ath6kl_irq_enable_reg regs; - int status = 0; - - ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n", - enable_rx ? "enable" : "disable"); - - /* take the lock to protect interrupt enable shadows */ - spin_lock_bh(&dev->lock); - - if (enable_rx) - dev->irq_en_reg.int_status_en |= - SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); - else - dev->irq_en_reg.int_status_en &= - ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); - - memcpy(®s, &dev->irq_en_reg, sizeof(regs)); - - spin_unlock_bh(&dev->lock); - - status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, - ®s.int_status_en, - sizeof(struct ath6kl_irq_enable_reg), - HIF_WR_SYNC_BYTE_INC); - - return status; -} - -int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev, - struct hif_scatter_req *scat_req, bool read) -{ - int status = 0; - - if (read) { - scat_req->req = HIF_RD_SYNC_BLOCK_FIX; - scat_req->addr = dev->ar->mbox_info.htc_addr; - } else { - scat_req->req = HIF_WR_ASYNC_BLOCK_INC; - - scat_req->addr = - (scat_req->len > HIF_MBOX_WIDTH) ? - dev->ar->mbox_info.htc_ext_addr : - dev->ar->mbox_info.htc_addr; - } - - ath6kl_dbg(ATH6KL_DBG_HIF, - "hif submit scatter request entries %d len %d mbox 0x%x %s %s\n", - scat_req->scat_entries, scat_req->len, - scat_req->addr, !read ? "async" : "sync", - (read) ? "rd" : "wr"); - - if (!read && scat_req->virt_scat) { - status = ath6kl_hif_cp_scat_dma_buf(scat_req, false); - if (status) { - scat_req->status = status; - scat_req->complete(dev->ar->htc_target, scat_req); - return 0; - } - } - - status = ath6kl_hif_scat_req_rw(dev->ar, scat_req); - - if (read) { - /* in sync mode, we can touch the scatter request */ - scat_req->status = status; - if (!status && scat_req->virt_scat) - scat_req->status = - ath6kl_hif_cp_scat_dma_buf(scat_req, true); - } - - return status; -} - -static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev) -{ - u8 counter_int_status; - - ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n"); - - counter_int_status = dev->irq_proc_reg.counter_int_status & - dev->irq_en_reg.cntr_int_status_en; - - ath6kl_dbg(ATH6KL_DBG_IRQ, - "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", - counter_int_status); - - /* - * NOTE: other modules like GMBOX may use the counter interrupt for - * credit flow control on other counters, we only need to check for - * the debug assertion counter interrupt. - */ - if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK) - return ath6kl_hif_proc_dbg_intr(dev); - - return 0; -} - -static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev) -{ - int status; - u8 error_int_status; - u8 reg_buf[4]; - - ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n"); - - error_int_status = dev->irq_proc_reg.error_int_status & 0x0F; - if (!error_int_status) { - WARN_ON(1); - return -EIO; - } - - ath6kl_dbg(ATH6KL_DBG_IRQ, - "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n", - error_int_status); - - if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status)) - ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n"); - - if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status)) - ath6kl_err("rx underflow\n"); - - if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status)) - ath6kl_err("tx overflow\n"); - - /* Clear the interrupt */ - dev->irq_proc_reg.error_int_status &= ~error_int_status; - - /* set W1C value to clear the interrupt, this hits the register first */ - reg_buf[0] = error_int_status; - reg_buf[1] = 0; - reg_buf[2] = 0; - reg_buf[3] = 0; - - status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS, - reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); - - if (status) - WARN_ON(1); - - return status; -} - -static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) -{ - int status; - u8 cpu_int_status; - u8 reg_buf[4]; - - ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n"); - - cpu_int_status = dev->irq_proc_reg.cpu_int_status & - dev->irq_en_reg.cpu_int_status_en; - if (!cpu_int_status) { - WARN_ON(1); - return -EIO; - } - - ath6kl_dbg(ATH6KL_DBG_IRQ, - "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", - cpu_int_status); - - /* Clear the interrupt */ - dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status; - - /* - * Set up the register transfer buffer to hit the register 4 times , - * this is done to make the access 4-byte aligned to mitigate issues - * with host bus interconnects that restrict bus transfer lengths to - * be a multiple of 4-bytes. - */ - - /* set W1C value to clear the interrupt, this hits the register first */ - reg_buf[0] = cpu_int_status; - /* the remaining are set to zero which have no-effect */ - reg_buf[1] = 0; - reg_buf[2] = 0; - reg_buf[3] = 0; - - status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS, - reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); - - if (status) - WARN_ON(1); - - return status; -} - -/* 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; - struct ath6kl_vif *vif; - vif = ath6kl_vif_first(dev->ar); - 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. - */ -#ifdef CONFIG_MACH_PX - if (vif->sdio_remove == true) { - *done = true; - goto out; - } -#endif - /* - * 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; - - if (AR_DBG_LVL_CHECK(ATH6KL_DBG_IRQ)) - 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 (vif->force_reload == true) { - lk_ahd = 0; - } - if (!lk_ahd) { - ath6kl_err("lookAhead is zero! force_reload = %d\n", vif->force_reload); -#ifdef CONFIG_MACH_PX - cfg80211_priv_event(vif->ndev, "HANG", GFP_ATOMIC); - ath6kl_hif_rx_control(dev, false); - ssleep(3); - status = -ENOMEM; -#endif - } - } - } - } - - 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); -#ifdef CONFIG_MACH_PX - if (status && status != -ECANCELED) { - cfg80211_priv_event(vif->ndev, "HANG", GFP_ATOMIC); - ath6kl_hif_rx_control(dev, false); - ssleep(3); - goto out; - } -#else - if (status) - goto out; -#endif - 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; -} - -/* interrupt handler, kicks off all interrupt processing */ -int ath6kl_hif_intr_bh_handler(struct ath6kl *ar) -{ -#ifdef CONFIG_MACH_PX - struct ath6kl_device *dev; - unsigned long timeout; - int status = 0; - bool done = false; - - if ((ar != NULL) && (ar->htc_target != NULL) && (ar->htc_target->dev != NULL)) - dev = ar->htc_target->dev; - else - return status; -#else - struct ath6kl_device *dev = ar->htc_target->dev; - unsigned long timeout; - int status = 0; - bool done = false; -#endif - - /* - * Reset counter used to flag a re-scan of IRQ status registers on - * the target. - */ - dev->htc_cnxt->chk_irq_status_cnt = 0; - - /* - * IRQ processing is synchronous, interrupt status registers can be - * re-read. - */ - timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT); -#ifdef CONFIG_MACH_PX - while (time_before(jiffies, timeout) && !done && (dev != NULL)) -#else - while (time_before(jiffies, timeout) && !done) -#endif - { - status = proc_pending_irqs(dev, &done); - if (status) - break; - } - - return status; -} - -static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev) -{ - struct ath6kl_irq_enable_reg regs; - int status; - - spin_lock_bh(&dev->lock); - - /* Enable all but ATH6KL CPU interrupts */ - dev->irq_en_reg.int_status_en = - SM(INT_STATUS_ENABLE_ERROR, 0x01) | - SM(INT_STATUS_ENABLE_CPU, 0x01) | - SM(INT_STATUS_ENABLE_COUNTER, 0x01); - - /* - * NOTE: There are some cases where HIF can do detection of - * pending mbox messages which is disabled now. - */ - dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); - - /* Set up the CPU Interrupt status Register */ - dev->irq_en_reg.cpu_int_status_en = 0; - - /* Set up the Error Interrupt status Register */ - dev->irq_en_reg.err_int_status_en = - SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) | - SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1); - - /* - * Enable Counter interrupt status register to get fatal errors for - * debugging. - */ - dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT, - ATH6KL_TARGET_DEBUG_INTR_MASK); - memcpy(®s, &dev->irq_en_reg, sizeof(regs)); - - spin_unlock_bh(&dev->lock); - - status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, - ®s.int_status_en, sizeof(regs), - HIF_WR_SYNC_BYTE_INC); - - if (status) - ath6kl_err("failed to update interrupt ctl reg err: %d\n", - status); - - return status; -} - -int ath6kl_hif_disable_intrs(struct ath6kl_device *dev) -{ - struct ath6kl_irq_enable_reg regs; - - spin_lock_bh(&dev->lock); - /* Disable all interrupts */ - dev->irq_en_reg.int_status_en = 0; - dev->irq_en_reg.cpu_int_status_en = 0; - dev->irq_en_reg.err_int_status_en = 0; - dev->irq_en_reg.cntr_int_status_en = 0; - memcpy(®s, &dev->irq_en_reg, sizeof(regs)); - spin_unlock_bh(&dev->lock); - - return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, - ®s.int_status_en, sizeof(regs), - HIF_WR_SYNC_BYTE_INC); -} - -/* enable device interrupts */ -int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev) -{ - int status = 0; - - /* - * Make sure interrupt are disabled before unmasking at the HIF - * layer. The rationale here is that between device insertion - * (where we clear the interrupts the first time) and when HTC - * is finally ready to handle interrupts, other software can perform - * target "soft" resets. The ATH6KL interrupt enables reset back to an - * "enabled" state when this happens. - */ - ath6kl_hif_disable_intrs(dev); - - /* unmask the host controller interrupts */ - ath6kl_hif_irq_enable(dev->ar); - status = ath6kl_hif_enable_intrs(dev); - - return status; -} - -/* disable all device interrupts */ -int ath6kl_hif_mask_intrs(struct ath6kl_device *dev) -{ - /* - * Mask the interrupt at the HIF layer to avoid any stray interrupt - * taken while we zero out our shadow registers in - * ath6kl_hif_disable_intrs(). - */ - ath6kl_hif_irq_disable(dev->ar); - - return ath6kl_hif_disable_intrs(dev); -} - -int ath6kl_hif_setup(struct ath6kl_device *dev) -{ - int status = 0; - - spin_lock_init(&dev->lock); - - /* - * NOTE: we actually get the block size of a mailbox other than 0, - * for SDIO the block size on mailbox 0 is artificially set to 1. - * So we use the block size that is set for the other 3 mailboxes. - */ - dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size; - - /* must be a power of 2 */ - if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) { - WARN_ON(1); - status = -EINVAL; - goto fail_setup; - } - - /* assemble mask, used for padding to a block */ - dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1; - - ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", - dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); - - /* usb doesn't support enabling interrupts */ - /* FIXME: remove check once USB support is implemented */ - if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) - return 0; - - status = ath6kl_hif_disable_intrs(dev); - -fail_setup: - return status; - -} - -int ath6kl_hif_wait_for_pending_recv(struct ath6kl *ar) -{ - int loop_cnt = 5; - u8 host_int_status; - int status = 0; - - struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); - - do { - int irq_cnt = 10; - while (atomic_read(&ar_sdio->irq_handling) && --irq_cnt > 0) { - /* wait until irq handler finished all the jobs */ - schedule_timeout_interruptible(HZ / 10); - } - /* check if there is any pending irq due to force done */ - host_int_status = 0; - status = hif_read_write_sync(ar, HOST_INT_STATUS_ADDRESS, - (u8 *)&host_int_status, sizeof(host_int_status), - HIF_RD_SYNC_BYTE_INC); - /* force it to query again due to resources issue*/ - if (status) - host_int_status = 1; - else - host_int_status = (host_int_status & (1 << 0)); - - if (host_int_status) { - /* Wait until irq handler finishes its job */ - schedule_timeout_interruptible(1); - } - } while (host_int_status && --loop_cnt > 0); - - if (host_int_status || loop_cnt == 0) { - ath6kl_err("%s(), Unable clear up pending IRQ" - "before the system suspended\n", __func__); - return -1; - } - - return 0; -} |