Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2007-2011 Atheros Communications Inc.
0003  * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
0004  *
0005  * Permission to use, copy, modify, and/or distribute this software for any
0006  * purpose with or without fee is hereby granted, provided that the above
0007  * copyright notice and this permission notice appear in all copies.
0008  *
0009  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0010  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0011  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0012  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0013  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0014  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0015  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0016  */
0017 #include "hif.h"
0018 
0019 #include <linux/export.h>
0020 
0021 #include "core.h"
0022 #include "target.h"
0023 #include "hif-ops.h"
0024 #include "debug.h"
0025 #include "trace.h"
0026 
0027 #define MAILBOX_FOR_BLOCK_SIZE          1
0028 
0029 #define ATH6KL_TIME_QUANTUM 10  /* in ms */
0030 
0031 static int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req,
0032                       bool from_dma)
0033 {
0034     u8 *buf;
0035     int i;
0036 
0037     buf = req->virt_dma_buf;
0038 
0039     for (i = 0; i < req->scat_entries; i++) {
0040         if (from_dma)
0041             memcpy(req->scat_list[i].buf, buf,
0042                    req->scat_list[i].len);
0043         else
0044             memcpy(buf, req->scat_list[i].buf,
0045                    req->scat_list[i].len);
0046 
0047         buf += req->scat_list[i].len;
0048     }
0049 
0050     return 0;
0051 }
0052 
0053 int ath6kl_hif_rw_comp_handler(void *context, int status)
0054 {
0055     struct htc_packet *packet = context;
0056 
0057     ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n",
0058            packet, status);
0059 
0060     packet->status = status;
0061     packet->completion(packet->context, packet);
0062 
0063     return 0;
0064 }
0065 EXPORT_SYMBOL(ath6kl_hif_rw_comp_handler);
0066 
0067 #define REGISTER_DUMP_COUNT     60
0068 #define REGISTER_DUMP_LEN_MAX   60
0069 
0070 static void ath6kl_hif_dump_fw_crash(struct ath6kl *ar)
0071 {
0072     __le32 regdump_val[REGISTER_DUMP_LEN_MAX];
0073     u32 i, address, regdump_addr = 0;
0074     int ret;
0075 
0076     /* the reg dump pointer is copied to the host interest area */
0077     address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state));
0078     address = TARG_VTOP(ar->target_type, address);
0079 
0080     /* read RAM location through diagnostic window */
0081     ret = ath6kl_diag_read32(ar, address, &regdump_addr);
0082 
0083     if (ret || !regdump_addr) {
0084         ath6kl_warn("failed to get ptr to register dump area: %d\n",
0085                 ret);
0086         return;
0087     }
0088 
0089     ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n",
0090            regdump_addr);
0091     regdump_addr = TARG_VTOP(ar->target_type, regdump_addr);
0092 
0093     /* fetch register dump data */
0094     ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)&regdump_val[0],
0095                   REGISTER_DUMP_COUNT * (sizeof(u32)));
0096     if (ret) {
0097         ath6kl_warn("failed to get register dump: %d\n", ret);
0098         return;
0099     }
0100 
0101     ath6kl_info("crash dump:\n");
0102     ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version,
0103             ar->wiphy->fw_version);
0104 
0105     BUILD_BUG_ON(REGISTER_DUMP_COUNT % 4);
0106 
0107     for (i = 0; i < REGISTER_DUMP_COUNT; i += 4) {
0108         ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n",
0109                 i,
0110                 le32_to_cpu(regdump_val[i]),
0111                 le32_to_cpu(regdump_val[i + 1]),
0112                 le32_to_cpu(regdump_val[i + 2]),
0113                 le32_to_cpu(regdump_val[i + 3]));
0114     }
0115 }
0116 
0117 static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev)
0118 {
0119     u32 dummy;
0120     int ret;
0121 
0122     ath6kl_warn("firmware crashed\n");
0123 
0124     /*
0125      * read counter to clear the interrupt, the debug error interrupt is
0126      * counter 0.
0127      */
0128     ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS,
0129                      (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC);
0130     if (ret)
0131         ath6kl_warn("Failed to clear debug interrupt: %d\n", ret);
0132 
0133     ath6kl_hif_dump_fw_crash(dev->ar);
0134     ath6kl_read_fwlogs(dev->ar);
0135     ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT);
0136 
0137     return ret;
0138 }
0139 
0140 /* mailbox recv message polling */
0141 int ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd,
0142                   int timeout)
0143 {
0144     struct ath6kl_irq_proc_registers *rg;
0145     int status = 0, i;
0146     u8 htc_mbox = 1 << HTC_MAILBOX;
0147 
0148     for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) {
0149         /* this is the standard HIF way, load the reg table */
0150         status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
0151                          (u8 *) &dev->irq_proc_reg,
0152                          sizeof(dev->irq_proc_reg),
0153                          HIF_RD_SYNC_BYTE_INC);
0154 
0155         if (status) {
0156             ath6kl_err("failed to read reg table\n");
0157             return status;
0158         }
0159 
0160         /* check for MBOX data and valid lookahead */
0161         if (dev->irq_proc_reg.host_int_status & htc_mbox) {
0162             if (dev->irq_proc_reg.rx_lkahd_valid &
0163                 htc_mbox) {
0164                 /*
0165                  * Mailbox has a message and the look ahead
0166                  * is valid.
0167                  */
0168                 rg = &dev->irq_proc_reg;
0169                 *lk_ahd =
0170                     le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
0171                 break;
0172             }
0173         }
0174 
0175         /* delay a little  */
0176         mdelay(ATH6KL_TIME_QUANTUM);
0177         ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i);
0178     }
0179 
0180     if (i == 0) {
0181         ath6kl_err("timeout waiting for recv message\n");
0182         status = -ETIME;
0183         /* check if the target asserted */
0184         if (dev->irq_proc_reg.counter_int_status &
0185             ATH6KL_TARGET_DEBUG_INTR_MASK)
0186             /*
0187              * Target failure handler will be called in case of
0188              * an assert.
0189              */
0190             ath6kl_hif_proc_dbg_intr(dev);
0191     }
0192 
0193     return status;
0194 }
0195 
0196 /*
0197  * Disable packet reception (used in case the host runs out of buffers)
0198  * using the interrupt enable registers through the host I/F
0199  */
0200 int ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx)
0201 {
0202     struct ath6kl_irq_enable_reg regs;
0203     int status = 0;
0204 
0205     ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n",
0206            enable_rx ? "enable" : "disable");
0207 
0208     /* take the lock to protect interrupt enable shadows */
0209     spin_lock_bh(&dev->lock);
0210 
0211     if (enable_rx)
0212         dev->irq_en_reg.int_status_en |=
0213             SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
0214     else
0215         dev->irq_en_reg.int_status_en &=
0216             ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
0217 
0218     memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
0219 
0220     spin_unlock_bh(&dev->lock);
0221 
0222     status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
0223                      &regs.int_status_en,
0224                      sizeof(struct ath6kl_irq_enable_reg),
0225                      HIF_WR_SYNC_BYTE_INC);
0226 
0227     return status;
0228 }
0229 
0230 int ath6kl_hif_submit_scat_req(struct ath6kl_device *dev,
0231                   struct hif_scatter_req *scat_req, bool read)
0232 {
0233     int status = 0;
0234 
0235     if (read) {
0236         scat_req->req = HIF_RD_SYNC_BLOCK_FIX;
0237         scat_req->addr = dev->ar->mbox_info.htc_addr;
0238     } else {
0239         scat_req->req = HIF_WR_ASYNC_BLOCK_INC;
0240 
0241         scat_req->addr =
0242             (scat_req->len > HIF_MBOX_WIDTH) ?
0243             dev->ar->mbox_info.htc_ext_addr :
0244             dev->ar->mbox_info.htc_addr;
0245     }
0246 
0247     ath6kl_dbg(ATH6KL_DBG_HIF,
0248            "hif submit scatter request entries %d len %d mbox 0x%x %s %s\n",
0249            scat_req->scat_entries, scat_req->len,
0250            scat_req->addr, !read ? "async" : "sync",
0251            (read) ? "rd" : "wr");
0252 
0253     if (!read && scat_req->virt_scat) {
0254         status = ath6kl_hif_cp_scat_dma_buf(scat_req, false);
0255         if (status) {
0256             scat_req->status = status;
0257             scat_req->complete(dev->ar->htc_target, scat_req);
0258             return 0;
0259         }
0260     }
0261 
0262     status = ath6kl_hif_scat_req_rw(dev->ar, scat_req);
0263 
0264     if (read) {
0265         /* in sync mode, we can touch the scatter request */
0266         scat_req->status = status;
0267         if (!status && scat_req->virt_scat)
0268             scat_req->status =
0269                 ath6kl_hif_cp_scat_dma_buf(scat_req, true);
0270     }
0271 
0272     return status;
0273 }
0274 
0275 static int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev)
0276 {
0277     u8 counter_int_status;
0278 
0279     ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n");
0280 
0281     counter_int_status = dev->irq_proc_reg.counter_int_status &
0282                  dev->irq_en_reg.cntr_int_status_en;
0283 
0284     ath6kl_dbg(ATH6KL_DBG_IRQ,
0285            "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n",
0286         counter_int_status);
0287 
0288     /*
0289      * NOTE: other modules like GMBOX may use the counter interrupt for
0290      * credit flow control on other counters, we only need to check for
0291      * the debug assertion counter interrupt.
0292      */
0293     if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK)
0294         return ath6kl_hif_proc_dbg_intr(dev);
0295 
0296     return 0;
0297 }
0298 
0299 static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev)
0300 {
0301     int status;
0302     u8 error_int_status;
0303     u8 reg_buf[4];
0304 
0305     ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n");
0306 
0307     error_int_status = dev->irq_proc_reg.error_int_status & 0x0F;
0308     if (!error_int_status) {
0309         WARN_ON(1);
0310         return -EIO;
0311     }
0312 
0313     ath6kl_dbg(ATH6KL_DBG_IRQ,
0314            "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n",
0315            error_int_status);
0316 
0317     if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status))
0318         ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n");
0319 
0320     if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status))
0321         ath6kl_err("rx underflow\n");
0322 
0323     if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status))
0324         ath6kl_err("tx overflow\n");
0325 
0326     /* Clear the interrupt */
0327     dev->irq_proc_reg.error_int_status &= ~error_int_status;
0328 
0329     /* set W1C value to clear the interrupt, this hits the register first */
0330     reg_buf[0] = error_int_status;
0331     reg_buf[1] = 0;
0332     reg_buf[2] = 0;
0333     reg_buf[3] = 0;
0334 
0335     status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS,
0336                      reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
0337 
0338     WARN_ON(status);
0339 
0340     return status;
0341 }
0342 
0343 static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev)
0344 {
0345     int status;
0346     u8 cpu_int_status;
0347     u8 reg_buf[4];
0348 
0349     ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n");
0350 
0351     cpu_int_status = dev->irq_proc_reg.cpu_int_status &
0352              dev->irq_en_reg.cpu_int_status_en;
0353     if (!cpu_int_status) {
0354         WARN_ON(1);
0355         return -EIO;
0356     }
0357 
0358     ath6kl_dbg(ATH6KL_DBG_IRQ,
0359            "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n",
0360         cpu_int_status);
0361 
0362     /* Clear the interrupt */
0363     dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status;
0364 
0365     /*
0366      * Set up the register transfer buffer to hit the register 4 times ,
0367      * this is done to make the access 4-byte aligned to mitigate issues
0368      * with host bus interconnects that restrict bus transfer lengths to
0369      * be a multiple of 4-bytes.
0370      */
0371 
0372     /* set W1C value to clear the interrupt, this hits the register first */
0373     reg_buf[0] = cpu_int_status;
0374     /* the remaining are set to zero which have no-effect  */
0375     reg_buf[1] = 0;
0376     reg_buf[2] = 0;
0377     reg_buf[3] = 0;
0378 
0379     status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS,
0380                      reg_buf, 4, HIF_WR_SYNC_BYTE_FIX);
0381 
0382     WARN_ON(status);
0383 
0384     return status;
0385 }
0386 
0387 /* process pending interrupts synchronously */
0388 static int proc_pending_irqs(struct ath6kl_device *dev, bool *done)
0389 {
0390     struct ath6kl_irq_proc_registers *rg;
0391     int status = 0;
0392     u8 host_int_status = 0;
0393     u32 lk_ahd = 0;
0394     u8 htc_mbox = 1 << HTC_MAILBOX;
0395 
0396     ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev);
0397 
0398     /*
0399      * NOTE: HIF implementation guarantees that the context of this
0400      * call allows us to perform SYNCHRONOUS I/O, that is we can block,
0401      * sleep or call any API that can block or switch thread/task
0402      * contexts. This is a fully schedulable context.
0403      */
0404 
0405     /*
0406      * Process pending intr only when int_status_en is clear, it may
0407      * result in unnecessary bus transaction otherwise. Target may be
0408      * unresponsive at the time.
0409      */
0410     if (dev->irq_en_reg.int_status_en) {
0411         /*
0412          * Read the first 28 bytes of the HTC register table. This
0413          * will yield us the value of different int status
0414          * registers and the lookahead registers.
0415          *
0416          *    length = sizeof(int_status) + sizeof(cpu_int_status)
0417          *             + sizeof(error_int_status) +
0418          *             sizeof(counter_int_status) +
0419          *             sizeof(mbox_frame) + sizeof(rx_lkahd_valid)
0420          *             + sizeof(hole) + sizeof(rx_lkahd) +
0421          *             sizeof(int_status_en) +
0422          *             sizeof(cpu_int_status_en) +
0423          *             sizeof(err_int_status_en) +
0424          *             sizeof(cntr_int_status_en);
0425          */
0426         status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS,
0427                          (u8 *) &dev->irq_proc_reg,
0428                          sizeof(dev->irq_proc_reg),
0429                          HIF_RD_SYNC_BYTE_INC);
0430         if (status)
0431             goto out;
0432 
0433         ath6kl_dump_registers(dev, &dev->irq_proc_reg,
0434                       &dev->irq_en_reg);
0435         trace_ath6kl_sdio_irq(&dev->irq_en_reg,
0436                       sizeof(dev->irq_en_reg));
0437 
0438         /* Update only those registers that are enabled */
0439         host_int_status = dev->irq_proc_reg.host_int_status &
0440                   dev->irq_en_reg.int_status_en;
0441 
0442         /* Look at mbox status */
0443         if (host_int_status & htc_mbox) {
0444             /*
0445              * Mask out pending mbox value, we use "lookAhead as
0446              * the real flag for mbox processing.
0447              */
0448             host_int_status &= ~htc_mbox;
0449             if (dev->irq_proc_reg.rx_lkahd_valid &
0450                 htc_mbox) {
0451                 rg = &dev->irq_proc_reg;
0452                 lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]);
0453                 if (!lk_ahd)
0454                     ath6kl_err("lookAhead is zero!\n");
0455             }
0456         }
0457     }
0458 
0459     if (!host_int_status && !lk_ahd) {
0460         *done = true;
0461         goto out;
0462     }
0463 
0464     if (lk_ahd) {
0465         int fetched = 0;
0466 
0467         ath6kl_dbg(ATH6KL_DBG_IRQ,
0468                "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd);
0469         /*
0470          * Mailbox Interrupt, the HTC layer may issue async
0471          * requests to empty the mailbox. When emptying the recv
0472          * mailbox we use the async handler above called from the
0473          * completion routine of the callers read request. This can
0474          * improve performance by reducing context switching when
0475          * we rapidly pull packets.
0476          */
0477         status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt,
0478                               lk_ahd, &fetched);
0479         if (status)
0480             goto out;
0481 
0482         if (!fetched)
0483             /*
0484              * HTC could not pull any messages out due to lack
0485              * of resources.
0486              */
0487             dev->htc_cnxt->chk_irq_status_cnt = 0;
0488     }
0489 
0490     /* now handle the rest of them */
0491     ath6kl_dbg(ATH6KL_DBG_IRQ,
0492            "valid interrupt source(s) for other interrupts: 0x%x\n",
0493            host_int_status);
0494 
0495     if (MS(HOST_INT_STATUS_CPU, host_int_status)) {
0496         /* CPU Interrupt */
0497         status = ath6kl_hif_proc_cpu_intr(dev);
0498         if (status)
0499             goto out;
0500     }
0501 
0502     if (MS(HOST_INT_STATUS_ERROR, host_int_status)) {
0503         /* Error Interrupt */
0504         status = ath6kl_hif_proc_err_intr(dev);
0505         if (status)
0506             goto out;
0507     }
0508 
0509     if (MS(HOST_INT_STATUS_COUNTER, host_int_status))
0510         /* Counter Interrupt */
0511         status = ath6kl_hif_proc_counter_intr(dev);
0512 
0513 out:
0514     /*
0515      * An optimization to bypass reading the IRQ status registers
0516      * unecessarily which can re-wake the target, if upper layers
0517      * determine that we are in a low-throughput mode, we can rely on
0518      * taking another interrupt rather than re-checking the status
0519      * registers which can re-wake the target.
0520      *
0521      * NOTE : for host interfaces that makes use of detecting pending
0522      * mbox messages at hif can not use this optimization due to
0523      * possible side effects, SPI requires the host to drain all
0524      * messages from the mailbox before exiting the ISR routine.
0525      */
0526 
0527     ath6kl_dbg(ATH6KL_DBG_IRQ,
0528            "bypassing irq status re-check, forcing done\n");
0529 
0530     if (!dev->htc_cnxt->chk_irq_status_cnt)
0531         *done = true;
0532 
0533     ath6kl_dbg(ATH6KL_DBG_IRQ,
0534            "proc_pending_irqs: (done:%d, status=%d\n", *done, status);
0535 
0536     return status;
0537 }
0538 
0539 /* interrupt handler, kicks off all interrupt processing */
0540 int ath6kl_hif_intr_bh_handler(struct ath6kl *ar)
0541 {
0542     struct ath6kl_device *dev = ar->htc_target->dev;
0543     unsigned long timeout;
0544     int status = 0;
0545     bool done = false;
0546 
0547     /*
0548      * Reset counter used to flag a re-scan of IRQ status registers on
0549      * the target.
0550      */
0551     dev->htc_cnxt->chk_irq_status_cnt = 0;
0552 
0553     /*
0554      * IRQ processing is synchronous, interrupt status registers can be
0555      * re-read.
0556      */
0557     timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT);
0558     while (time_before(jiffies, timeout) && !done) {
0559         status = proc_pending_irqs(dev, &done);
0560         if (status)
0561             break;
0562     }
0563 
0564     return status;
0565 }
0566 EXPORT_SYMBOL(ath6kl_hif_intr_bh_handler);
0567 
0568 static int ath6kl_hif_enable_intrs(struct ath6kl_device *dev)
0569 {
0570     struct ath6kl_irq_enable_reg regs;
0571     int status;
0572 
0573     spin_lock_bh(&dev->lock);
0574 
0575     /* Enable all but ATH6KL CPU interrupts */
0576     dev->irq_en_reg.int_status_en =
0577             SM(INT_STATUS_ENABLE_ERROR, 0x01) |
0578             SM(INT_STATUS_ENABLE_CPU, 0x01) |
0579             SM(INT_STATUS_ENABLE_COUNTER, 0x01);
0580 
0581     /*
0582      * NOTE: There are some cases where HIF can do detection of
0583      * pending mbox messages which is disabled now.
0584      */
0585     dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01);
0586 
0587     /* Set up the CPU Interrupt status Register */
0588     dev->irq_en_reg.cpu_int_status_en = 0;
0589 
0590     /* Set up the Error Interrupt status Register */
0591     dev->irq_en_reg.err_int_status_en =
0592         SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) |
0593         SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1);
0594 
0595     /*
0596      * Enable Counter interrupt status register to get fatal errors for
0597      * debugging.
0598      */
0599     dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT,
0600                         ATH6KL_TARGET_DEBUG_INTR_MASK);
0601     memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
0602 
0603     spin_unlock_bh(&dev->lock);
0604 
0605     status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
0606                      &regs.int_status_en, sizeof(regs),
0607                      HIF_WR_SYNC_BYTE_INC);
0608 
0609     if (status)
0610         ath6kl_err("failed to update interrupt ctl reg err: %d\n",
0611                status);
0612 
0613     return status;
0614 }
0615 
0616 int ath6kl_hif_disable_intrs(struct ath6kl_device *dev)
0617 {
0618     struct ath6kl_irq_enable_reg regs;
0619 
0620     spin_lock_bh(&dev->lock);
0621     /* Disable all interrupts */
0622     dev->irq_en_reg.int_status_en = 0;
0623     dev->irq_en_reg.cpu_int_status_en = 0;
0624     dev->irq_en_reg.err_int_status_en = 0;
0625     dev->irq_en_reg.cntr_int_status_en = 0;
0626     memcpy(&regs, &dev->irq_en_reg, sizeof(regs));
0627     spin_unlock_bh(&dev->lock);
0628 
0629     return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS,
0630                    &regs.int_status_en, sizeof(regs),
0631                    HIF_WR_SYNC_BYTE_INC);
0632 }
0633 
0634 /* enable device interrupts */
0635 int ath6kl_hif_unmask_intrs(struct ath6kl_device *dev)
0636 {
0637     int status = 0;
0638 
0639     /*
0640      * Make sure interrupt are disabled before unmasking at the HIF
0641      * layer. The rationale here is that between device insertion
0642      * (where we clear the interrupts the first time) and when HTC
0643      * is finally ready to handle interrupts, other software can perform
0644      * target "soft" resets. The ATH6KL interrupt enables reset back to an
0645      * "enabled" state when this happens.
0646      */
0647     ath6kl_hif_disable_intrs(dev);
0648 
0649     /* unmask the host controller interrupts */
0650     ath6kl_hif_irq_enable(dev->ar);
0651     status = ath6kl_hif_enable_intrs(dev);
0652 
0653     return status;
0654 }
0655 
0656 /* disable all device interrupts */
0657 int ath6kl_hif_mask_intrs(struct ath6kl_device *dev)
0658 {
0659     /*
0660      * Mask the interrupt at the HIF layer to avoid any stray interrupt
0661      * taken while we zero out our shadow registers in
0662      * ath6kl_hif_disable_intrs().
0663      */
0664     ath6kl_hif_irq_disable(dev->ar);
0665 
0666     return ath6kl_hif_disable_intrs(dev);
0667 }
0668 
0669 int ath6kl_hif_setup(struct ath6kl_device *dev)
0670 {
0671     int status = 0;
0672 
0673     spin_lock_init(&dev->lock);
0674 
0675     /*
0676      * NOTE: we actually get the block size of a mailbox other than 0,
0677      * for SDIO the block size on mailbox 0 is artificially set to 1.
0678      * So we use the block size that is set for the other 3 mailboxes.
0679      */
0680     dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size;
0681 
0682     /* must be a power of 2 */
0683     if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) {
0684         WARN_ON(1);
0685         status = -EINVAL;
0686         goto fail_setup;
0687     }
0688 
0689     /* assemble mask, used for padding to a block */
0690     dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1;
0691 
0692     ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n",
0693            dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr);
0694 
0695     status = ath6kl_hif_disable_intrs(dev);
0696 
0697 fail_setup:
0698     return status;
0699 }