18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2007-2011 Atheros Communications Inc. 38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 68c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 78c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 108c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 118c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 128c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 138c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 158c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci#include "hif.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/export.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "core.h" 228c2ecf20Sopenharmony_ci#include "target.h" 238c2ecf20Sopenharmony_ci#include "hif-ops.h" 248c2ecf20Sopenharmony_ci#include "debug.h" 258c2ecf20Sopenharmony_ci#include "trace.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define MAILBOX_FOR_BLOCK_SIZE 1 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define ATH6KL_TIME_QUANTUM 10 /* in ms */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic int ath6kl_hif_cp_scat_dma_buf(struct hif_scatter_req *req, 328c2ecf20Sopenharmony_ci bool from_dma) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci u8 *buf; 358c2ecf20Sopenharmony_ci int i; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci buf = req->virt_dma_buf; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci for (i = 0; i < req->scat_entries; i++) { 408c2ecf20Sopenharmony_ci if (from_dma) 418c2ecf20Sopenharmony_ci memcpy(req->scat_list[i].buf, buf, 428c2ecf20Sopenharmony_ci req->scat_list[i].len); 438c2ecf20Sopenharmony_ci else 448c2ecf20Sopenharmony_ci memcpy(buf, req->scat_list[i].buf, 458c2ecf20Sopenharmony_ci req->scat_list[i].len); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci buf += req->scat_list[i].len; 488c2ecf20Sopenharmony_ci } 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci return 0; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ciint ath6kl_hif_rw_comp_handler(void *context, int status) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct htc_packet *packet = context; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HIF, "hif rw completion pkt 0x%p status %d\n", 588c2ecf20Sopenharmony_ci packet, status); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci packet->status = status; 618c2ecf20Sopenharmony_ci packet->completion(packet->context, packet); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return 0; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath6kl_hif_rw_comp_handler); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define REGISTER_DUMP_COUNT 60 688c2ecf20Sopenharmony_ci#define REGISTER_DUMP_LEN_MAX 60 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic void ath6kl_hif_dump_fw_crash(struct ath6kl *ar) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci __le32 regdump_val[REGISTER_DUMP_LEN_MAX]; 738c2ecf20Sopenharmony_ci u32 i, address, regdump_addr = 0; 748c2ecf20Sopenharmony_ci int ret; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci /* the reg dump pointer is copied to the host interest area */ 778c2ecf20Sopenharmony_ci address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); 788c2ecf20Sopenharmony_ci address = TARG_VTOP(ar->target_type, address); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* read RAM location through diagnostic window */ 818c2ecf20Sopenharmony_ci ret = ath6kl_diag_read32(ar, address, ®dump_addr); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (ret || !regdump_addr) { 848c2ecf20Sopenharmony_ci ath6kl_warn("failed to get ptr to register dump area: %d\n", 858c2ecf20Sopenharmony_ci ret); 868c2ecf20Sopenharmony_ci return; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, "register dump data address 0x%x\n", 908c2ecf20Sopenharmony_ci regdump_addr); 918c2ecf20Sopenharmony_ci regdump_addr = TARG_VTOP(ar->target_type, regdump_addr); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* fetch register dump data */ 948c2ecf20Sopenharmony_ci ret = ath6kl_diag_read(ar, regdump_addr, (u8 *)®dump_val[0], 958c2ecf20Sopenharmony_ci REGISTER_DUMP_COUNT * (sizeof(u32))); 968c2ecf20Sopenharmony_ci if (ret) { 978c2ecf20Sopenharmony_ci ath6kl_warn("failed to get register dump: %d\n", ret); 988c2ecf20Sopenharmony_ci return; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci ath6kl_info("crash dump:\n"); 1028c2ecf20Sopenharmony_ci ath6kl_info("hw 0x%x fw %s\n", ar->wiphy->hw_version, 1038c2ecf20Sopenharmony_ci ar->wiphy->fw_version); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci BUILD_BUG_ON(REGISTER_DUMP_COUNT % 4); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci for (i = 0; i < REGISTER_DUMP_COUNT; i += 4) { 1088c2ecf20Sopenharmony_ci ath6kl_info("%d: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", 1098c2ecf20Sopenharmony_ci i, 1108c2ecf20Sopenharmony_ci le32_to_cpu(regdump_val[i]), 1118c2ecf20Sopenharmony_ci le32_to_cpu(regdump_val[i + 1]), 1128c2ecf20Sopenharmony_ci le32_to_cpu(regdump_val[i + 2]), 1138c2ecf20Sopenharmony_ci le32_to_cpu(regdump_val[i + 3])); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u32 dummy; 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ath6kl_warn("firmware crashed\n"); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* 1258c2ecf20Sopenharmony_ci * read counter to clear the interrupt, the debug error interrupt is 1268c2ecf20Sopenharmony_ci * counter 0. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci ret = hif_read_write_sync(dev->ar, COUNT_DEC_ADDRESS, 1298c2ecf20Sopenharmony_ci (u8 *)&dummy, 4, HIF_RD_SYNC_BYTE_INC); 1308c2ecf20Sopenharmony_ci if (ret) 1318c2ecf20Sopenharmony_ci ath6kl_warn("Failed to clear debug interrupt: %d\n", ret); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ath6kl_hif_dump_fw_crash(dev->ar); 1348c2ecf20Sopenharmony_ci ath6kl_read_fwlogs(dev->ar); 1358c2ecf20Sopenharmony_ci ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci return ret; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* mailbox recv message polling */ 1418c2ecf20Sopenharmony_ciint ath6kl_hif_poll_mboxmsg_rx(struct ath6kl_device *dev, u32 *lk_ahd, 1428c2ecf20Sopenharmony_ci int timeout) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct ath6kl_irq_proc_registers *rg; 1458c2ecf20Sopenharmony_ci int status = 0, i; 1468c2ecf20Sopenharmony_ci u8 htc_mbox = 1 << HTC_MAILBOX; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = timeout / ATH6KL_TIME_QUANTUM; i > 0; i--) { 1498c2ecf20Sopenharmony_ci /* this is the standard HIF way, load the reg table */ 1508c2ecf20Sopenharmony_ci status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, 1518c2ecf20Sopenharmony_ci (u8 *) &dev->irq_proc_reg, 1528c2ecf20Sopenharmony_ci sizeof(dev->irq_proc_reg), 1538c2ecf20Sopenharmony_ci HIF_RD_SYNC_BYTE_INC); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (status) { 1568c2ecf20Sopenharmony_ci ath6kl_err("failed to read reg table\n"); 1578c2ecf20Sopenharmony_ci return status; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* check for MBOX data and valid lookahead */ 1618c2ecf20Sopenharmony_ci if (dev->irq_proc_reg.host_int_status & htc_mbox) { 1628c2ecf20Sopenharmony_ci if (dev->irq_proc_reg.rx_lkahd_valid & 1638c2ecf20Sopenharmony_ci htc_mbox) { 1648c2ecf20Sopenharmony_ci /* 1658c2ecf20Sopenharmony_ci * Mailbox has a message and the look ahead 1668c2ecf20Sopenharmony_ci * is valid. 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci rg = &dev->irq_proc_reg; 1698c2ecf20Sopenharmony_ci *lk_ahd = 1708c2ecf20Sopenharmony_ci le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* delay a little */ 1768c2ecf20Sopenharmony_ci mdelay(ATH6KL_TIME_QUANTUM); 1778c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HIF, "hif retry mbox poll try %d\n", i); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (i == 0) { 1818c2ecf20Sopenharmony_ci ath6kl_err("timeout waiting for recv message\n"); 1828c2ecf20Sopenharmony_ci status = -ETIME; 1838c2ecf20Sopenharmony_ci /* check if the target asserted */ 1848c2ecf20Sopenharmony_ci if (dev->irq_proc_reg.counter_int_status & 1858c2ecf20Sopenharmony_ci ATH6KL_TARGET_DEBUG_INTR_MASK) 1868c2ecf20Sopenharmony_ci /* 1878c2ecf20Sopenharmony_ci * Target failure handler will be called in case of 1888c2ecf20Sopenharmony_ci * an assert. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_ci ath6kl_hif_proc_dbg_intr(dev); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return status; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * Disable packet reception (used in case the host runs out of buffers) 1988c2ecf20Sopenharmony_ci * using the interrupt enable registers through the host I/F 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_ciint ath6kl_hif_rx_control(struct ath6kl_device *dev, bool enable_rx) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct ath6kl_irq_enable_reg regs; 2038c2ecf20Sopenharmony_ci int status = 0; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HIF, "hif rx %s\n", 2068c2ecf20Sopenharmony_ci enable_rx ? "enable" : "disable"); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* take the lock to protect interrupt enable shadows */ 2098c2ecf20Sopenharmony_ci spin_lock_bh(&dev->lock); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (enable_rx) 2128c2ecf20Sopenharmony_ci dev->irq_en_reg.int_status_en |= 2138c2ecf20Sopenharmony_ci SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); 2148c2ecf20Sopenharmony_ci else 2158c2ecf20Sopenharmony_ci dev->irq_en_reg.int_status_en &= 2168c2ecf20Sopenharmony_ci ~SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci memcpy(®s, &dev->irq_en_reg, sizeof(regs)); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci spin_unlock_bh(&dev->lock); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, 2238c2ecf20Sopenharmony_ci ®s.int_status_en, 2248c2ecf20Sopenharmony_ci sizeof(struct ath6kl_irq_enable_reg), 2258c2ecf20Sopenharmony_ci HIF_WR_SYNC_BYTE_INC); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return status; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ciint ath6kl_hif_submit_scat_req(struct ath6kl_device *dev, 2318c2ecf20Sopenharmony_ci struct hif_scatter_req *scat_req, bool read) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci int status = 0; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (read) { 2368c2ecf20Sopenharmony_ci scat_req->req = HIF_RD_SYNC_BLOCK_FIX; 2378c2ecf20Sopenharmony_ci scat_req->addr = dev->ar->mbox_info.htc_addr; 2388c2ecf20Sopenharmony_ci } else { 2398c2ecf20Sopenharmony_ci scat_req->req = HIF_WR_ASYNC_BLOCK_INC; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci scat_req->addr = 2428c2ecf20Sopenharmony_ci (scat_req->len > HIF_MBOX_WIDTH) ? 2438c2ecf20Sopenharmony_ci dev->ar->mbox_info.htc_ext_addr : 2448c2ecf20Sopenharmony_ci dev->ar->mbox_info.htc_addr; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HIF, 2488c2ecf20Sopenharmony_ci "hif submit scatter request entries %d len %d mbox 0x%x %s %s\n", 2498c2ecf20Sopenharmony_ci scat_req->scat_entries, scat_req->len, 2508c2ecf20Sopenharmony_ci scat_req->addr, !read ? "async" : "sync", 2518c2ecf20Sopenharmony_ci (read) ? "rd" : "wr"); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!read && scat_req->virt_scat) { 2548c2ecf20Sopenharmony_ci status = ath6kl_hif_cp_scat_dma_buf(scat_req, false); 2558c2ecf20Sopenharmony_ci if (status) { 2568c2ecf20Sopenharmony_ci scat_req->status = status; 2578c2ecf20Sopenharmony_ci scat_req->complete(dev->ar->htc_target, scat_req); 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci status = ath6kl_hif_scat_req_rw(dev->ar, scat_req); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (read) { 2658c2ecf20Sopenharmony_ci /* in sync mode, we can touch the scatter request */ 2668c2ecf20Sopenharmony_ci scat_req->status = status; 2678c2ecf20Sopenharmony_ci if (!status && scat_req->virt_scat) 2688c2ecf20Sopenharmony_ci scat_req->status = 2698c2ecf20Sopenharmony_ci ath6kl_hif_cp_scat_dma_buf(scat_req, true); 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return status; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int ath6kl_hif_proc_counter_intr(struct ath6kl_device *dev) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci u8 counter_int_status; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, "counter interrupt\n"); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci counter_int_status = dev->irq_proc_reg.counter_int_status & 2828c2ecf20Sopenharmony_ci dev->irq_en_reg.cntr_int_status_en; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, 2858c2ecf20Sopenharmony_ci "valid interrupt source(s) in COUNTER_INT_STATUS: 0x%x\n", 2868c2ecf20Sopenharmony_ci counter_int_status); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* 2898c2ecf20Sopenharmony_ci * NOTE: other modules like GMBOX may use the counter interrupt for 2908c2ecf20Sopenharmony_ci * credit flow control on other counters, we only need to check for 2918c2ecf20Sopenharmony_ci * the debug assertion counter interrupt. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci if (counter_int_status & ATH6KL_TARGET_DEBUG_INTR_MASK) 2948c2ecf20Sopenharmony_ci return ath6kl_hif_proc_dbg_intr(dev); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci int status; 3028c2ecf20Sopenharmony_ci u8 error_int_status; 3038c2ecf20Sopenharmony_ci u8 reg_buf[4]; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, "error interrupt\n"); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci error_int_status = dev->irq_proc_reg.error_int_status & 0x0F; 3088c2ecf20Sopenharmony_ci if (!error_int_status) { 3098c2ecf20Sopenharmony_ci WARN_ON(1); 3108c2ecf20Sopenharmony_ci return -EIO; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, 3148c2ecf20Sopenharmony_ci "valid interrupt source(s) in ERROR_INT_STATUS: 0x%x\n", 3158c2ecf20Sopenharmony_ci error_int_status); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (MS(ERROR_INT_STATUS_WAKEUP, error_int_status)) 3188c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, "error : wakeup\n"); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (MS(ERROR_INT_STATUS_RX_UNDERFLOW, error_int_status)) 3218c2ecf20Sopenharmony_ci ath6kl_err("rx underflow\n"); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (MS(ERROR_INT_STATUS_TX_OVERFLOW, error_int_status)) 3248c2ecf20Sopenharmony_ci ath6kl_err("tx overflow\n"); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Clear the interrupt */ 3278c2ecf20Sopenharmony_ci dev->irq_proc_reg.error_int_status &= ~error_int_status; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* set W1C value to clear the interrupt, this hits the register first */ 3308c2ecf20Sopenharmony_ci reg_buf[0] = error_int_status; 3318c2ecf20Sopenharmony_ci reg_buf[1] = 0; 3328c2ecf20Sopenharmony_ci reg_buf[2] = 0; 3338c2ecf20Sopenharmony_ci reg_buf[3] = 0; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS, 3368c2ecf20Sopenharmony_ci reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci WARN_ON(status); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return status; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci int status; 3468c2ecf20Sopenharmony_ci u8 cpu_int_status; 3478c2ecf20Sopenharmony_ci u8 reg_buf[4]; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, "cpu interrupt\n"); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci cpu_int_status = dev->irq_proc_reg.cpu_int_status & 3528c2ecf20Sopenharmony_ci dev->irq_en_reg.cpu_int_status_en; 3538c2ecf20Sopenharmony_ci if (!cpu_int_status) { 3548c2ecf20Sopenharmony_ci WARN_ON(1); 3558c2ecf20Sopenharmony_ci return -EIO; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, 3598c2ecf20Sopenharmony_ci "valid interrupt source(s) in CPU_INT_STATUS: 0x%x\n", 3608c2ecf20Sopenharmony_ci cpu_int_status); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Clear the interrupt */ 3638c2ecf20Sopenharmony_ci dev->irq_proc_reg.cpu_int_status &= ~cpu_int_status; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci /* 3668c2ecf20Sopenharmony_ci * Set up the register transfer buffer to hit the register 4 times , 3678c2ecf20Sopenharmony_ci * this is done to make the access 4-byte aligned to mitigate issues 3688c2ecf20Sopenharmony_ci * with host bus interconnects that restrict bus transfer lengths to 3698c2ecf20Sopenharmony_ci * be a multiple of 4-bytes. 3708c2ecf20Sopenharmony_ci */ 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* set W1C value to clear the interrupt, this hits the register first */ 3738c2ecf20Sopenharmony_ci reg_buf[0] = cpu_int_status; 3748c2ecf20Sopenharmony_ci /* the remaining are set to zero which have no-effect */ 3758c2ecf20Sopenharmony_ci reg_buf[1] = 0; 3768c2ecf20Sopenharmony_ci reg_buf[2] = 0; 3778c2ecf20Sopenharmony_ci reg_buf[3] = 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS, 3808c2ecf20Sopenharmony_ci reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci WARN_ON(status); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci return status; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* process pending interrupts synchronously */ 3888c2ecf20Sopenharmony_cistatic int proc_pending_irqs(struct ath6kl_device *dev, bool *done) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct ath6kl_irq_proc_registers *rg; 3918c2ecf20Sopenharmony_ci int status = 0; 3928c2ecf20Sopenharmony_ci u8 host_int_status = 0; 3938c2ecf20Sopenharmony_ci u32 lk_ahd = 0; 3948c2ecf20Sopenharmony_ci u8 htc_mbox = 1 << HTC_MAILBOX; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, "proc_pending_irqs: (dev: 0x%p)\n", dev); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* 3998c2ecf20Sopenharmony_ci * NOTE: HIF implementation guarantees that the context of this 4008c2ecf20Sopenharmony_ci * call allows us to perform SYNCHRONOUS I/O, that is we can block, 4018c2ecf20Sopenharmony_ci * sleep or call any API that can block or switch thread/task 4028c2ecf20Sopenharmony_ci * contexts. This is a fully schedulable context. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * Process pending intr only when int_status_en is clear, it may 4078c2ecf20Sopenharmony_ci * result in unnecessary bus transaction otherwise. Target may be 4088c2ecf20Sopenharmony_ci * unresponsive at the time. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci if (dev->irq_en_reg.int_status_en) { 4118c2ecf20Sopenharmony_ci /* 4128c2ecf20Sopenharmony_ci * Read the first 28 bytes of the HTC register table. This 4138c2ecf20Sopenharmony_ci * will yield us the value of different int status 4148c2ecf20Sopenharmony_ci * registers and the lookahead registers. 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * length = sizeof(int_status) + sizeof(cpu_int_status) 4178c2ecf20Sopenharmony_ci * + sizeof(error_int_status) + 4188c2ecf20Sopenharmony_ci * sizeof(counter_int_status) + 4198c2ecf20Sopenharmony_ci * sizeof(mbox_frame) + sizeof(rx_lkahd_valid) 4208c2ecf20Sopenharmony_ci * + sizeof(hole) + sizeof(rx_lkahd) + 4218c2ecf20Sopenharmony_ci * sizeof(int_status_en) + 4228c2ecf20Sopenharmony_ci * sizeof(cpu_int_status_en) + 4238c2ecf20Sopenharmony_ci * sizeof(err_int_status_en) + 4248c2ecf20Sopenharmony_ci * sizeof(cntr_int_status_en); 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci status = hif_read_write_sync(dev->ar, HOST_INT_STATUS_ADDRESS, 4278c2ecf20Sopenharmony_ci (u8 *) &dev->irq_proc_reg, 4288c2ecf20Sopenharmony_ci sizeof(dev->irq_proc_reg), 4298c2ecf20Sopenharmony_ci HIF_RD_SYNC_BYTE_INC); 4308c2ecf20Sopenharmony_ci if (status) 4318c2ecf20Sopenharmony_ci goto out; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ath6kl_dump_registers(dev, &dev->irq_proc_reg, 4348c2ecf20Sopenharmony_ci &dev->irq_en_reg); 4358c2ecf20Sopenharmony_ci trace_ath6kl_sdio_irq(&dev->irq_en_reg, 4368c2ecf20Sopenharmony_ci sizeof(dev->irq_en_reg)); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Update only those registers that are enabled */ 4398c2ecf20Sopenharmony_ci host_int_status = dev->irq_proc_reg.host_int_status & 4408c2ecf20Sopenharmony_ci dev->irq_en_reg.int_status_en; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Look at mbox status */ 4438c2ecf20Sopenharmony_ci if (host_int_status & htc_mbox) { 4448c2ecf20Sopenharmony_ci /* 4458c2ecf20Sopenharmony_ci * Mask out pending mbox value, we use "lookAhead as 4468c2ecf20Sopenharmony_ci * the real flag for mbox processing. 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_ci host_int_status &= ~htc_mbox; 4498c2ecf20Sopenharmony_ci if (dev->irq_proc_reg.rx_lkahd_valid & 4508c2ecf20Sopenharmony_ci htc_mbox) { 4518c2ecf20Sopenharmony_ci rg = &dev->irq_proc_reg; 4528c2ecf20Sopenharmony_ci lk_ahd = le32_to_cpu(rg->rx_lkahd[HTC_MAILBOX]); 4538c2ecf20Sopenharmony_ci if (!lk_ahd) 4548c2ecf20Sopenharmony_ci ath6kl_err("lookAhead is zero!\n"); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (!host_int_status && !lk_ahd) { 4608c2ecf20Sopenharmony_ci *done = true; 4618c2ecf20Sopenharmony_ci goto out; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (lk_ahd) { 4658c2ecf20Sopenharmony_ci int fetched = 0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, 4688c2ecf20Sopenharmony_ci "pending mailbox msg, lk_ahd: 0x%X\n", lk_ahd); 4698c2ecf20Sopenharmony_ci /* 4708c2ecf20Sopenharmony_ci * Mailbox Interrupt, the HTC layer may issue async 4718c2ecf20Sopenharmony_ci * requests to empty the mailbox. When emptying the recv 4728c2ecf20Sopenharmony_ci * mailbox we use the async handler above called from the 4738c2ecf20Sopenharmony_ci * completion routine of the callers read request. This can 4748c2ecf20Sopenharmony_ci * improve performance by reducing context switching when 4758c2ecf20Sopenharmony_ci * we rapidly pull packets. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci status = ath6kl_htc_rxmsg_pending_handler(dev->htc_cnxt, 4788c2ecf20Sopenharmony_ci lk_ahd, &fetched); 4798c2ecf20Sopenharmony_ci if (status) 4808c2ecf20Sopenharmony_ci goto out; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (!fetched) 4838c2ecf20Sopenharmony_ci /* 4848c2ecf20Sopenharmony_ci * HTC could not pull any messages out due to lack 4858c2ecf20Sopenharmony_ci * of resources. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci dev->htc_cnxt->chk_irq_status_cnt = 0; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* now handle the rest of them */ 4918c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, 4928c2ecf20Sopenharmony_ci "valid interrupt source(s) for other interrupts: 0x%x\n", 4938c2ecf20Sopenharmony_ci host_int_status); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (MS(HOST_INT_STATUS_CPU, host_int_status)) { 4968c2ecf20Sopenharmony_ci /* CPU Interrupt */ 4978c2ecf20Sopenharmony_ci status = ath6kl_hif_proc_cpu_intr(dev); 4988c2ecf20Sopenharmony_ci if (status) 4998c2ecf20Sopenharmony_ci goto out; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (MS(HOST_INT_STATUS_ERROR, host_int_status)) { 5038c2ecf20Sopenharmony_ci /* Error Interrupt */ 5048c2ecf20Sopenharmony_ci status = ath6kl_hif_proc_err_intr(dev); 5058c2ecf20Sopenharmony_ci if (status) 5068c2ecf20Sopenharmony_ci goto out; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (MS(HOST_INT_STATUS_COUNTER, host_int_status)) 5108c2ecf20Sopenharmony_ci /* Counter Interrupt */ 5118c2ecf20Sopenharmony_ci status = ath6kl_hif_proc_counter_intr(dev); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ciout: 5148c2ecf20Sopenharmony_ci /* 5158c2ecf20Sopenharmony_ci * An optimization to bypass reading the IRQ status registers 5168c2ecf20Sopenharmony_ci * unecessarily which can re-wake the target, if upper layers 5178c2ecf20Sopenharmony_ci * determine that we are in a low-throughput mode, we can rely on 5188c2ecf20Sopenharmony_ci * taking another interrupt rather than re-checking the status 5198c2ecf20Sopenharmony_ci * registers which can re-wake the target. 5208c2ecf20Sopenharmony_ci * 5218c2ecf20Sopenharmony_ci * NOTE : for host interfaces that makes use of detecting pending 5228c2ecf20Sopenharmony_ci * mbox messages at hif can not use this optimization due to 5238c2ecf20Sopenharmony_ci * possible side effects, SPI requires the host to drain all 5248c2ecf20Sopenharmony_ci * messages from the mailbox before exiting the ISR routine. 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, 5288c2ecf20Sopenharmony_ci "bypassing irq status re-check, forcing done\n"); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!dev->htc_cnxt->chk_irq_status_cnt) 5318c2ecf20Sopenharmony_ci *done = true; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_IRQ, 5348c2ecf20Sopenharmony_ci "proc_pending_irqs: (done:%d, status=%d\n", *done, status); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci return status; 5378c2ecf20Sopenharmony_ci} 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci/* interrupt handler, kicks off all interrupt processing */ 5408c2ecf20Sopenharmony_ciint ath6kl_hif_intr_bh_handler(struct ath6kl *ar) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct ath6kl_device *dev = ar->htc_target->dev; 5438c2ecf20Sopenharmony_ci unsigned long timeout; 5448c2ecf20Sopenharmony_ci int status = 0; 5458c2ecf20Sopenharmony_ci bool done = false; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* 5488c2ecf20Sopenharmony_ci * Reset counter used to flag a re-scan of IRQ status registers on 5498c2ecf20Sopenharmony_ci * the target. 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci dev->htc_cnxt->chk_irq_status_cnt = 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* 5548c2ecf20Sopenharmony_ci * IRQ processing is synchronous, interrupt status registers can be 5558c2ecf20Sopenharmony_ci * re-read. 5568c2ecf20Sopenharmony_ci */ 5578c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(ATH6KL_HIF_COMMUNICATION_TIMEOUT); 5588c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout) && !done) { 5598c2ecf20Sopenharmony_ci status = proc_pending_irqs(dev, &done); 5608c2ecf20Sopenharmony_ci if (status) 5618c2ecf20Sopenharmony_ci break; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return status; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath6kl_hif_intr_bh_handler); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int ath6kl_hif_enable_intrs(struct ath6kl_device *dev) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct ath6kl_irq_enable_reg regs; 5718c2ecf20Sopenharmony_ci int status; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci spin_lock_bh(&dev->lock); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Enable all but ATH6KL CPU interrupts */ 5768c2ecf20Sopenharmony_ci dev->irq_en_reg.int_status_en = 5778c2ecf20Sopenharmony_ci SM(INT_STATUS_ENABLE_ERROR, 0x01) | 5788c2ecf20Sopenharmony_ci SM(INT_STATUS_ENABLE_CPU, 0x01) | 5798c2ecf20Sopenharmony_ci SM(INT_STATUS_ENABLE_COUNTER, 0x01); 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* 5828c2ecf20Sopenharmony_ci * NOTE: There are some cases where HIF can do detection of 5838c2ecf20Sopenharmony_ci * pending mbox messages which is disabled now. 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ci dev->irq_en_reg.int_status_en |= SM(INT_STATUS_ENABLE_MBOX_DATA, 0x01); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Set up the CPU Interrupt status Register */ 5888c2ecf20Sopenharmony_ci dev->irq_en_reg.cpu_int_status_en = 0; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* Set up the Error Interrupt status Register */ 5918c2ecf20Sopenharmony_ci dev->irq_en_reg.err_int_status_en = 5928c2ecf20Sopenharmony_ci SM(ERROR_STATUS_ENABLE_RX_UNDERFLOW, 0x01) | 5938c2ecf20Sopenharmony_ci SM(ERROR_STATUS_ENABLE_TX_OVERFLOW, 0x1); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* 5968c2ecf20Sopenharmony_ci * Enable Counter interrupt status register to get fatal errors for 5978c2ecf20Sopenharmony_ci * debugging. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci dev->irq_en_reg.cntr_int_status_en = SM(COUNTER_INT_STATUS_ENABLE_BIT, 6008c2ecf20Sopenharmony_ci ATH6KL_TARGET_DEBUG_INTR_MASK); 6018c2ecf20Sopenharmony_ci memcpy(®s, &dev->irq_en_reg, sizeof(regs)); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci spin_unlock_bh(&dev->lock); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci status = hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, 6068c2ecf20Sopenharmony_ci ®s.int_status_en, sizeof(regs), 6078c2ecf20Sopenharmony_ci HIF_WR_SYNC_BYTE_INC); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (status) 6108c2ecf20Sopenharmony_ci ath6kl_err("failed to update interrupt ctl reg err: %d\n", 6118c2ecf20Sopenharmony_ci status); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci return status; 6148c2ecf20Sopenharmony_ci} 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ciint ath6kl_hif_disable_intrs(struct ath6kl_device *dev) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci struct ath6kl_irq_enable_reg regs; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci spin_lock_bh(&dev->lock); 6218c2ecf20Sopenharmony_ci /* Disable all interrupts */ 6228c2ecf20Sopenharmony_ci dev->irq_en_reg.int_status_en = 0; 6238c2ecf20Sopenharmony_ci dev->irq_en_reg.cpu_int_status_en = 0; 6248c2ecf20Sopenharmony_ci dev->irq_en_reg.err_int_status_en = 0; 6258c2ecf20Sopenharmony_ci dev->irq_en_reg.cntr_int_status_en = 0; 6268c2ecf20Sopenharmony_ci memcpy(®s, &dev->irq_en_reg, sizeof(regs)); 6278c2ecf20Sopenharmony_ci spin_unlock_bh(&dev->lock); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci return hif_read_write_sync(dev->ar, INT_STATUS_ENABLE_ADDRESS, 6308c2ecf20Sopenharmony_ci ®s.int_status_en, sizeof(regs), 6318c2ecf20Sopenharmony_ci HIF_WR_SYNC_BYTE_INC); 6328c2ecf20Sopenharmony_ci} 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci/* enable device interrupts */ 6358c2ecf20Sopenharmony_ciint ath6kl_hif_unmask_intrs(struct ath6kl_device *dev) 6368c2ecf20Sopenharmony_ci{ 6378c2ecf20Sopenharmony_ci int status = 0; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * Make sure interrupt are disabled before unmasking at the HIF 6418c2ecf20Sopenharmony_ci * layer. The rationale here is that between device insertion 6428c2ecf20Sopenharmony_ci * (where we clear the interrupts the first time) and when HTC 6438c2ecf20Sopenharmony_ci * is finally ready to handle interrupts, other software can perform 6448c2ecf20Sopenharmony_ci * target "soft" resets. The ATH6KL interrupt enables reset back to an 6458c2ecf20Sopenharmony_ci * "enabled" state when this happens. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci ath6kl_hif_disable_intrs(dev); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* unmask the host controller interrupts */ 6508c2ecf20Sopenharmony_ci ath6kl_hif_irq_enable(dev->ar); 6518c2ecf20Sopenharmony_ci status = ath6kl_hif_enable_intrs(dev); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return status; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/* disable all device interrupts */ 6578c2ecf20Sopenharmony_ciint ath6kl_hif_mask_intrs(struct ath6kl_device *dev) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci /* 6608c2ecf20Sopenharmony_ci * Mask the interrupt at the HIF layer to avoid any stray interrupt 6618c2ecf20Sopenharmony_ci * taken while we zero out our shadow registers in 6628c2ecf20Sopenharmony_ci * ath6kl_hif_disable_intrs(). 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci ath6kl_hif_irq_disable(dev->ar); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return ath6kl_hif_disable_intrs(dev); 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ciint ath6kl_hif_setup(struct ath6kl_device *dev) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci int status = 0; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci spin_lock_init(&dev->lock); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci /* 6768c2ecf20Sopenharmony_ci * NOTE: we actually get the block size of a mailbox other than 0, 6778c2ecf20Sopenharmony_ci * for SDIO the block size on mailbox 0 is artificially set to 1. 6788c2ecf20Sopenharmony_ci * So we use the block size that is set for the other 3 mailboxes. 6798c2ecf20Sopenharmony_ci */ 6808c2ecf20Sopenharmony_ci dev->htc_cnxt->block_sz = dev->ar->mbox_info.block_size; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* must be a power of 2 */ 6838c2ecf20Sopenharmony_ci if ((dev->htc_cnxt->block_sz & (dev->htc_cnxt->block_sz - 1)) != 0) { 6848c2ecf20Sopenharmony_ci WARN_ON(1); 6858c2ecf20Sopenharmony_ci status = -EINVAL; 6868c2ecf20Sopenharmony_ci goto fail_setup; 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci /* assemble mask, used for padding to a block */ 6908c2ecf20Sopenharmony_ci dev->htc_cnxt->block_mask = dev->htc_cnxt->block_sz - 1; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", 6938c2ecf20Sopenharmony_ci dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci status = ath6kl_hif_disable_intrs(dev); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_cifail_setup: 6988c2ecf20Sopenharmony_ci return status; 6998c2ecf20Sopenharmony_ci} 700