18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * NXP Wireless LAN device driver: PCIE specific handling 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2011-2020 NXP 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This software file (the "File") is distributed by NXP 78c2ecf20Sopenharmony_ci * under the terms of the GNU General Public License Version 2, June 1991 88c2ecf20Sopenharmony_ci * (the "License"). You may use, redistribute and/or modify this File in 98c2ecf20Sopenharmony_ci * accordance with the terms and conditions of the License, a copy of which 108c2ecf20Sopenharmony_ci * is available by writing to the Free Software Foundation, Inc., 118c2ecf20Sopenharmony_ci * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the 128c2ecf20Sopenharmony_ci * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 158c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 168c2ecf20Sopenharmony_ci * ARE EXPRESSLY DISCLAIMED. The License provides additional details about 178c2ecf20Sopenharmony_ci * this warranty disclaimer. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 218c2ecf20Sopenharmony_ci#include <linux/firmware.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "decl.h" 248c2ecf20Sopenharmony_ci#include "ioctl.h" 258c2ecf20Sopenharmony_ci#include "util.h" 268c2ecf20Sopenharmony_ci#include "fw.h" 278c2ecf20Sopenharmony_ci#include "main.h" 288c2ecf20Sopenharmony_ci#include "wmm.h" 298c2ecf20Sopenharmony_ci#include "11n.h" 308c2ecf20Sopenharmony_ci#include "pcie.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define PCIE_VERSION "1.0" 338c2ecf20Sopenharmony_ci#define DRV_NAME "Marvell mwifiex PCIe" 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct mwifiex_if_ops pcie_ops; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { 388c2ecf20Sopenharmony_ci .cmd_addr_lo = PCIE_SCRATCH_0_REG, 398c2ecf20Sopenharmony_ci .cmd_addr_hi = PCIE_SCRATCH_1_REG, 408c2ecf20Sopenharmony_ci .cmd_size = PCIE_SCRATCH_2_REG, 418c2ecf20Sopenharmony_ci .fw_status = PCIE_SCRATCH_3_REG, 428c2ecf20Sopenharmony_ci .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, 438c2ecf20Sopenharmony_ci .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, 448c2ecf20Sopenharmony_ci .tx_rdptr = PCIE_SCRATCH_6_REG, 458c2ecf20Sopenharmony_ci .tx_wrptr = PCIE_SCRATCH_7_REG, 468c2ecf20Sopenharmony_ci .rx_rdptr = PCIE_SCRATCH_8_REG, 478c2ecf20Sopenharmony_ci .rx_wrptr = PCIE_SCRATCH_9_REG, 488c2ecf20Sopenharmony_ci .evt_rdptr = PCIE_SCRATCH_10_REG, 498c2ecf20Sopenharmony_ci .evt_wrptr = PCIE_SCRATCH_11_REG, 508c2ecf20Sopenharmony_ci .drv_rdy = PCIE_SCRATCH_12_REG, 518c2ecf20Sopenharmony_ci .tx_start_ptr = 0, 528c2ecf20Sopenharmony_ci .tx_mask = MWIFIEX_TXBD_MASK, 538c2ecf20Sopenharmony_ci .tx_wrap_mask = 0, 548c2ecf20Sopenharmony_ci .rx_mask = MWIFIEX_RXBD_MASK, 558c2ecf20Sopenharmony_ci .rx_wrap_mask = 0, 568c2ecf20Sopenharmony_ci .tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, 578c2ecf20Sopenharmony_ci .rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, 588c2ecf20Sopenharmony_ci .evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, 598c2ecf20Sopenharmony_ci .ring_flag_sop = 0, 608c2ecf20Sopenharmony_ci .ring_flag_eop = 0, 618c2ecf20Sopenharmony_ci .ring_flag_xs_sop = 0, 628c2ecf20Sopenharmony_ci .ring_flag_xs_eop = 0, 638c2ecf20Sopenharmony_ci .ring_tx_start_ptr = 0, 648c2ecf20Sopenharmony_ci .pfu_enabled = 0, 658c2ecf20Sopenharmony_ci .sleep_cookie = 1, 668c2ecf20Sopenharmony_ci .msix_support = 0, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { 708c2ecf20Sopenharmony_ci .cmd_addr_lo = PCIE_SCRATCH_0_REG, 718c2ecf20Sopenharmony_ci .cmd_addr_hi = PCIE_SCRATCH_1_REG, 728c2ecf20Sopenharmony_ci .cmd_size = PCIE_SCRATCH_2_REG, 738c2ecf20Sopenharmony_ci .fw_status = PCIE_SCRATCH_3_REG, 748c2ecf20Sopenharmony_ci .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, 758c2ecf20Sopenharmony_ci .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, 768c2ecf20Sopenharmony_ci .tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1, 778c2ecf20Sopenharmony_ci .tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1, 788c2ecf20Sopenharmony_ci .rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1, 798c2ecf20Sopenharmony_ci .rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1, 808c2ecf20Sopenharmony_ci .evt_rdptr = PCIE_SCRATCH_10_REG, 818c2ecf20Sopenharmony_ci .evt_wrptr = PCIE_SCRATCH_11_REG, 828c2ecf20Sopenharmony_ci .drv_rdy = PCIE_SCRATCH_12_REG, 838c2ecf20Sopenharmony_ci .tx_start_ptr = 16, 848c2ecf20Sopenharmony_ci .tx_mask = 0x03FF0000, 858c2ecf20Sopenharmony_ci .tx_wrap_mask = 0x07FF0000, 868c2ecf20Sopenharmony_ci .rx_mask = 0x000003FF, 878c2ecf20Sopenharmony_ci .rx_wrap_mask = 0x000007FF, 888c2ecf20Sopenharmony_ci .tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND, 898c2ecf20Sopenharmony_ci .rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND, 908c2ecf20Sopenharmony_ci .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, 918c2ecf20Sopenharmony_ci .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, 928c2ecf20Sopenharmony_ci .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, 938c2ecf20Sopenharmony_ci .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, 948c2ecf20Sopenharmony_ci .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, 958c2ecf20Sopenharmony_ci .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, 968c2ecf20Sopenharmony_ci .pfu_enabled = 1, 978c2ecf20Sopenharmony_ci .sleep_cookie = 0, 988c2ecf20Sopenharmony_ci .fw_dump_ctrl = PCIE_SCRATCH_13_REG, 998c2ecf20Sopenharmony_ci .fw_dump_start = PCIE_SCRATCH_14_REG, 1008c2ecf20Sopenharmony_ci .fw_dump_end = 0xcff, 1018c2ecf20Sopenharmony_ci .fw_dump_host_ready = 0xee, 1028c2ecf20Sopenharmony_ci .fw_dump_read_done = 0xfe, 1038c2ecf20Sopenharmony_ci .msix_support = 0, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { 1078c2ecf20Sopenharmony_ci .cmd_addr_lo = PCIE_SCRATCH_0_REG, 1088c2ecf20Sopenharmony_ci .cmd_addr_hi = PCIE_SCRATCH_1_REG, 1098c2ecf20Sopenharmony_ci .cmd_size = PCIE_SCRATCH_2_REG, 1108c2ecf20Sopenharmony_ci .fw_status = PCIE_SCRATCH_3_REG, 1118c2ecf20Sopenharmony_ci .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, 1128c2ecf20Sopenharmony_ci .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, 1138c2ecf20Sopenharmony_ci .tx_rdptr = 0xC1A4, 1148c2ecf20Sopenharmony_ci .tx_wrptr = 0xC174, 1158c2ecf20Sopenharmony_ci .rx_rdptr = 0xC174, 1168c2ecf20Sopenharmony_ci .rx_wrptr = 0xC1A4, 1178c2ecf20Sopenharmony_ci .evt_rdptr = PCIE_SCRATCH_10_REG, 1188c2ecf20Sopenharmony_ci .evt_wrptr = PCIE_SCRATCH_11_REG, 1198c2ecf20Sopenharmony_ci .drv_rdy = PCIE_SCRATCH_12_REG, 1208c2ecf20Sopenharmony_ci .tx_start_ptr = 16, 1218c2ecf20Sopenharmony_ci .tx_mask = 0x0FFF0000, 1228c2ecf20Sopenharmony_ci .tx_wrap_mask = 0x1FFF0000, 1238c2ecf20Sopenharmony_ci .rx_mask = 0x00000FFF, 1248c2ecf20Sopenharmony_ci .rx_wrap_mask = 0x00001FFF, 1258c2ecf20Sopenharmony_ci .tx_rollover_ind = BIT(28), 1268c2ecf20Sopenharmony_ci .rx_rollover_ind = BIT(12), 1278c2ecf20Sopenharmony_ci .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, 1288c2ecf20Sopenharmony_ci .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, 1298c2ecf20Sopenharmony_ci .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, 1308c2ecf20Sopenharmony_ci .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, 1318c2ecf20Sopenharmony_ci .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, 1328c2ecf20Sopenharmony_ci .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, 1338c2ecf20Sopenharmony_ci .pfu_enabled = 1, 1348c2ecf20Sopenharmony_ci .sleep_cookie = 0, 1358c2ecf20Sopenharmony_ci .fw_dump_ctrl = PCIE_SCRATCH_13_REG, 1368c2ecf20Sopenharmony_ci .fw_dump_start = PCIE_SCRATCH_14_REG, 1378c2ecf20Sopenharmony_ci .fw_dump_end = 0xcff, 1388c2ecf20Sopenharmony_ci .fw_dump_host_ready = 0xcc, 1398c2ecf20Sopenharmony_ci .fw_dump_read_done = 0xdd, 1408c2ecf20Sopenharmony_ci .msix_support = 0, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic struct memory_type_mapping mem_type_mapping_tbl_w8897[] = { 1448c2ecf20Sopenharmony_ci {"ITCM", NULL, 0, 0xF0}, 1458c2ecf20Sopenharmony_ci {"DTCM", NULL, 0, 0xF1}, 1468c2ecf20Sopenharmony_ci {"SQRAM", NULL, 0, 0xF2}, 1478c2ecf20Sopenharmony_ci {"IRAM", NULL, 0, 0xF3}, 1488c2ecf20Sopenharmony_ci {"APU", NULL, 0, 0xF4}, 1498c2ecf20Sopenharmony_ci {"CIU", NULL, 0, 0xF5}, 1508c2ecf20Sopenharmony_ci {"ICU", NULL, 0, 0xF6}, 1518c2ecf20Sopenharmony_ci {"MAC", NULL, 0, 0xF7}, 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic struct memory_type_mapping mem_type_mapping_tbl_w8997[] = { 1558c2ecf20Sopenharmony_ci {"DUMP", NULL, 0, 0xDD}, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct mwifiex_pcie_device mwifiex_pcie8766 = { 1598c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_8766, 1608c2ecf20Sopenharmony_ci .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, 1618c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 1628c2ecf20Sopenharmony_ci .can_dump_fw = false, 1638c2ecf20Sopenharmony_ci .can_ext_scan = true, 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct mwifiex_pcie_device mwifiex_pcie8897 = { 1678c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_8897, 1688c2ecf20Sopenharmony_ci .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, 1698c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 1708c2ecf20Sopenharmony_ci .can_dump_fw = true, 1718c2ecf20Sopenharmony_ci .mem_type_mapping_tbl = mem_type_mapping_tbl_w8897, 1728c2ecf20Sopenharmony_ci .num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl_w8897), 1738c2ecf20Sopenharmony_ci .can_ext_scan = true, 1748c2ecf20Sopenharmony_ci}; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const struct mwifiex_pcie_device mwifiex_pcie8997 = { 1778c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_8997, 1788c2ecf20Sopenharmony_ci .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, 1798c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 1808c2ecf20Sopenharmony_ci .can_dump_fw = true, 1818c2ecf20Sopenharmony_ci .mem_type_mapping_tbl = mem_type_mapping_tbl_w8997, 1828c2ecf20Sopenharmony_ci .num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl_w8997), 1838c2ecf20Sopenharmony_ci .can_ext_scan = true, 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic const struct of_device_id mwifiex_pcie_of_match_table[] __maybe_unused = { 1878c2ecf20Sopenharmony_ci { .compatible = "pci11ab,2b42" }, 1888c2ecf20Sopenharmony_ci { .compatible = "pci1b4b,2b42" }, 1898c2ecf20Sopenharmony_ci { } 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int mwifiex_pcie_probe_of(struct device *dev) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci if (!of_match_node(mwifiex_pcie_of_match_table, dev->of_node)) { 1958c2ecf20Sopenharmony_ci dev_err(dev, "required compatible string missing\n"); 1968c2ecf20Sopenharmony_ci return -EINVAL; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cistatic void mwifiex_pcie_work(struct work_struct *work); 2038c2ecf20Sopenharmony_cistatic int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter); 2048c2ecf20Sopenharmony_cistatic int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int 2078c2ecf20Sopenharmony_cimwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, 2088c2ecf20Sopenharmony_ci size_t size, int flags) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 2118c2ecf20Sopenharmony_ci struct mwifiex_dma_mapping mapping; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci mapping.addr = dma_map_single(&card->dev->dev, skb->data, size, flags); 2148c2ecf20Sopenharmony_ci if (dma_mapping_error(&card->dev->dev, mapping.addr)) { 2158c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "failed to map pci memory!\n"); 2168c2ecf20Sopenharmony_ci return -1; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci mapping.len = size; 2198c2ecf20Sopenharmony_ci mwifiex_store_mapping(skb, &mapping); 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, 2248c2ecf20Sopenharmony_ci struct sk_buff *skb, int flags) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 2278c2ecf20Sopenharmony_ci struct mwifiex_dma_mapping mapping; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci mwifiex_get_mapping(skb, &mapping); 2308c2ecf20Sopenharmony_ci dma_unmap_single(&card->dev->dev, mapping.addr, mapping.len, flags); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * This function writes data into PCIE card register. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_cistatic int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci iowrite32(data, card->pci_mmap1 + reg); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return 0; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* This function reads data from PCIE card register. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci *data = ioread32(card->pci_mmap1 + reg); 2528c2ecf20Sopenharmony_ci if (*data == 0xffffffff) 2538c2ecf20Sopenharmony_ci return 0xffffffff; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* This function reads u8 data from PCIE card register. */ 2598c2ecf20Sopenharmony_cistatic int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, 2608c2ecf20Sopenharmony_ci int reg, u8 *data) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci *data = ioread8(card->pci_mmap1 + reg); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * This function reads sleep cookie and checks if FW is ready 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci u32 cookie_value; 2758c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 2768c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (!reg->sleep_cookie) 2798c2ecf20Sopenharmony_ci return true; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (card->sleep_cookie_vbase) { 2828c2ecf20Sopenharmony_ci cookie_value = get_unaligned_le32(card->sleep_cookie_vbase); 2838c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 2848c2ecf20Sopenharmony_ci "info: ACCESS_HW: sleep cookie=0x%x\n", 2858c2ecf20Sopenharmony_ci cookie_value); 2868c2ecf20Sopenharmony_ci if (cookie_value == FW_AWAKE_COOKIE) 2878c2ecf20Sopenharmony_ci return true; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return false; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2948c2ecf20Sopenharmony_ci/* 2958c2ecf20Sopenharmony_ci * Kernel needs to suspend all functions separately. Therefore all 2968c2ecf20Sopenharmony_ci * registered functions must have drivers with suspend and resume 2978c2ecf20Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * If already not suspended, this function allocates and sends a host 3008c2ecf20Sopenharmony_ci * sleep activate request to the firmware and turns off the traffic. 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic int mwifiex_pcie_suspend(struct device *dev) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 3058c2ecf20Sopenharmony_ci struct pcie_service_card *card = dev_get_drvdata(dev); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Might still be loading firmware */ 3098c2ecf20Sopenharmony_ci wait_for_completion(&card->fw_done); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci adapter = card->adapter; 3128c2ecf20Sopenharmony_ci if (!adapter) { 3138c2ecf20Sopenharmony_ci dev_err(dev, "adapter is not valid\n"); 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci mwifiex_enable_wake(adapter); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* Enable the Host Sleep */ 3208c2ecf20Sopenharmony_ci if (!mwifiex_enable_hs(adapter)) { 3218c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 3228c2ecf20Sopenharmony_ci "cmd: failed to suspend\n"); 3238c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 3248c2ecf20Sopenharmony_ci mwifiex_disable_wake(adapter); 3258c2ecf20Sopenharmony_ci return -EFAULT; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci flush_workqueue(adapter->workqueue); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Indicate device suspended */ 3318c2ecf20Sopenharmony_ci set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 3328c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* 3388c2ecf20Sopenharmony_ci * Kernel needs to suspend all functions separately. Therefore all 3398c2ecf20Sopenharmony_ci * registered functions must have drivers with suspend and resume 3408c2ecf20Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * If already not resumed, this function turns on the traffic and 3438c2ecf20Sopenharmony_ci * sends a host sleep cancel request to the firmware. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_cistatic int mwifiex_pcie_resume(struct device *dev) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 3488c2ecf20Sopenharmony_ci struct pcie_service_card *card = dev_get_drvdata(dev); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci if (!card->adapter) { 3528c2ecf20Sopenharmony_ci dev_err(dev, "adapter structure is not valid\n"); 3538c2ecf20Sopenharmony_ci return 0; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci adapter = card->adapter; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 3598c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 3608c2ecf20Sopenharmony_ci "Device already resumed\n"); 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), 3678c2ecf20Sopenharmony_ci MWIFIEX_ASYNC_CMD); 3688c2ecf20Sopenharmony_ci mwifiex_disable_wake(adapter); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci#endif 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/* 3758c2ecf20Sopenharmony_ci * This function probes an mwifiex device and registers it. It allocates 3768c2ecf20Sopenharmony_ci * the card structure, enables PCIE function number and initiates the 3778c2ecf20Sopenharmony_ci * device registration and initialization procedure by adding a logical 3788c2ecf20Sopenharmony_ci * interface. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic int mwifiex_pcie_probe(struct pci_dev *pdev, 3818c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci struct pcie_service_card *card; 3848c2ecf20Sopenharmony_ci int ret; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", 3878c2ecf20Sopenharmony_ci pdev->vendor, pdev->device, pdev->revision); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL); 3908c2ecf20Sopenharmony_ci if (!card) 3918c2ecf20Sopenharmony_ci return -ENOMEM; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci init_completion(&card->fw_done); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci card->dev = pdev; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci if (ent->driver_data) { 3988c2ecf20Sopenharmony_ci struct mwifiex_pcie_device *data = (void *)ent->driver_data; 3998c2ecf20Sopenharmony_ci card->pcie.reg = data->reg; 4008c2ecf20Sopenharmony_ci card->pcie.blksz_fw_dl = data->blksz_fw_dl; 4018c2ecf20Sopenharmony_ci card->pcie.tx_buf_size = data->tx_buf_size; 4028c2ecf20Sopenharmony_ci card->pcie.can_dump_fw = data->can_dump_fw; 4038c2ecf20Sopenharmony_ci card->pcie.mem_type_mapping_tbl = data->mem_type_mapping_tbl; 4048c2ecf20Sopenharmony_ci card->pcie.num_mem_types = data->num_mem_types; 4058c2ecf20Sopenharmony_ci card->pcie.can_ext_scan = data->can_ext_scan; 4068c2ecf20Sopenharmony_ci INIT_WORK(&card->work, mwifiex_pcie_work); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci /* device tree node parsing and platform specific configuration*/ 4108c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 4118c2ecf20Sopenharmony_ci ret = mwifiex_pcie_probe_of(&pdev->dev); 4128c2ecf20Sopenharmony_ci if (ret) 4138c2ecf20Sopenharmony_ci return ret; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (mwifiex_add_card(card, &card->fw_done, &pcie_ops, 4178c2ecf20Sopenharmony_ci MWIFIEX_PCIE, &pdev->dev)) { 4188c2ecf20Sopenharmony_ci pr_err("%s failed\n", __func__); 4198c2ecf20Sopenharmony_ci return -1; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* 4268c2ecf20Sopenharmony_ci * This function removes the interface and frees up the card structure. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_cistatic void mwifiex_pcie_remove(struct pci_dev *pdev) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct pcie_service_card *card; 4318c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 4328c2ecf20Sopenharmony_ci struct mwifiex_private *priv; 4338c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg; 4348c2ecf20Sopenharmony_ci u32 fw_status; 4358c2ecf20Sopenharmony_ci int ret; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci card = pci_get_drvdata(pdev); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci wait_for_completion(&card->fw_done); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci adapter = card->adapter; 4428c2ecf20Sopenharmony_ci if (!adapter || !adapter->priv_num) 4438c2ecf20Sopenharmony_ci return; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci reg = card->pcie.reg; 4468c2ecf20Sopenharmony_ci if (reg) 4478c2ecf20Sopenharmony_ci ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status); 4488c2ecf20Sopenharmony_ci else 4498c2ecf20Sopenharmony_ci fw_status = -1; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (fw_status == FIRMWARE_READY_PCIE && !adapter->mfg_mode) { 4528c2ecf20Sopenharmony_ci mwifiex_deauthenticate_all(adapter); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci mwifiex_disable_auto_ds(priv); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci mwifiex_remove_card(adapter); 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic void mwifiex_pcie_shutdown(struct pci_dev *pdev) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci mwifiex_pcie_remove(pdev); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic void mwifiex_pcie_coredump(struct device *dev) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct pci_dev *pdev; 4748c2ecf20Sopenharmony_ci struct pcie_service_card *card; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci pdev = container_of(dev, struct pci_dev, dev); 4778c2ecf20Sopenharmony_ci card = pci_get_drvdata(pdev); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 4808c2ecf20Sopenharmony_ci &card->work_flags)) 4818c2ecf20Sopenharmony_ci schedule_work(&card->work); 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic const struct pci_device_id mwifiex_ids[] = { 4858c2ecf20Sopenharmony_ci { 4868c2ecf20Sopenharmony_ci PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, 4878c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4888c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_pcie8766, 4898c2ecf20Sopenharmony_ci }, 4908c2ecf20Sopenharmony_ci { 4918c2ecf20Sopenharmony_ci PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897, 4928c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4938c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_pcie8897, 4948c2ecf20Sopenharmony_ci }, 4958c2ecf20Sopenharmony_ci { 4968c2ecf20Sopenharmony_ci PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997, 4978c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4988c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_pcie8997, 4998c2ecf20Sopenharmony_ci }, 5008c2ecf20Sopenharmony_ci { 5018c2ecf20Sopenharmony_ci PCIE_VENDOR_ID_V2_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997, 5028c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5038c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_pcie8997, 5048c2ecf20Sopenharmony_ci }, 5058c2ecf20Sopenharmony_ci {}, 5068c2ecf20Sopenharmony_ci}; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mwifiex_ids); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci/* 5118c2ecf20Sopenharmony_ci * Cleanup all software without cleaning anything related to PCIe and HW. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_cistatic void mwifiex_pcie_reset_prepare(struct pci_dev *pdev) 5148c2ecf20Sopenharmony_ci{ 5158c2ecf20Sopenharmony_ci struct pcie_service_card *card = pci_get_drvdata(pdev); 5168c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = card->adapter; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci if (!adapter) { 5198c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: adapter structure is not valid\n", 5208c2ecf20Sopenharmony_ci __func__); 5218c2ecf20Sopenharmony_ci return; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 5258c2ecf20Sopenharmony_ci "%s: vendor=0x%4.04x device=0x%4.04x rev=%d Pre-FLR\n", 5268c2ecf20Sopenharmony_ci __func__, pdev->vendor, pdev->device, pdev->revision); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci mwifiex_shutdown_sw(adapter); 5298c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); 5308c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); 5318c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci card->pci_reset_ongoing = true; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/* 5378c2ecf20Sopenharmony_ci * Kernel stores and restores PCIe function context before and after performing 5388c2ecf20Sopenharmony_ci * FLR respectively. Reconfigure the software and firmware including firmware 5398c2ecf20Sopenharmony_ci * redownload. 5408c2ecf20Sopenharmony_ci */ 5418c2ecf20Sopenharmony_cistatic void mwifiex_pcie_reset_done(struct pci_dev *pdev) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct pcie_service_card *card = pci_get_drvdata(pdev); 5448c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter = card->adapter; 5458c2ecf20Sopenharmony_ci int ret; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci if (!adapter) { 5488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s: adapter structure is not valid\n", 5498c2ecf20Sopenharmony_ci __func__); 5508c2ecf20Sopenharmony_ci return; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 5548c2ecf20Sopenharmony_ci "%s: vendor=0x%4.04x device=0x%4.04x rev=%d Post-FLR\n", 5558c2ecf20Sopenharmony_ci __func__, pdev->vendor, pdev->device, pdev->revision); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci ret = mwifiex_reinit_sw(adapter); 5588c2ecf20Sopenharmony_ci if (ret) 5598c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "reinit failed: %d\n", ret); 5608c2ecf20Sopenharmony_ci else 5618c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci card->pci_reset_ongoing = false; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic const struct pci_error_handlers mwifiex_pcie_err_handler = { 5678c2ecf20Sopenharmony_ci .reset_prepare = mwifiex_pcie_reset_prepare, 5688c2ecf20Sopenharmony_ci .reset_done = mwifiex_pcie_reset_done, 5698c2ecf20Sopenharmony_ci}; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5728c2ecf20Sopenharmony_ci/* Power Management Hooks */ 5738c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend, 5748c2ecf20Sopenharmony_ci mwifiex_pcie_resume); 5758c2ecf20Sopenharmony_ci#endif 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci/* PCI Device Driver */ 5788c2ecf20Sopenharmony_cistatic struct pci_driver __refdata mwifiex_pcie = { 5798c2ecf20Sopenharmony_ci .name = "mwifiex_pcie", 5808c2ecf20Sopenharmony_ci .id_table = mwifiex_ids, 5818c2ecf20Sopenharmony_ci .probe = mwifiex_pcie_probe, 5828c2ecf20Sopenharmony_ci .remove = mwifiex_pcie_remove, 5838c2ecf20Sopenharmony_ci .driver = { 5848c2ecf20Sopenharmony_ci .coredump = mwifiex_pcie_coredump, 5858c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5868c2ecf20Sopenharmony_ci .pm = &mwifiex_pcie_pm_ops, 5878c2ecf20Sopenharmony_ci#endif 5888c2ecf20Sopenharmony_ci }, 5898c2ecf20Sopenharmony_ci .shutdown = mwifiex_pcie_shutdown, 5908c2ecf20Sopenharmony_ci .err_handler = &mwifiex_pcie_err_handler, 5918c2ecf20Sopenharmony_ci}; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/* 5948c2ecf20Sopenharmony_ci * This function adds delay loop to ensure FW is awake before proceeding. 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_cistatic void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci int i = 0; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci while (mwifiex_pcie_ok_to_access_hw(adapter)) { 6018c2ecf20Sopenharmony_ci i++; 6028c2ecf20Sopenharmony_ci usleep_range(10, 20); 6038c2ecf20Sopenharmony_ci /* 50ms max wait */ 6048c2ecf20Sopenharmony_ci if (i == 5000) 6058c2ecf20Sopenharmony_ci break; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci return; 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter, 6128c2ecf20Sopenharmony_ci u32 max_delay_loop_cnt) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 6158c2ecf20Sopenharmony_ci u8 *buffer; 6168c2ecf20Sopenharmony_ci u32 sleep_cookie, count; 6178c2ecf20Sopenharmony_ci struct sk_buff *cmdrsp = card->cmdrsp_buf; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci for (count = 0; count < max_delay_loop_cnt; count++) { 6208c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&card->dev->dev, 6218c2ecf20Sopenharmony_ci MWIFIEX_SKB_DMA_ADDR(cmdrsp), 6228c2ecf20Sopenharmony_ci sizeof(sleep_cookie), DMA_FROM_DEVICE); 6238c2ecf20Sopenharmony_ci buffer = cmdrsp->data; 6248c2ecf20Sopenharmony_ci sleep_cookie = get_unaligned_le32(buffer); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) { 6278c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 6288c2ecf20Sopenharmony_ci "sleep cookie found at count %d\n", count); 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci dma_sync_single_for_device(&card->dev->dev, 6328c2ecf20Sopenharmony_ci MWIFIEX_SKB_DMA_ADDR(cmdrsp), 6338c2ecf20Sopenharmony_ci sizeof(sleep_cookie), 6348c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 6358c2ecf20Sopenharmony_ci usleep_range(20, 30); 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (count >= max_delay_loop_cnt) 6398c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 6408c2ecf20Sopenharmony_ci "max count reached while accessing sleep cookie\n"); 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci#define N_WAKEUP_TRIES_SHORT_INTERVAL 15 6448c2ecf20Sopenharmony_ci#define N_WAKEUP_TRIES_LONG_INTERVAL 35 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/* This function wakes up the card by reading fw_status register. */ 6478c2ecf20Sopenharmony_cistatic int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 6508c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 6518c2ecf20Sopenharmony_ci int retval; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 6548c2ecf20Sopenharmony_ci "event: Wakeup device...\n"); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (reg->sleep_cookie) 6578c2ecf20Sopenharmony_ci mwifiex_pcie_dev_wakeup_delay(adapter); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* The 88W8897 PCIe+USB firmware (latest version 15.68.19.p21) sometimes 6608c2ecf20Sopenharmony_ci * appears to ignore or miss our wakeup request, so we continue trying 6618c2ecf20Sopenharmony_ci * until we receive an interrupt from the card. 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci if (read_poll_timeout(mwifiex_write_reg, retval, 6648c2ecf20Sopenharmony_ci READ_ONCE(adapter->int_status) != 0, 6658c2ecf20Sopenharmony_ci 500, 500 * N_WAKEUP_TRIES_SHORT_INTERVAL, 6668c2ecf20Sopenharmony_ci false, 6678c2ecf20Sopenharmony_ci adapter, reg->fw_status, FIRMWARE_READY_PCIE)) { 6688c2ecf20Sopenharmony_ci if (read_poll_timeout(mwifiex_write_reg, retval, 6698c2ecf20Sopenharmony_ci READ_ONCE(adapter->int_status) != 0, 6708c2ecf20Sopenharmony_ci 10000, 10000 * N_WAKEUP_TRIES_LONG_INTERVAL, 6718c2ecf20Sopenharmony_ci false, 6728c2ecf20Sopenharmony_ci adapter, reg->fw_status, FIRMWARE_READY_PCIE)) { 6738c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 6748c2ecf20Sopenharmony_ci "Firmware didn't wake up\n"); 6758c2ecf20Sopenharmony_ci return -EIO; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (reg->sleep_cookie) { 6808c2ecf20Sopenharmony_ci mwifiex_pcie_dev_wakeup_delay(adapter); 6818c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 6828c2ecf20Sopenharmony_ci "PCIE wakeup: Setting PS_STATE_AWAKE\n"); 6838c2ecf20Sopenharmony_ci adapter->ps_state = PS_STATE_AWAKE; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci return 0; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci/* 6908c2ecf20Sopenharmony_ci * This function is called after the card has woken up. 6918c2ecf20Sopenharmony_ci * 6928c2ecf20Sopenharmony_ci * The card configuration register is reset. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_cistatic int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, CMD, 6978c2ecf20Sopenharmony_ci "cmd: Wakeup device completed\n"); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/* 7038c2ecf20Sopenharmony_ci * This function disables the host interrupt. 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * The host interrupt mask is read, the disable bit is reset and 7068c2ecf20Sopenharmony_ci * written back to the card host interrupt mask register. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_cistatic int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci if (mwifiex_pcie_ok_to_access_hw(adapter)) { 7118c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, 7128c2ecf20Sopenharmony_ci 0x00000000)) { 7138c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 7148c2ecf20Sopenharmony_ci "Disable host interrupt failed\n"); 7158c2ecf20Sopenharmony_ci return -1; 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci atomic_set(&adapter->tx_hw_pending, 0); 7208c2ecf20Sopenharmony_ci return 0; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic void mwifiex_pcie_disable_host_int_noerr(struct mwifiex_adapter *adapter) 7248c2ecf20Sopenharmony_ci{ 7258c2ecf20Sopenharmony_ci WARN_ON(mwifiex_pcie_disable_host_int(adapter)); 7268c2ecf20Sopenharmony_ci} 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci/* 7298c2ecf20Sopenharmony_ci * This function enables the host interrupt. 7308c2ecf20Sopenharmony_ci * 7318c2ecf20Sopenharmony_ci * The host interrupt enable mask is written to the card 7328c2ecf20Sopenharmony_ci * host interrupt mask register. 7338c2ecf20Sopenharmony_ci */ 7348c2ecf20Sopenharmony_cistatic int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci if (mwifiex_pcie_ok_to_access_hw(adapter)) { 7378c2ecf20Sopenharmony_ci /* Simply write the mask to the register */ 7388c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, 7398c2ecf20Sopenharmony_ci HOST_INTR_MASK)) { 7408c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 7418c2ecf20Sopenharmony_ci "Enable host interrupt failed\n"); 7428c2ecf20Sopenharmony_ci return -1; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return 0; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci/* 7508c2ecf20Sopenharmony_ci * This function initializes TX buffer ring descriptors 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_cistatic int mwifiex_init_txq_ring(struct mwifiex_adapter *adapter) 7538c2ecf20Sopenharmony_ci{ 7548c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 7558c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 7568c2ecf20Sopenharmony_ci struct mwifiex_pcie_buf_desc *desc; 7578c2ecf20Sopenharmony_ci struct mwifiex_pfu_buf_desc *desc2; 7588c2ecf20Sopenharmony_ci int i; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { 7618c2ecf20Sopenharmony_ci card->tx_buf_list[i] = NULL; 7628c2ecf20Sopenharmony_ci if (reg->pfu_enabled) { 7638c2ecf20Sopenharmony_ci card->txbd_ring[i] = (void *)card->txbd_ring_vbase + 7648c2ecf20Sopenharmony_ci (sizeof(*desc2) * i); 7658c2ecf20Sopenharmony_ci desc2 = card->txbd_ring[i]; 7668c2ecf20Sopenharmony_ci memset(desc2, 0, sizeof(*desc2)); 7678c2ecf20Sopenharmony_ci } else { 7688c2ecf20Sopenharmony_ci card->txbd_ring[i] = (void *)card->txbd_ring_vbase + 7698c2ecf20Sopenharmony_ci (sizeof(*desc) * i); 7708c2ecf20Sopenharmony_ci desc = card->txbd_ring[i]; 7718c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci return 0; 7768c2ecf20Sopenharmony_ci} 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci/* This function initializes RX buffer ring descriptors. Each SKB is allocated 7798c2ecf20Sopenharmony_ci * here and after mapping PCI memory, its physical address is assigned to 7808c2ecf20Sopenharmony_ci * PCIE Rx buffer descriptor's physical address. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_cistatic int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 7858c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 7868c2ecf20Sopenharmony_ci struct sk_buff *skb; 7878c2ecf20Sopenharmony_ci struct mwifiex_pcie_buf_desc *desc; 7888c2ecf20Sopenharmony_ci struct mwifiex_pfu_buf_desc *desc2; 7898c2ecf20Sopenharmony_ci dma_addr_t buf_pa; 7908c2ecf20Sopenharmony_ci int i; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { 7938c2ecf20Sopenharmony_ci /* Allocate skb here so that firmware can DMA data from it */ 7948c2ecf20Sopenharmony_ci skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, 7958c2ecf20Sopenharmony_ci GFP_KERNEL); 7968c2ecf20Sopenharmony_ci if (!skb) { 7978c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 7988c2ecf20Sopenharmony_ci "Unable to allocate skb for RX ring.\n"); 7998c2ecf20Sopenharmony_ci return -ENOMEM; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, 8038c2ecf20Sopenharmony_ci MWIFIEX_RX_DATA_BUF_SIZE, 8048c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) { 8058c2ecf20Sopenharmony_ci kfree_skb(skb); 8068c2ecf20Sopenharmony_ci return -ENOMEM; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 8128c2ecf20Sopenharmony_ci "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", 8138c2ecf20Sopenharmony_ci skb, skb->len, skb->data, (u32)buf_pa, 8148c2ecf20Sopenharmony_ci (u32)((u64)buf_pa >> 32)); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci card->rx_buf_list[i] = skb; 8178c2ecf20Sopenharmony_ci if (reg->pfu_enabled) { 8188c2ecf20Sopenharmony_ci card->rxbd_ring[i] = (void *)card->rxbd_ring_vbase + 8198c2ecf20Sopenharmony_ci (sizeof(*desc2) * i); 8208c2ecf20Sopenharmony_ci desc2 = card->rxbd_ring[i]; 8218c2ecf20Sopenharmony_ci desc2->paddr = buf_pa; 8228c2ecf20Sopenharmony_ci desc2->len = (u16)skb->len; 8238c2ecf20Sopenharmony_ci desc2->frag_len = (u16)skb->len; 8248c2ecf20Sopenharmony_ci desc2->flags = reg->ring_flag_eop | reg->ring_flag_sop; 8258c2ecf20Sopenharmony_ci desc2->offset = 0; 8268c2ecf20Sopenharmony_ci } else { 8278c2ecf20Sopenharmony_ci card->rxbd_ring[i] = (void *)(card->rxbd_ring_vbase + 8288c2ecf20Sopenharmony_ci (sizeof(*desc) * i)); 8298c2ecf20Sopenharmony_ci desc = card->rxbd_ring[i]; 8308c2ecf20Sopenharmony_ci desc->paddr = buf_pa; 8318c2ecf20Sopenharmony_ci desc->len = (u16)skb->len; 8328c2ecf20Sopenharmony_ci desc->flags = 0; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci return 0; 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci/* This function initializes event buffer ring descriptors. Each SKB is 8408c2ecf20Sopenharmony_ci * allocated here and after mapping PCI memory, its physical address is assigned 8418c2ecf20Sopenharmony_ci * to PCIE Rx buffer descriptor's physical address 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_cistatic int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 8468c2ecf20Sopenharmony_ci struct mwifiex_evt_buf_desc *desc; 8478c2ecf20Sopenharmony_ci struct sk_buff *skb; 8488c2ecf20Sopenharmony_ci dma_addr_t buf_pa; 8498c2ecf20Sopenharmony_ci int i; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { 8528c2ecf20Sopenharmony_ci /* Allocate skb here so that firmware can DMA data from it */ 8538c2ecf20Sopenharmony_ci skb = dev_alloc_skb(MAX_EVENT_SIZE); 8548c2ecf20Sopenharmony_ci if (!skb) { 8558c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 8568c2ecf20Sopenharmony_ci "Unable to allocate skb for EVENT buf.\n"); 8578c2ecf20Sopenharmony_ci return -ENOMEM; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci skb_put(skb, MAX_EVENT_SIZE); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, 8628c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) { 8638c2ecf20Sopenharmony_ci kfree_skb(skb); 8648c2ecf20Sopenharmony_ci return -ENOMEM; 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 8708c2ecf20Sopenharmony_ci "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", 8718c2ecf20Sopenharmony_ci skb, skb->len, skb->data, (u32)buf_pa, 8728c2ecf20Sopenharmony_ci (u32)((u64)buf_pa >> 32)); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci card->evt_buf_list[i] = skb; 8758c2ecf20Sopenharmony_ci card->evtbd_ring[i] = (void *)(card->evtbd_ring_vbase + 8768c2ecf20Sopenharmony_ci (sizeof(*desc) * i)); 8778c2ecf20Sopenharmony_ci desc = card->evtbd_ring[i]; 8788c2ecf20Sopenharmony_ci desc->paddr = buf_pa; 8798c2ecf20Sopenharmony_ci desc->len = (u16)skb->len; 8808c2ecf20Sopenharmony_ci desc->flags = 0; 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return 0; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci/* This function cleans up TX buffer rings. If any of the buffer list has valid 8878c2ecf20Sopenharmony_ci * SKB address, associated SKB is freed. 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_cistatic void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) 8908c2ecf20Sopenharmony_ci{ 8918c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 8928c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 8938c2ecf20Sopenharmony_ci struct sk_buff *skb; 8948c2ecf20Sopenharmony_ci struct mwifiex_pcie_buf_desc *desc; 8958c2ecf20Sopenharmony_ci struct mwifiex_pfu_buf_desc *desc2; 8968c2ecf20Sopenharmony_ci int i; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { 8998c2ecf20Sopenharmony_ci if (reg->pfu_enabled) { 9008c2ecf20Sopenharmony_ci desc2 = card->txbd_ring[i]; 9018c2ecf20Sopenharmony_ci if (card->tx_buf_list[i]) { 9028c2ecf20Sopenharmony_ci skb = card->tx_buf_list[i]; 9038c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 9048c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9058c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci memset(desc2, 0, sizeof(*desc2)); 9088c2ecf20Sopenharmony_ci } else { 9098c2ecf20Sopenharmony_ci desc = card->txbd_ring[i]; 9108c2ecf20Sopenharmony_ci if (card->tx_buf_list[i]) { 9118c2ecf20Sopenharmony_ci skb = card->tx_buf_list[i]; 9128c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 9138c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 9148c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci card->tx_buf_list[i] = NULL; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci atomic_set(&adapter->tx_hw_pending, 0); 9228c2ecf20Sopenharmony_ci return; 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci/* This function cleans up RX buffer rings. If any of the buffer list has valid 9268c2ecf20Sopenharmony_ci * SKB address, associated SKB is freed. 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_cistatic void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 9318c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 9328c2ecf20Sopenharmony_ci struct mwifiex_pcie_buf_desc *desc; 9338c2ecf20Sopenharmony_ci struct mwifiex_pfu_buf_desc *desc2; 9348c2ecf20Sopenharmony_ci struct sk_buff *skb; 9358c2ecf20Sopenharmony_ci int i; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { 9388c2ecf20Sopenharmony_ci if (reg->pfu_enabled) { 9398c2ecf20Sopenharmony_ci desc2 = card->rxbd_ring[i]; 9408c2ecf20Sopenharmony_ci if (card->rx_buf_list[i]) { 9418c2ecf20Sopenharmony_ci skb = card->rx_buf_list[i]; 9428c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 9438c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 9448c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci memset(desc2, 0, sizeof(*desc2)); 9478c2ecf20Sopenharmony_ci } else { 9488c2ecf20Sopenharmony_ci desc = card->rxbd_ring[i]; 9498c2ecf20Sopenharmony_ci if (card->rx_buf_list[i]) { 9508c2ecf20Sopenharmony_ci skb = card->rx_buf_list[i]; 9518c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 9528c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 9538c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 9548c2ecf20Sopenharmony_ci } 9558c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci card->rx_buf_list[i] = NULL; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci/* This function cleans up event buffer rings. If any of the buffer list has 9648c2ecf20Sopenharmony_ci * valid SKB address, associated SKB is freed. 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_cistatic void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter) 9678c2ecf20Sopenharmony_ci{ 9688c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 9698c2ecf20Sopenharmony_ci struct mwifiex_evt_buf_desc *desc; 9708c2ecf20Sopenharmony_ci struct sk_buff *skb; 9718c2ecf20Sopenharmony_ci int i; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { 9748c2ecf20Sopenharmony_ci desc = card->evtbd_ring[i]; 9758c2ecf20Sopenharmony_ci if (card->evt_buf_list[i]) { 9768c2ecf20Sopenharmony_ci skb = card->evt_buf_list[i]; 9778c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 9788c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 9798c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci card->evt_buf_list[i] = NULL; 9828c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci return; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci/* This function creates buffer descriptor ring for TX 9898c2ecf20Sopenharmony_ci */ 9908c2ecf20Sopenharmony_cistatic int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 9938c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci /* 9968c2ecf20Sopenharmony_ci * driver maintaines the write pointer and firmware maintaines the read 9978c2ecf20Sopenharmony_ci * pointer. The write pointer starts at 0 (zero) while the read pointer 9988c2ecf20Sopenharmony_ci * starts at zero with rollover bit set 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_ci card->txbd_wrptr = 0; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (reg->pfu_enabled) 10038c2ecf20Sopenharmony_ci card->txbd_rdptr = 0; 10048c2ecf20Sopenharmony_ci else 10058c2ecf20Sopenharmony_ci card->txbd_rdptr |= reg->tx_rollover_ind; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* allocate shared memory for the BD ring and divide the same in to 10088c2ecf20Sopenharmony_ci several descriptors */ 10098c2ecf20Sopenharmony_ci if (reg->pfu_enabled) 10108c2ecf20Sopenharmony_ci card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * 10118c2ecf20Sopenharmony_ci MWIFIEX_MAX_TXRX_BD; 10128c2ecf20Sopenharmony_ci else 10138c2ecf20Sopenharmony_ci card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * 10148c2ecf20Sopenharmony_ci MWIFIEX_MAX_TXRX_BD; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 10178c2ecf20Sopenharmony_ci "info: txbd_ring: Allocating %d bytes\n", 10188c2ecf20Sopenharmony_ci card->txbd_ring_size); 10198c2ecf20Sopenharmony_ci card->txbd_ring_vbase = dma_alloc_coherent(&card->dev->dev, 10208c2ecf20Sopenharmony_ci card->txbd_ring_size, 10218c2ecf20Sopenharmony_ci &card->txbd_ring_pbase, 10228c2ecf20Sopenharmony_ci GFP_KERNEL); 10238c2ecf20Sopenharmony_ci if (!card->txbd_ring_vbase) { 10248c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 10258c2ecf20Sopenharmony_ci "allocate coherent memory (%d bytes) failed!\n", 10268c2ecf20Sopenharmony_ci card->txbd_ring_size); 10278c2ecf20Sopenharmony_ci return -ENOMEM; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 10318c2ecf20Sopenharmony_ci "info: txbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", 10328c2ecf20Sopenharmony_ci card->txbd_ring_vbase, (u32)card->txbd_ring_pbase, 10338c2ecf20Sopenharmony_ci (u32)((u64)card->txbd_ring_pbase >> 32), 10348c2ecf20Sopenharmony_ci card->txbd_ring_size); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return mwifiex_init_txq_ring(adapter); 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 10428c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci mwifiex_cleanup_txq_ring(adapter); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (card->txbd_ring_vbase) 10478c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, card->txbd_ring_size, 10488c2ecf20Sopenharmony_ci card->txbd_ring_vbase, 10498c2ecf20Sopenharmony_ci card->txbd_ring_pbase); 10508c2ecf20Sopenharmony_ci card->txbd_ring_size = 0; 10518c2ecf20Sopenharmony_ci card->txbd_wrptr = 0; 10528c2ecf20Sopenharmony_ci card->txbd_rdptr = 0 | reg->tx_rollover_ind; 10538c2ecf20Sopenharmony_ci card->txbd_ring_vbase = NULL; 10548c2ecf20Sopenharmony_ci card->txbd_ring_pbase = 0; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci/* 10608c2ecf20Sopenharmony_ci * This function creates buffer descriptor ring for RX 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_cistatic int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci int ret; 10658c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 10668c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* 10698c2ecf20Sopenharmony_ci * driver maintaines the read pointer and firmware maintaines the write 10708c2ecf20Sopenharmony_ci * pointer. The write pointer starts at 0 (zero) while the read pointer 10718c2ecf20Sopenharmony_ci * starts at zero with rollover bit set 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_ci card->rxbd_wrptr = 0; 10748c2ecf20Sopenharmony_ci card->rxbd_rdptr = reg->rx_rollover_ind; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci if (reg->pfu_enabled) 10778c2ecf20Sopenharmony_ci card->rxbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * 10788c2ecf20Sopenharmony_ci MWIFIEX_MAX_TXRX_BD; 10798c2ecf20Sopenharmony_ci else 10808c2ecf20Sopenharmony_ci card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * 10818c2ecf20Sopenharmony_ci MWIFIEX_MAX_TXRX_BD; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 10848c2ecf20Sopenharmony_ci "info: rxbd_ring: Allocating %d bytes\n", 10858c2ecf20Sopenharmony_ci card->rxbd_ring_size); 10868c2ecf20Sopenharmony_ci card->rxbd_ring_vbase = dma_alloc_coherent(&card->dev->dev, 10878c2ecf20Sopenharmony_ci card->rxbd_ring_size, 10888c2ecf20Sopenharmony_ci &card->rxbd_ring_pbase, 10898c2ecf20Sopenharmony_ci GFP_KERNEL); 10908c2ecf20Sopenharmony_ci if (!card->rxbd_ring_vbase) { 10918c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 10928c2ecf20Sopenharmony_ci "allocate coherent memory (%d bytes) failed!\n", 10938c2ecf20Sopenharmony_ci card->rxbd_ring_size); 10948c2ecf20Sopenharmony_ci return -ENOMEM; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 10988c2ecf20Sopenharmony_ci "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", 10998c2ecf20Sopenharmony_ci card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase, 11008c2ecf20Sopenharmony_ci (u32)((u64)card->rxbd_ring_pbase >> 32), 11018c2ecf20Sopenharmony_ci card->rxbd_ring_size); 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci ret = mwifiex_init_rxq_ring(adapter); 11048c2ecf20Sopenharmony_ci if (ret) 11058c2ecf20Sopenharmony_ci mwifiex_pcie_delete_rxbd_ring(adapter); 11068c2ecf20Sopenharmony_ci return ret; 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci/* 11108c2ecf20Sopenharmony_ci * This function deletes Buffer descriptor ring for RX 11118c2ecf20Sopenharmony_ci */ 11128c2ecf20Sopenharmony_cistatic int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) 11138c2ecf20Sopenharmony_ci{ 11148c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 11158c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci mwifiex_cleanup_rxq_ring(adapter); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (card->rxbd_ring_vbase) 11208c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, card->rxbd_ring_size, 11218c2ecf20Sopenharmony_ci card->rxbd_ring_vbase, 11228c2ecf20Sopenharmony_ci card->rxbd_ring_pbase); 11238c2ecf20Sopenharmony_ci card->rxbd_ring_size = 0; 11248c2ecf20Sopenharmony_ci card->rxbd_wrptr = 0; 11258c2ecf20Sopenharmony_ci card->rxbd_rdptr = 0 | reg->rx_rollover_ind; 11268c2ecf20Sopenharmony_ci card->rxbd_ring_vbase = NULL; 11278c2ecf20Sopenharmony_ci card->rxbd_ring_pbase = 0; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci return 0; 11308c2ecf20Sopenharmony_ci} 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci/* 11338c2ecf20Sopenharmony_ci * This function creates buffer descriptor ring for Events 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_cistatic int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci int ret; 11388c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 11398c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci /* 11428c2ecf20Sopenharmony_ci * driver maintaines the read pointer and firmware maintaines the write 11438c2ecf20Sopenharmony_ci * pointer. The write pointer starts at 0 (zero) while the read pointer 11448c2ecf20Sopenharmony_ci * starts at zero with rollover bit set 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_ci card->evtbd_wrptr = 0; 11478c2ecf20Sopenharmony_ci card->evtbd_rdptr = reg->evt_rollover_ind; 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci card->evtbd_ring_size = sizeof(struct mwifiex_evt_buf_desc) * 11508c2ecf20Sopenharmony_ci MWIFIEX_MAX_EVT_BD; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 11538c2ecf20Sopenharmony_ci "info: evtbd_ring: Allocating %d bytes\n", 11548c2ecf20Sopenharmony_ci card->evtbd_ring_size); 11558c2ecf20Sopenharmony_ci card->evtbd_ring_vbase = dma_alloc_coherent(&card->dev->dev, 11568c2ecf20Sopenharmony_ci card->evtbd_ring_size, 11578c2ecf20Sopenharmony_ci &card->evtbd_ring_pbase, 11588c2ecf20Sopenharmony_ci GFP_KERNEL); 11598c2ecf20Sopenharmony_ci if (!card->evtbd_ring_vbase) { 11608c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 11618c2ecf20Sopenharmony_ci "allocate coherent memory (%d bytes) failed!\n", 11628c2ecf20Sopenharmony_ci card->evtbd_ring_size); 11638c2ecf20Sopenharmony_ci return -ENOMEM; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 11678c2ecf20Sopenharmony_ci "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n", 11688c2ecf20Sopenharmony_ci card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase, 11698c2ecf20Sopenharmony_ci (u32)((u64)card->evtbd_ring_pbase >> 32), 11708c2ecf20Sopenharmony_ci card->evtbd_ring_size); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci ret = mwifiex_pcie_init_evt_ring(adapter); 11738c2ecf20Sopenharmony_ci if (ret) 11748c2ecf20Sopenharmony_ci mwifiex_pcie_delete_evtbd_ring(adapter); 11758c2ecf20Sopenharmony_ci return ret; 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci/* 11798c2ecf20Sopenharmony_ci * This function deletes Buffer descriptor ring for Events 11808c2ecf20Sopenharmony_ci */ 11818c2ecf20Sopenharmony_cistatic int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) 11828c2ecf20Sopenharmony_ci{ 11838c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 11848c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci mwifiex_cleanup_evt_ring(adapter); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (card->evtbd_ring_vbase) 11898c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, card->evtbd_ring_size, 11908c2ecf20Sopenharmony_ci card->evtbd_ring_vbase, 11918c2ecf20Sopenharmony_ci card->evtbd_ring_pbase); 11928c2ecf20Sopenharmony_ci card->evtbd_wrptr = 0; 11938c2ecf20Sopenharmony_ci card->evtbd_rdptr = 0 | reg->evt_rollover_ind; 11948c2ecf20Sopenharmony_ci card->evtbd_ring_size = 0; 11958c2ecf20Sopenharmony_ci card->evtbd_ring_vbase = NULL; 11968c2ecf20Sopenharmony_ci card->evtbd_ring_pbase = 0; 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return 0; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci/* 12028c2ecf20Sopenharmony_ci * This function allocates a buffer for CMDRSP 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_cistatic int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) 12058c2ecf20Sopenharmony_ci{ 12068c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 12078c2ecf20Sopenharmony_ci struct sk_buff *skb; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* Allocate memory for receiving command response data */ 12108c2ecf20Sopenharmony_ci skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); 12118c2ecf20Sopenharmony_ci if (!skb) { 12128c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 12138c2ecf20Sopenharmony_ci "Unable to allocate skb for command response data.\n"); 12148c2ecf20Sopenharmony_ci return -ENOMEM; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci skb_put(skb, MWIFIEX_UPLD_SIZE); 12178c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, 12188c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) { 12198c2ecf20Sopenharmony_ci kfree_skb(skb); 12208c2ecf20Sopenharmony_ci return -1; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci card->cmdrsp_buf = skb; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci return 0; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci/* 12298c2ecf20Sopenharmony_ci * This function deletes a buffer for CMDRSP 12308c2ecf20Sopenharmony_ci */ 12318c2ecf20Sopenharmony_cistatic int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci struct pcie_service_card *card; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci if (!adapter) 12368c2ecf20Sopenharmony_ci return 0; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci card = adapter->card; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (card && card->cmdrsp_buf) { 12418c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, 12428c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 12438c2ecf20Sopenharmony_ci dev_kfree_skb_any(card->cmdrsp_buf); 12448c2ecf20Sopenharmony_ci card->cmdrsp_buf = NULL; 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci if (card && card->cmd_buf) { 12488c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, card->cmd_buf, 12498c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 12508c2ecf20Sopenharmony_ci dev_kfree_skb_any(card->cmd_buf); 12518c2ecf20Sopenharmony_ci card->cmd_buf = NULL; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci return 0; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/* 12578c2ecf20Sopenharmony_ci * This function allocates a buffer for sleep cookie 12588c2ecf20Sopenharmony_ci */ 12598c2ecf20Sopenharmony_cistatic int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 12628c2ecf20Sopenharmony_ci u32 *cookie; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci card->sleep_cookie_vbase = dma_alloc_coherent(&card->dev->dev, 12658c2ecf20Sopenharmony_ci sizeof(u32), 12668c2ecf20Sopenharmony_ci &card->sleep_cookie_pbase, 12678c2ecf20Sopenharmony_ci GFP_KERNEL); 12688c2ecf20Sopenharmony_ci if (!card->sleep_cookie_vbase) { 12698c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 12708c2ecf20Sopenharmony_ci "dma_alloc_coherent failed!\n"); 12718c2ecf20Sopenharmony_ci return -ENOMEM; 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci cookie = (u32 *)card->sleep_cookie_vbase; 12748c2ecf20Sopenharmony_ci /* Init val of Sleep Cookie */ 12758c2ecf20Sopenharmony_ci *cookie = FW_AWAKE_COOKIE; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "alloc_scook: sleep cookie=0x%x\n", *cookie); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci return 0; 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci/* 12838c2ecf20Sopenharmony_ci * This function deletes buffer for sleep cookie 12848c2ecf20Sopenharmony_ci */ 12858c2ecf20Sopenharmony_cistatic int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci struct pcie_service_card *card; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (!adapter) 12908c2ecf20Sopenharmony_ci return 0; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci card = adapter->card; 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci if (card && card->sleep_cookie_vbase) { 12958c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, sizeof(u32), 12968c2ecf20Sopenharmony_ci card->sleep_cookie_vbase, 12978c2ecf20Sopenharmony_ci card->sleep_cookie_pbase); 12988c2ecf20Sopenharmony_ci card->sleep_cookie_vbase = NULL; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci return 0; 13028c2ecf20Sopenharmony_ci} 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci/* This function flushes the TX buffer descriptor ring 13058c2ecf20Sopenharmony_ci * This function defined as handler is also called while cleaning TXRX 13068c2ecf20Sopenharmony_ci * during disconnect/ bss stop. 13078c2ecf20Sopenharmony_ci */ 13088c2ecf20Sopenharmony_cistatic int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) 13098c2ecf20Sopenharmony_ci{ 13108c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) { 13138c2ecf20Sopenharmony_ci card->txbd_flush = 1; 13148c2ecf20Sopenharmony_ci /* write pointer already set at last send 13158c2ecf20Sopenharmony_ci * send dnld-rdy intr again, wait for completion. 13168c2ecf20Sopenharmony_ci */ 13178c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, 13188c2ecf20Sopenharmony_ci CPU_INTR_DNLD_RDY)) { 13198c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 13208c2ecf20Sopenharmony_ci "failed to assert dnld-rdy interrupt.\n"); 13218c2ecf20Sopenharmony_ci return -1; 13228c2ecf20Sopenharmony_ci } 13238c2ecf20Sopenharmony_ci } 13248c2ecf20Sopenharmony_ci return 0; 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci/* 13288c2ecf20Sopenharmony_ci * This function unmaps and frees downloaded data buffer 13298c2ecf20Sopenharmony_ci */ 13308c2ecf20Sopenharmony_cistatic int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) 13318c2ecf20Sopenharmony_ci{ 13328c2ecf20Sopenharmony_ci struct sk_buff *skb; 13338c2ecf20Sopenharmony_ci u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0; 13348c2ecf20Sopenharmony_ci struct mwifiex_pcie_buf_desc *desc; 13358c2ecf20Sopenharmony_ci struct mwifiex_pfu_buf_desc *desc2; 13368c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 13378c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci if (!mwifiex_pcie_ok_to_access_hw(adapter)) 13408c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci /* Read the TX ring read pointer set by firmware */ 13438c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) { 13448c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 13458c2ecf20Sopenharmony_ci "SEND COMP: failed to read reg->tx_rdptr\n"); 13468c2ecf20Sopenharmony_ci return -1; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 13508c2ecf20Sopenharmony_ci "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n", 13518c2ecf20Sopenharmony_ci card->txbd_rdptr, rdptr); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; 13548c2ecf20Sopenharmony_ci /* free from previous txbd_rdptr to current txbd_rdptr */ 13558c2ecf20Sopenharmony_ci while (((card->txbd_rdptr & reg->tx_mask) != 13568c2ecf20Sopenharmony_ci (rdptr & reg->tx_mask)) || 13578c2ecf20Sopenharmony_ci ((card->txbd_rdptr & reg->tx_rollover_ind) != 13588c2ecf20Sopenharmony_ci (rdptr & reg->tx_rollover_ind))) { 13598c2ecf20Sopenharmony_ci wrdoneidx = (card->txbd_rdptr & reg->tx_mask) >> 13608c2ecf20Sopenharmony_ci reg->tx_start_ptr; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci skb = card->tx_buf_list[wrdoneidx]; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci if (skb) { 13658c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 13668c2ecf20Sopenharmony_ci "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", 13678c2ecf20Sopenharmony_ci skb, wrdoneidx); 13688c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 13698c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci unmap_count++; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci if (card->txbd_flush) 13748c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, 13758c2ecf20Sopenharmony_ci -1); 13768c2ecf20Sopenharmony_ci else 13778c2ecf20Sopenharmony_ci mwifiex_write_data_complete(adapter, skb, 0, 0); 13788c2ecf20Sopenharmony_ci atomic_dec(&adapter->tx_hw_pending); 13798c2ecf20Sopenharmony_ci } 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci card->tx_buf_list[wrdoneidx] = NULL; 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci if (reg->pfu_enabled) { 13848c2ecf20Sopenharmony_ci desc2 = card->txbd_ring[wrdoneidx]; 13858c2ecf20Sopenharmony_ci memset(desc2, 0, sizeof(*desc2)); 13868c2ecf20Sopenharmony_ci } else { 13878c2ecf20Sopenharmony_ci desc = card->txbd_ring[wrdoneidx]; 13888c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci switch (card->dev->device) { 13918c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8766P: 13928c2ecf20Sopenharmony_ci card->txbd_rdptr++; 13938c2ecf20Sopenharmony_ci break; 13948c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8897: 13958c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8997: 13968c2ecf20Sopenharmony_ci card->txbd_rdptr += reg->ring_tx_start_ptr; 13978c2ecf20Sopenharmony_ci break; 13988c2ecf20Sopenharmony_ci } 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci if ((card->txbd_rdptr & reg->tx_mask) == num_tx_buffs) 14028c2ecf20Sopenharmony_ci card->txbd_rdptr = ((card->txbd_rdptr & 14038c2ecf20Sopenharmony_ci reg->tx_rollover_ind) ^ 14048c2ecf20Sopenharmony_ci reg->tx_rollover_ind); 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci if (unmap_count) 14088c2ecf20Sopenharmony_ci adapter->data_sent = false; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci if (card->txbd_flush) { 14118c2ecf20Sopenharmony_ci if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) 14128c2ecf20Sopenharmony_ci card->txbd_flush = 0; 14138c2ecf20Sopenharmony_ci else 14148c2ecf20Sopenharmony_ci mwifiex_clean_pcie_ring_buf(adapter); 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci return 0; 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci/* This function sends data buffer to device. First 4 bytes of payload 14218c2ecf20Sopenharmony_ci * are filled with payload length and payload type. Then this payload 14228c2ecf20Sopenharmony_ci * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. 14238c2ecf20Sopenharmony_ci * Download ready interrupt to FW is deffered if Tx ring is not full and 14248c2ecf20Sopenharmony_ci * additional payload can be accomodated. 14258c2ecf20Sopenharmony_ci * Caller must ensure tx_param parameter to this function is not NULL. 14268c2ecf20Sopenharmony_ci */ 14278c2ecf20Sopenharmony_cistatic int 14288c2ecf20Sopenharmony_cimwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, 14298c2ecf20Sopenharmony_ci struct mwifiex_tx_param *tx_param) 14308c2ecf20Sopenharmony_ci{ 14318c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 14328c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 14338c2ecf20Sopenharmony_ci u32 wrindx, num_tx_buffs, rx_val; 14348c2ecf20Sopenharmony_ci int ret; 14358c2ecf20Sopenharmony_ci dma_addr_t buf_pa; 14368c2ecf20Sopenharmony_ci struct mwifiex_pcie_buf_desc *desc = NULL; 14378c2ecf20Sopenharmony_ci struct mwifiex_pfu_buf_desc *desc2 = NULL; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci if (!(skb->data && skb->len)) { 14408c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14418c2ecf20Sopenharmony_ci "%s(): invalid parameter <%p, %#x>\n", 14428c2ecf20Sopenharmony_ci __func__, skb->data, skb->len); 14438c2ecf20Sopenharmony_ci return -1; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci if (!mwifiex_pcie_ok_to_access_hw(adapter)) 14478c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; 14508c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 14518c2ecf20Sopenharmony_ci "info: SEND DATA: <Rd: %#x, Wr: %#x>\n", 14528c2ecf20Sopenharmony_ci card->txbd_rdptr, card->txbd_wrptr); 14538c2ecf20Sopenharmony_ci if (mwifiex_pcie_txbd_not_full(card)) { 14548c2ecf20Sopenharmony_ci u8 *payload; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci adapter->data_sent = true; 14578c2ecf20Sopenharmony_ci payload = skb->data; 14588c2ecf20Sopenharmony_ci put_unaligned_le16((u16)skb->len, payload + 0); 14598c2ecf20Sopenharmony_ci put_unaligned_le16(MWIFIEX_TYPE_DATA, payload + 2); 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, skb->len, 14628c2ecf20Sopenharmony_ci DMA_TO_DEVICE)) 14638c2ecf20Sopenharmony_ci return -1; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; 14668c2ecf20Sopenharmony_ci buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); 14678c2ecf20Sopenharmony_ci card->tx_buf_list[wrindx] = skb; 14688c2ecf20Sopenharmony_ci atomic_inc(&adapter->tx_hw_pending); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci if (reg->pfu_enabled) { 14718c2ecf20Sopenharmony_ci desc2 = card->txbd_ring[wrindx]; 14728c2ecf20Sopenharmony_ci desc2->paddr = buf_pa; 14738c2ecf20Sopenharmony_ci desc2->len = (u16)skb->len; 14748c2ecf20Sopenharmony_ci desc2->frag_len = (u16)skb->len; 14758c2ecf20Sopenharmony_ci desc2->offset = 0; 14768c2ecf20Sopenharmony_ci desc2->flags = MWIFIEX_BD_FLAG_FIRST_DESC | 14778c2ecf20Sopenharmony_ci MWIFIEX_BD_FLAG_LAST_DESC; 14788c2ecf20Sopenharmony_ci } else { 14798c2ecf20Sopenharmony_ci desc = card->txbd_ring[wrindx]; 14808c2ecf20Sopenharmony_ci desc->paddr = buf_pa; 14818c2ecf20Sopenharmony_ci desc->len = (u16)skb->len; 14828c2ecf20Sopenharmony_ci desc->flags = MWIFIEX_BD_FLAG_FIRST_DESC | 14838c2ecf20Sopenharmony_ci MWIFIEX_BD_FLAG_LAST_DESC; 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci switch (card->dev->device) { 14878c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8766P: 14888c2ecf20Sopenharmony_ci card->txbd_wrptr++; 14898c2ecf20Sopenharmony_ci break; 14908c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8897: 14918c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8997: 14928c2ecf20Sopenharmony_ci card->txbd_wrptr += reg->ring_tx_start_ptr; 14938c2ecf20Sopenharmony_ci break; 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci if ((card->txbd_wrptr & reg->tx_mask) == num_tx_buffs) 14978c2ecf20Sopenharmony_ci card->txbd_wrptr = ((card->txbd_wrptr & 14988c2ecf20Sopenharmony_ci reg->tx_rollover_ind) ^ 14998c2ecf20Sopenharmony_ci reg->tx_rollover_ind); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci rx_val = card->rxbd_rdptr & reg->rx_wrap_mask; 15028c2ecf20Sopenharmony_ci /* Write the TX ring write pointer in to reg->tx_wrptr */ 15038c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->tx_wrptr, 15048c2ecf20Sopenharmony_ci card->txbd_wrptr | rx_val)) { 15058c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15068c2ecf20Sopenharmony_ci "SEND DATA: failed to write reg->tx_wrptr\n"); 15078c2ecf20Sopenharmony_ci ret = -1; 15088c2ecf20Sopenharmony_ci goto done_unmap; 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci /* The firmware (latest version 15.68.19.p21) of the 88W8897 PCIe+USB card 15128c2ecf20Sopenharmony_ci * seems to crash randomly after setting the TX ring write pointer when 15138c2ecf20Sopenharmony_ci * ASPM powersaving is enabled. A workaround seems to be keeping the bus 15148c2ecf20Sopenharmony_ci * busy by reading a random register afterwards. 15158c2ecf20Sopenharmony_ci */ 15168c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, PCI_VENDOR_ID, &rx_val); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci if ((mwifiex_pcie_txbd_not_full(card)) && 15198c2ecf20Sopenharmony_ci tx_param->next_pkt_len) { 15208c2ecf20Sopenharmony_ci /* have more packets and TxBD still can hold more */ 15218c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 15228c2ecf20Sopenharmony_ci "SEND DATA: delay dnld-rdy interrupt.\n"); 15238c2ecf20Sopenharmony_ci adapter->data_sent = false; 15248c2ecf20Sopenharmony_ci } else { 15258c2ecf20Sopenharmony_ci /* Send the TX ready interrupt */ 15268c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, 15278c2ecf20Sopenharmony_ci CPU_INTR_DNLD_RDY)) { 15288c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15298c2ecf20Sopenharmony_ci "SEND DATA: failed to assert dnld-rdy interrupt.\n"); 15308c2ecf20Sopenharmony_ci ret = -1; 15318c2ecf20Sopenharmony_ci goto done_unmap; 15328c2ecf20Sopenharmony_ci } 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 15358c2ecf20Sopenharmony_ci "info: SEND DATA: Updated <Rd: %#x, Wr:\t" 15368c2ecf20Sopenharmony_ci "%#x> and sent packet to firmware successfully\n", 15378c2ecf20Sopenharmony_ci card->txbd_rdptr, card->txbd_wrptr); 15388c2ecf20Sopenharmony_ci } else { 15398c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 15408c2ecf20Sopenharmony_ci "info: TX Ring full, can't send packets to fw\n"); 15418c2ecf20Sopenharmony_ci adapter->data_sent = true; 15428c2ecf20Sopenharmony_ci /* Send the TX ready interrupt */ 15438c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, 15448c2ecf20Sopenharmony_ci CPU_INTR_DNLD_RDY)) 15458c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15468c2ecf20Sopenharmony_ci "SEND DATA: failed to assert door-bell intr\n"); 15478c2ecf20Sopenharmony_ci return -EBUSY; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci return -EINPROGRESS; 15518c2ecf20Sopenharmony_cidone_unmap: 15528c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, DMA_TO_DEVICE); 15538c2ecf20Sopenharmony_ci card->tx_buf_list[wrindx] = NULL; 15548c2ecf20Sopenharmony_ci atomic_dec(&adapter->tx_hw_pending); 15558c2ecf20Sopenharmony_ci if (reg->pfu_enabled) 15568c2ecf20Sopenharmony_ci memset(desc2, 0, sizeof(*desc2)); 15578c2ecf20Sopenharmony_ci else 15588c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci return ret; 15618c2ecf20Sopenharmony_ci} 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci/* 15648c2ecf20Sopenharmony_ci * This function handles received buffer ring and 15658c2ecf20Sopenharmony_ci * dispatches packets to upper 15668c2ecf20Sopenharmony_ci */ 15678c2ecf20Sopenharmony_cistatic int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 15708c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 15718c2ecf20Sopenharmony_ci u32 wrptr, rd_index, tx_val; 15728c2ecf20Sopenharmony_ci dma_addr_t buf_pa; 15738c2ecf20Sopenharmony_ci int ret = 0; 15748c2ecf20Sopenharmony_ci struct sk_buff *skb_tmp = NULL; 15758c2ecf20Sopenharmony_ci struct mwifiex_pcie_buf_desc *desc; 15768c2ecf20Sopenharmony_ci struct mwifiex_pfu_buf_desc *desc2; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci if (!mwifiex_pcie_ok_to_access_hw(adapter)) 15798c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci /* Read the RX ring Write pointer set by firmware */ 15828c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { 15838c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15848c2ecf20Sopenharmony_ci "RECV DATA: failed to read reg->rx_wrptr\n"); 15858c2ecf20Sopenharmony_ci ret = -1; 15868c2ecf20Sopenharmony_ci goto done; 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci card->rxbd_wrptr = wrptr; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci while (((wrptr & reg->rx_mask) != 15918c2ecf20Sopenharmony_ci (card->rxbd_rdptr & reg->rx_mask)) || 15928c2ecf20Sopenharmony_ci ((wrptr & reg->rx_rollover_ind) == 15938c2ecf20Sopenharmony_ci (card->rxbd_rdptr & reg->rx_rollover_ind))) { 15948c2ecf20Sopenharmony_ci struct sk_buff *skb_data; 15958c2ecf20Sopenharmony_ci u16 rx_len; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci rd_index = card->rxbd_rdptr & reg->rx_mask; 15988c2ecf20Sopenharmony_ci skb_data = card->rx_buf_list[rd_index]; 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci /* If skb allocation was failed earlier for Rx packet, 16018c2ecf20Sopenharmony_ci * rx_buf_list[rd_index] would have been left with a NULL. 16028c2ecf20Sopenharmony_ci */ 16038c2ecf20Sopenharmony_ci if (!skb_data) 16048c2ecf20Sopenharmony_ci return -ENOMEM; 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb_data, DMA_FROM_DEVICE); 16078c2ecf20Sopenharmony_ci card->rx_buf_list[rd_index] = NULL; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci /* Get data length from interface header - 16108c2ecf20Sopenharmony_ci * first 2 bytes for len, next 2 bytes is for type 16118c2ecf20Sopenharmony_ci */ 16128c2ecf20Sopenharmony_ci rx_len = get_unaligned_le16(skb_data->data); 16138c2ecf20Sopenharmony_ci if (WARN_ON(rx_len <= adapter->intf_hdr_len || 16148c2ecf20Sopenharmony_ci rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) { 16158c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 16168c2ecf20Sopenharmony_ci "Invalid RX len %d, Rd=%#x, Wr=%#x\n", 16178c2ecf20Sopenharmony_ci rx_len, card->rxbd_rdptr, wrptr); 16188c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb_data); 16198c2ecf20Sopenharmony_ci } else { 16208c2ecf20Sopenharmony_ci skb_put(skb_data, rx_len); 16218c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 16228c2ecf20Sopenharmony_ci "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", 16238c2ecf20Sopenharmony_ci card->rxbd_rdptr, wrptr, rx_len); 16248c2ecf20Sopenharmony_ci skb_pull(skb_data, adapter->intf_hdr_len); 16258c2ecf20Sopenharmony_ci if (adapter->rx_work_enabled) { 16268c2ecf20Sopenharmony_ci skb_queue_tail(&adapter->rx_data_q, skb_data); 16278c2ecf20Sopenharmony_ci adapter->data_received = true; 16288c2ecf20Sopenharmony_ci atomic_inc(&adapter->rx_pending); 16298c2ecf20Sopenharmony_ci } else { 16308c2ecf20Sopenharmony_ci mwifiex_handle_rx_packet(adapter, skb_data); 16318c2ecf20Sopenharmony_ci } 16328c2ecf20Sopenharmony_ci } 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, 16358c2ecf20Sopenharmony_ci GFP_KERNEL); 16368c2ecf20Sopenharmony_ci if (!skb_tmp) { 16378c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 16388c2ecf20Sopenharmony_ci "Unable to allocate skb.\n"); 16398c2ecf20Sopenharmony_ci return -ENOMEM; 16408c2ecf20Sopenharmony_ci } 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb_tmp, 16438c2ecf20Sopenharmony_ci MWIFIEX_RX_DATA_BUF_SIZE, 16448c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) 16458c2ecf20Sopenharmony_ci return -1; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp); 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 16508c2ecf20Sopenharmony_ci "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", 16518c2ecf20Sopenharmony_ci skb_tmp, rd_index); 16528c2ecf20Sopenharmony_ci card->rx_buf_list[rd_index] = skb_tmp; 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if (reg->pfu_enabled) { 16558c2ecf20Sopenharmony_ci desc2 = card->rxbd_ring[rd_index]; 16568c2ecf20Sopenharmony_ci desc2->paddr = buf_pa; 16578c2ecf20Sopenharmony_ci desc2->len = skb_tmp->len; 16588c2ecf20Sopenharmony_ci desc2->frag_len = skb_tmp->len; 16598c2ecf20Sopenharmony_ci desc2->offset = 0; 16608c2ecf20Sopenharmony_ci desc2->flags = reg->ring_flag_sop | reg->ring_flag_eop; 16618c2ecf20Sopenharmony_ci } else { 16628c2ecf20Sopenharmony_ci desc = card->rxbd_ring[rd_index]; 16638c2ecf20Sopenharmony_ci desc->paddr = buf_pa; 16648c2ecf20Sopenharmony_ci desc->len = skb_tmp->len; 16658c2ecf20Sopenharmony_ci desc->flags = 0; 16668c2ecf20Sopenharmony_ci } 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci if ((++card->rxbd_rdptr & reg->rx_mask) == 16698c2ecf20Sopenharmony_ci MWIFIEX_MAX_TXRX_BD) { 16708c2ecf20Sopenharmony_ci card->rxbd_rdptr = ((card->rxbd_rdptr & 16718c2ecf20Sopenharmony_ci reg->rx_rollover_ind) ^ 16728c2ecf20Sopenharmony_ci reg->rx_rollover_ind); 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 16758c2ecf20Sopenharmony_ci "info: RECV DATA: <Rd: %#x, Wr: %#x>\n", 16768c2ecf20Sopenharmony_ci card->rxbd_rdptr, wrptr); 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci tx_val = card->txbd_wrptr & reg->tx_wrap_mask; 16798c2ecf20Sopenharmony_ci /* Write the RX ring read pointer in to reg->rx_rdptr */ 16808c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->rx_rdptr, 16818c2ecf20Sopenharmony_ci card->rxbd_rdptr | tx_val)) { 16828c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 16838c2ecf20Sopenharmony_ci "RECV DATA: failed to write reg->rx_rdptr\n"); 16848c2ecf20Sopenharmony_ci ret = -1; 16858c2ecf20Sopenharmony_ci goto done; 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci /* Read the RX ring Write pointer set by firmware */ 16898c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { 16908c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 16918c2ecf20Sopenharmony_ci "RECV DATA: failed to read reg->rx_wrptr\n"); 16928c2ecf20Sopenharmony_ci ret = -1; 16938c2ecf20Sopenharmony_ci goto done; 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 16968c2ecf20Sopenharmony_ci "info: RECV DATA: Rcvd packet from fw successfully\n"); 16978c2ecf20Sopenharmony_ci card->rxbd_wrptr = wrptr; 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_cidone: 17018c2ecf20Sopenharmony_ci return ret; 17028c2ecf20Sopenharmony_ci} 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci/* 17058c2ecf20Sopenharmony_ci * This function downloads the boot command to device 17068c2ecf20Sopenharmony_ci */ 17078c2ecf20Sopenharmony_cistatic int 17088c2ecf20Sopenharmony_cimwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) 17098c2ecf20Sopenharmony_ci{ 17108c2ecf20Sopenharmony_ci dma_addr_t buf_pa; 17118c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 17128c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci if (!(skb->data && skb->len)) { 17158c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 17168c2ecf20Sopenharmony_ci "Invalid parameter in %s <%p. len %d>\n", 17178c2ecf20Sopenharmony_ci __func__, skb->data, skb->len); 17188c2ecf20Sopenharmony_ci return -1; 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, skb->len, DMA_TO_DEVICE)) 17228c2ecf20Sopenharmony_ci return -1; 17238c2ecf20Sopenharmony_ci 17248c2ecf20Sopenharmony_ci buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci /* Write the lower 32bits of the physical address to low command 17278c2ecf20Sopenharmony_ci * address scratch register 17288c2ecf20Sopenharmony_ci */ 17298c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa)) { 17308c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 17318c2ecf20Sopenharmony_ci "%s: failed to write download command to boot code.\n", 17328c2ecf20Sopenharmony_ci __func__); 17338c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, DMA_TO_DEVICE); 17348c2ecf20Sopenharmony_ci return -1; 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci /* Write the upper 32bits of the physical address to high command 17388c2ecf20Sopenharmony_ci * address scratch register 17398c2ecf20Sopenharmony_ci */ 17408c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, 17418c2ecf20Sopenharmony_ci (u32)((u64)buf_pa >> 32))) { 17428c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 17438c2ecf20Sopenharmony_ci "%s: failed to write download command to boot code.\n", 17448c2ecf20Sopenharmony_ci __func__); 17458c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, DMA_TO_DEVICE); 17468c2ecf20Sopenharmony_ci return -1; 17478c2ecf20Sopenharmony_ci } 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci /* Write the command length to cmd_size scratch register */ 17508c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmd_size, skb->len)) { 17518c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 17528c2ecf20Sopenharmony_ci "%s: failed to write command len to cmd_size scratch reg\n", 17538c2ecf20Sopenharmony_ci __func__); 17548c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, DMA_TO_DEVICE); 17558c2ecf20Sopenharmony_ci return -1; 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci /* Ring the door bell */ 17598c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, 17608c2ecf20Sopenharmony_ci CPU_INTR_DOOR_BELL)) { 17618c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 17628c2ecf20Sopenharmony_ci "%s: failed to assert door-bell intr\n", __func__); 17638c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, DMA_TO_DEVICE); 17648c2ecf20Sopenharmony_ci return -1; 17658c2ecf20Sopenharmony_ci } 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci return 0; 17688c2ecf20Sopenharmony_ci} 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci/* This function init rx port in firmware which in turn enables to receive data 17718c2ecf20Sopenharmony_ci * from device before transmitting any packet. 17728c2ecf20Sopenharmony_ci */ 17738c2ecf20Sopenharmony_cistatic int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) 17748c2ecf20Sopenharmony_ci{ 17758c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 17768c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 17778c2ecf20Sopenharmony_ci int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci /* Write the RX ring read pointer in to reg->rx_rdptr */ 17808c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | 17818c2ecf20Sopenharmony_ci tx_wrap)) { 17828c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 17838c2ecf20Sopenharmony_ci "RECV DATA: failed to write reg->rx_rdptr\n"); 17848c2ecf20Sopenharmony_ci return -1; 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci return 0; 17878c2ecf20Sopenharmony_ci} 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci/* This function downloads commands to the device 17908c2ecf20Sopenharmony_ci */ 17918c2ecf20Sopenharmony_cistatic int 17928c2ecf20Sopenharmony_cimwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) 17938c2ecf20Sopenharmony_ci{ 17948c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 17958c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 17968c2ecf20Sopenharmony_ci int ret = 0; 17978c2ecf20Sopenharmony_ci dma_addr_t cmd_buf_pa, cmdrsp_buf_pa; 17988c2ecf20Sopenharmony_ci u8 *payload = (u8 *)skb->data; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (!(skb->data && skb->len)) { 18018c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18028c2ecf20Sopenharmony_ci "Invalid parameter in %s <%p, %#x>\n", 18038c2ecf20Sopenharmony_ci __func__, skb->data, skb->len); 18048c2ecf20Sopenharmony_ci return -1; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci /* Make sure a command response buffer is available */ 18088c2ecf20Sopenharmony_ci if (!card->cmdrsp_buf) { 18098c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18108c2ecf20Sopenharmony_ci "No response buffer available, send command failed\n"); 18118c2ecf20Sopenharmony_ci return -EBUSY; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci if (!mwifiex_pcie_ok_to_access_hw(adapter)) 18158c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci adapter->cmd_sent = true; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci put_unaligned_le16((u16)skb->len, &payload[0]); 18208c2ecf20Sopenharmony_ci put_unaligned_le16(MWIFIEX_TYPE_CMD, &payload[2]); 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, skb->len, DMA_TO_DEVICE)) 18238c2ecf20Sopenharmony_ci return -1; 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci card->cmd_buf = skb; 18268c2ecf20Sopenharmony_ci /* 18278c2ecf20Sopenharmony_ci * Need to keep a reference, since core driver might free up this 18288c2ecf20Sopenharmony_ci * buffer before we've unmapped it. 18298c2ecf20Sopenharmony_ci */ 18308c2ecf20Sopenharmony_ci skb_get(skb); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* To send a command, the driver will: 18338c2ecf20Sopenharmony_ci 1. Write the 64bit physical address of the data buffer to 18348c2ecf20Sopenharmony_ci cmd response address low + cmd response address high 18358c2ecf20Sopenharmony_ci 2. Ring the door bell (i.e. set the door bell interrupt) 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci In response to door bell interrupt, the firmware will perform 18388c2ecf20Sopenharmony_ci the DMA of the command packet (first header to obtain the total 18398c2ecf20Sopenharmony_ci length and then rest of the command). 18408c2ecf20Sopenharmony_ci */ 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci if (card->cmdrsp_buf) { 18438c2ecf20Sopenharmony_ci cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); 18448c2ecf20Sopenharmony_ci /* Write the lower 32bits of the cmdrsp buffer physical 18458c2ecf20Sopenharmony_ci address */ 18468c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 18478c2ecf20Sopenharmony_ci (u32)cmdrsp_buf_pa)) { 18488c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18498c2ecf20Sopenharmony_ci "Failed to write download cmd to boot code.\n"); 18508c2ecf20Sopenharmony_ci ret = -1; 18518c2ecf20Sopenharmony_ci goto done; 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci /* Write the upper 32bits of the cmdrsp buffer physical 18548c2ecf20Sopenharmony_ci address */ 18558c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 18568c2ecf20Sopenharmony_ci (u32)((u64)cmdrsp_buf_pa >> 32))) { 18578c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18588c2ecf20Sopenharmony_ci "Failed to write download cmd to boot code.\n"); 18598c2ecf20Sopenharmony_ci ret = -1; 18608c2ecf20Sopenharmony_ci goto done; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf); 18658c2ecf20Sopenharmony_ci /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */ 18668c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, 18678c2ecf20Sopenharmony_ci (u32)cmd_buf_pa)) { 18688c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18698c2ecf20Sopenharmony_ci "Failed to write download cmd to boot code.\n"); 18708c2ecf20Sopenharmony_ci ret = -1; 18718c2ecf20Sopenharmony_ci goto done; 18728c2ecf20Sopenharmony_ci } 18738c2ecf20Sopenharmony_ci /* Write the upper 32bits of the physical address to reg->cmd_addr_hi */ 18748c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, 18758c2ecf20Sopenharmony_ci (u32)((u64)cmd_buf_pa >> 32))) { 18768c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18778c2ecf20Sopenharmony_ci "Failed to write download cmd to boot code.\n"); 18788c2ecf20Sopenharmony_ci ret = -1; 18798c2ecf20Sopenharmony_ci goto done; 18808c2ecf20Sopenharmony_ci } 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci /* Write the command length to reg->cmd_size */ 18838c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmd_size, 18848c2ecf20Sopenharmony_ci card->cmd_buf->len)) { 18858c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18868c2ecf20Sopenharmony_ci "Failed to write cmd len to reg->cmd_size\n"); 18878c2ecf20Sopenharmony_ci ret = -1; 18888c2ecf20Sopenharmony_ci goto done; 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci /* Ring the door bell */ 18928c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, 18938c2ecf20Sopenharmony_ci CPU_INTR_DOOR_BELL)) { 18948c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18958c2ecf20Sopenharmony_ci "Failed to assert door-bell intr\n"); 18968c2ecf20Sopenharmony_ci ret = -1; 18978c2ecf20Sopenharmony_ci goto done; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_cidone: 19018c2ecf20Sopenharmony_ci if (ret) 19028c2ecf20Sopenharmony_ci adapter->cmd_sent = false; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci return 0; 19058c2ecf20Sopenharmony_ci} 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci/* 19088c2ecf20Sopenharmony_ci * This function handles command complete interrupt 19098c2ecf20Sopenharmony_ci */ 19108c2ecf20Sopenharmony_cistatic int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) 19118c2ecf20Sopenharmony_ci{ 19128c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 19138c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 19148c2ecf20Sopenharmony_ci struct sk_buff *skb = card->cmdrsp_buf; 19158c2ecf20Sopenharmony_ci int count = 0; 19168c2ecf20Sopenharmony_ci u16 rx_len; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, CMD, 19198c2ecf20Sopenharmony_ci "info: Rx CMD Response\n"); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci if (adapter->curr_cmd) 19228c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, DMA_FROM_DEVICE); 19238c2ecf20Sopenharmony_ci else 19248c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&card->dev->dev, 19258c2ecf20Sopenharmony_ci MWIFIEX_SKB_DMA_ADDR(skb), 19268c2ecf20Sopenharmony_ci MWIFIEX_UPLD_SIZE, DMA_FROM_DEVICE); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci /* Unmap the command as a response has been received. */ 19298c2ecf20Sopenharmony_ci if (card->cmd_buf) { 19308c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, card->cmd_buf, 19318c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 19328c2ecf20Sopenharmony_ci dev_kfree_skb_any(card->cmd_buf); 19338c2ecf20Sopenharmony_ci card->cmd_buf = NULL; 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci rx_len = get_unaligned_le16(skb->data); 19378c2ecf20Sopenharmony_ci skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); 19388c2ecf20Sopenharmony_ci skb_trim(skb, rx_len); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci if (!adapter->curr_cmd) { 19418c2ecf20Sopenharmony_ci if (adapter->ps_state == PS_STATE_SLEEP_CFM) { 19428c2ecf20Sopenharmony_ci dma_sync_single_for_device(&card->dev->dev, 19438c2ecf20Sopenharmony_ci MWIFIEX_SKB_DMA_ADDR(skb), 19448c2ecf20Sopenharmony_ci MWIFIEX_SLEEP_COOKIE_SIZE, 19458c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 19468c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, 19478c2ecf20Sopenharmony_ci PCIE_CPU_INT_EVENT, 19488c2ecf20Sopenharmony_ci CPU_INTR_SLEEP_CFM_DONE)) { 19498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 19508c2ecf20Sopenharmony_ci "Write register failed\n"); 19518c2ecf20Sopenharmony_ci return -1; 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci mwifiex_delay_for_sleep_cookie(adapter, 19548c2ecf20Sopenharmony_ci MWIFIEX_MAX_DELAY_COUNT); 19558c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 19568c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 19578c2ecf20Sopenharmony_ci skb_pull(skb, adapter->intf_hdr_len); 19588c2ecf20Sopenharmony_ci while (reg->sleep_cookie && (count++ < 10) && 19598c2ecf20Sopenharmony_ci mwifiex_pcie_ok_to_access_hw(adapter)) 19608c2ecf20Sopenharmony_ci usleep_range(50, 60); 19618c2ecf20Sopenharmony_ci mwifiex_pcie_enable_host_int(adapter); 19628c2ecf20Sopenharmony_ci mwifiex_process_sleep_confirm_resp(adapter, skb->data, 19638c2ecf20Sopenharmony_ci skb->len); 19648c2ecf20Sopenharmony_ci } else { 19658c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 19668c2ecf20Sopenharmony_ci "There is no command but got cmdrsp\n"); 19678c2ecf20Sopenharmony_ci } 19688c2ecf20Sopenharmony_ci memcpy(adapter->upld_buf, skb->data, 19698c2ecf20Sopenharmony_ci min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); 19708c2ecf20Sopenharmony_ci skb_push(skb, adapter->intf_hdr_len); 19718c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, 19728c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) 19738c2ecf20Sopenharmony_ci return -1; 19748c2ecf20Sopenharmony_ci } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { 19758c2ecf20Sopenharmony_ci skb_pull(skb, adapter->intf_hdr_len); 19768c2ecf20Sopenharmony_ci adapter->curr_cmd->resp_skb = skb; 19778c2ecf20Sopenharmony_ci adapter->cmd_resp_received = true; 19788c2ecf20Sopenharmony_ci /* Take the pointer and set it to CMD node and will 19798c2ecf20Sopenharmony_ci return in the response complete callback */ 19808c2ecf20Sopenharmony_ci card->cmdrsp_buf = NULL; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci /* Clear the cmd-rsp buffer address in scratch registers. This 19838c2ecf20Sopenharmony_ci will prevent firmware from writing to the same response 19848c2ecf20Sopenharmony_ci buffer again. */ 19858c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0)) { 19868c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 19878c2ecf20Sopenharmony_ci "cmd_done: failed to clear cmd_rsp_addr_lo\n"); 19888c2ecf20Sopenharmony_ci return -1; 19898c2ecf20Sopenharmony_ci } 19908c2ecf20Sopenharmony_ci /* Write the upper 32bits of the cmdrsp buffer physical 19918c2ecf20Sopenharmony_ci address */ 19928c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0)) { 19938c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 19948c2ecf20Sopenharmony_ci "cmd_done: failed to clear cmd_rsp_addr_hi\n"); 19958c2ecf20Sopenharmony_ci return -1; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci return 0; 20008c2ecf20Sopenharmony_ci} 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci/* 20038c2ecf20Sopenharmony_ci * Command Response processing complete handler 20048c2ecf20Sopenharmony_ci */ 20058c2ecf20Sopenharmony_cistatic int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, 20068c2ecf20Sopenharmony_ci struct sk_buff *skb) 20078c2ecf20Sopenharmony_ci{ 20088c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci if (skb) { 20118c2ecf20Sopenharmony_ci card->cmdrsp_buf = skb; 20128c2ecf20Sopenharmony_ci skb_push(card->cmdrsp_buf, adapter->intf_hdr_len); 20138c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, 20148c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) 20158c2ecf20Sopenharmony_ci return -1; 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci return 0; 20198c2ecf20Sopenharmony_ci} 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci/* 20228c2ecf20Sopenharmony_ci * This function handles firmware event ready interrupt 20238c2ecf20Sopenharmony_ci */ 20248c2ecf20Sopenharmony_cistatic int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) 20258c2ecf20Sopenharmony_ci{ 20268c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 20278c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 20288c2ecf20Sopenharmony_ci u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; 20298c2ecf20Sopenharmony_ci u32 wrptr, event; 20308c2ecf20Sopenharmony_ci struct mwifiex_evt_buf_desc *desc; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci if (!mwifiex_pcie_ok_to_access_hw(adapter)) 20338c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci if (adapter->event_received) { 20368c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 20378c2ecf20Sopenharmony_ci "info: Event being processed,\t" 20388c2ecf20Sopenharmony_ci "do not process this interrupt just yet\n"); 20398c2ecf20Sopenharmony_ci return 0; 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (rdptr >= MWIFIEX_MAX_EVT_BD) { 20438c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 20448c2ecf20Sopenharmony_ci "info: Invalid read pointer...\n"); 20458c2ecf20Sopenharmony_ci return -1; 20468c2ecf20Sopenharmony_ci } 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci /* Read the event ring write pointer set by firmware */ 20498c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { 20508c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 20518c2ecf20Sopenharmony_ci "EventReady: failed to read reg->evt_wrptr\n"); 20528c2ecf20Sopenharmony_ci return -1; 20538c2ecf20Sopenharmony_ci } 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 20568c2ecf20Sopenharmony_ci "info: EventReady: Initial <Rd: 0x%x, Wr: 0x%x>", 20578c2ecf20Sopenharmony_ci card->evtbd_rdptr, wrptr); 20588c2ecf20Sopenharmony_ci if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr 20598c2ecf20Sopenharmony_ci & MWIFIEX_EVTBD_MASK)) || 20608c2ecf20Sopenharmony_ci ((wrptr & reg->evt_rollover_ind) == 20618c2ecf20Sopenharmony_ci (card->evtbd_rdptr & reg->evt_rollover_ind))) { 20628c2ecf20Sopenharmony_ci struct sk_buff *skb_cmd; 20638c2ecf20Sopenharmony_ci __le16 data_len = 0; 20648c2ecf20Sopenharmony_ci u16 evt_len; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 20678c2ecf20Sopenharmony_ci "info: Read Index: %d\n", rdptr); 20688c2ecf20Sopenharmony_ci skb_cmd = card->evt_buf_list[rdptr]; 20698c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb_cmd, DMA_FROM_DEVICE); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_ci /* Take the pointer and set it to event pointer in adapter 20728c2ecf20Sopenharmony_ci and will return back after event handling callback */ 20738c2ecf20Sopenharmony_ci card->evt_buf_list[rdptr] = NULL; 20748c2ecf20Sopenharmony_ci desc = card->evtbd_ring[rdptr]; 20758c2ecf20Sopenharmony_ci memset(desc, 0, sizeof(*desc)); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci event = get_unaligned_le32( 20788c2ecf20Sopenharmony_ci &skb_cmd->data[adapter->intf_hdr_len]); 20798c2ecf20Sopenharmony_ci adapter->event_cause = event; 20808c2ecf20Sopenharmony_ci /* The first 4bytes will be the event transfer header 20818c2ecf20Sopenharmony_ci len is 2 bytes followed by type which is 2 bytes */ 20828c2ecf20Sopenharmony_ci memcpy(&data_len, skb_cmd->data, sizeof(__le16)); 20838c2ecf20Sopenharmony_ci evt_len = le16_to_cpu(data_len); 20848c2ecf20Sopenharmony_ci skb_trim(skb_cmd, evt_len); 20858c2ecf20Sopenharmony_ci skb_pull(skb_cmd, adapter->intf_hdr_len); 20868c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 20878c2ecf20Sopenharmony_ci "info: Event length: %d\n", evt_len); 20888c2ecf20Sopenharmony_ci 20898c2ecf20Sopenharmony_ci if (evt_len > MWIFIEX_EVENT_HEADER_LEN && 20908c2ecf20Sopenharmony_ci evt_len < MAX_EVENT_SIZE) 20918c2ecf20Sopenharmony_ci memcpy(adapter->event_body, skb_cmd->data + 20928c2ecf20Sopenharmony_ci MWIFIEX_EVENT_HEADER_LEN, evt_len - 20938c2ecf20Sopenharmony_ci MWIFIEX_EVENT_HEADER_LEN); 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci adapter->event_received = true; 20968c2ecf20Sopenharmony_ci adapter->event_skb = skb_cmd; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci /* Do not update the event read pointer here, wait till the 20998c2ecf20Sopenharmony_ci buffer is released. This is just to make things simpler, 21008c2ecf20Sopenharmony_ci we need to find a better method of managing these buffers. 21018c2ecf20Sopenharmony_ci */ 21028c2ecf20Sopenharmony_ci } else { 21038c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, 21048c2ecf20Sopenharmony_ci CPU_INTR_EVENT_DONE)) { 21058c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 21068c2ecf20Sopenharmony_ci "Write register failed\n"); 21078c2ecf20Sopenharmony_ci return -1; 21088c2ecf20Sopenharmony_ci } 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci return 0; 21128c2ecf20Sopenharmony_ci} 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci/* 21158c2ecf20Sopenharmony_ci * Event processing complete handler 21168c2ecf20Sopenharmony_ci */ 21178c2ecf20Sopenharmony_cistatic int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, 21188c2ecf20Sopenharmony_ci struct sk_buff *skb) 21198c2ecf20Sopenharmony_ci{ 21208c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 21218c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 21228c2ecf20Sopenharmony_ci int ret = 0; 21238c2ecf20Sopenharmony_ci u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; 21248c2ecf20Sopenharmony_ci u32 wrptr; 21258c2ecf20Sopenharmony_ci struct mwifiex_evt_buf_desc *desc; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci if (!skb) 21288c2ecf20Sopenharmony_ci return 0; 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci if (rdptr >= MWIFIEX_MAX_EVT_BD) { 21318c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 21328c2ecf20Sopenharmony_ci "event_complete: Invalid rdptr 0x%x\n", 21338c2ecf20Sopenharmony_ci rdptr); 21348c2ecf20Sopenharmony_ci return -EINVAL; 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci /* Read the event ring write pointer set by firmware */ 21388c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { 21398c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 21408c2ecf20Sopenharmony_ci "event_complete: failed to read reg->evt_wrptr\n"); 21418c2ecf20Sopenharmony_ci return -1; 21428c2ecf20Sopenharmony_ci } 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci if (!card->evt_buf_list[rdptr]) { 21458c2ecf20Sopenharmony_ci skb_push(skb, adapter->intf_hdr_len); 21468c2ecf20Sopenharmony_ci skb_put(skb, MAX_EVENT_SIZE - skb->len); 21478c2ecf20Sopenharmony_ci if (mwifiex_map_pci_memory(adapter, skb, 21488c2ecf20Sopenharmony_ci MAX_EVENT_SIZE, 21498c2ecf20Sopenharmony_ci DMA_FROM_DEVICE)) 21508c2ecf20Sopenharmony_ci return -1; 21518c2ecf20Sopenharmony_ci card->evt_buf_list[rdptr] = skb; 21528c2ecf20Sopenharmony_ci desc = card->evtbd_ring[rdptr]; 21538c2ecf20Sopenharmony_ci desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb); 21548c2ecf20Sopenharmony_ci desc->len = (u16)skb->len; 21558c2ecf20Sopenharmony_ci desc->flags = 0; 21568c2ecf20Sopenharmony_ci skb = NULL; 21578c2ecf20Sopenharmony_ci } else { 21588c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 21598c2ecf20Sopenharmony_ci "info: ERROR: buf still valid at index %d, <%p, %p>\n", 21608c2ecf20Sopenharmony_ci rdptr, card->evt_buf_list[rdptr], skb); 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) { 21648c2ecf20Sopenharmony_ci card->evtbd_rdptr = ((card->evtbd_rdptr & 21658c2ecf20Sopenharmony_ci reg->evt_rollover_ind) ^ 21668c2ecf20Sopenharmony_ci reg->evt_rollover_ind); 21678c2ecf20Sopenharmony_ci } 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 21708c2ecf20Sopenharmony_ci "info: Updated <Rd: 0x%x, Wr: 0x%x>", 21718c2ecf20Sopenharmony_ci card->evtbd_rdptr, wrptr); 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* Write the event ring read pointer in to reg->evt_rdptr */ 21748c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->evt_rdptr, 21758c2ecf20Sopenharmony_ci card->evtbd_rdptr)) { 21768c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 21778c2ecf20Sopenharmony_ci "event_complete: failed to read reg->evt_rdptr\n"); 21788c2ecf20Sopenharmony_ci return -1; 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 21828c2ecf20Sopenharmony_ci "info: Check Events Again\n"); 21838c2ecf20Sopenharmony_ci ret = mwifiex_pcie_process_event_ready(adapter); 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci return ret; 21868c2ecf20Sopenharmony_ci} 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci/* Combo firmware image is a combination of 21898c2ecf20Sopenharmony_ci * (1) combo crc heaer, start with CMD5 21908c2ecf20Sopenharmony_ci * (2) bluetooth image, start with CMD7, end with CMD6, data wrapped in CMD1. 21918c2ecf20Sopenharmony_ci * (3) wifi image. 21928c2ecf20Sopenharmony_ci * 21938c2ecf20Sopenharmony_ci * This function bypass the header and bluetooth part, return 21948c2ecf20Sopenharmony_ci * the offset of tail wifi-only part. If the image is already wifi-only, 21958c2ecf20Sopenharmony_ci * that is start with CMD1, return 0. 21968c2ecf20Sopenharmony_ci */ 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_cistatic int mwifiex_extract_wifi_fw(struct mwifiex_adapter *adapter, 21998c2ecf20Sopenharmony_ci const void *firmware, u32 firmware_len) { 22008c2ecf20Sopenharmony_ci const struct mwifiex_fw_data *fwdata; 22018c2ecf20Sopenharmony_ci u32 offset = 0, data_len, dnld_cmd; 22028c2ecf20Sopenharmony_ci int ret = 0; 22038c2ecf20Sopenharmony_ci bool cmd7_before = false, first_cmd = false; 22048c2ecf20Sopenharmony_ci 22058c2ecf20Sopenharmony_ci while (1) { 22068c2ecf20Sopenharmony_ci /* Check for integer and buffer overflow */ 22078c2ecf20Sopenharmony_ci if (offset + sizeof(fwdata->header) < sizeof(fwdata->header) || 22088c2ecf20Sopenharmony_ci offset + sizeof(fwdata->header) >= firmware_len) { 22098c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 22108c2ecf20Sopenharmony_ci "extract wifi-only fw failure!\n"); 22118c2ecf20Sopenharmony_ci ret = -1; 22128c2ecf20Sopenharmony_ci goto done; 22138c2ecf20Sopenharmony_ci } 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci fwdata = firmware + offset; 22168c2ecf20Sopenharmony_ci dnld_cmd = le32_to_cpu(fwdata->header.dnld_cmd); 22178c2ecf20Sopenharmony_ci data_len = le32_to_cpu(fwdata->header.data_length); 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci /* Skip past header */ 22208c2ecf20Sopenharmony_ci offset += sizeof(fwdata->header); 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci switch (dnld_cmd) { 22238c2ecf20Sopenharmony_ci case MWIFIEX_FW_DNLD_CMD_1: 22248c2ecf20Sopenharmony_ci if (offset + data_len < data_len) { 22258c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); 22268c2ecf20Sopenharmony_ci ret = -1; 22278c2ecf20Sopenharmony_ci goto done; 22288c2ecf20Sopenharmony_ci } 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci /* Image start with cmd1, already wifi-only firmware */ 22318c2ecf20Sopenharmony_ci if (!first_cmd) { 22328c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, 22338c2ecf20Sopenharmony_ci "input wifi-only firmware\n"); 22348c2ecf20Sopenharmony_ci return 0; 22358c2ecf20Sopenharmony_ci } 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci if (!cmd7_before) { 22388c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 22398c2ecf20Sopenharmony_ci "no cmd7 before cmd1!\n"); 22408c2ecf20Sopenharmony_ci ret = -1; 22418c2ecf20Sopenharmony_ci goto done; 22428c2ecf20Sopenharmony_ci } 22438c2ecf20Sopenharmony_ci offset += data_len; 22448c2ecf20Sopenharmony_ci break; 22458c2ecf20Sopenharmony_ci case MWIFIEX_FW_DNLD_CMD_5: 22468c2ecf20Sopenharmony_ci first_cmd = true; 22478c2ecf20Sopenharmony_ci /* Check for integer overflow */ 22488c2ecf20Sopenharmony_ci if (offset + data_len < data_len) { 22498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); 22508c2ecf20Sopenharmony_ci ret = -1; 22518c2ecf20Sopenharmony_ci goto done; 22528c2ecf20Sopenharmony_ci } 22538c2ecf20Sopenharmony_ci offset += data_len; 22548c2ecf20Sopenharmony_ci break; 22558c2ecf20Sopenharmony_ci case MWIFIEX_FW_DNLD_CMD_6: 22568c2ecf20Sopenharmony_ci first_cmd = true; 22578c2ecf20Sopenharmony_ci /* Check for integer overflow */ 22588c2ecf20Sopenharmony_ci if (offset + data_len < data_len) { 22598c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "bad FW parse\n"); 22608c2ecf20Sopenharmony_ci ret = -1; 22618c2ecf20Sopenharmony_ci goto done; 22628c2ecf20Sopenharmony_ci } 22638c2ecf20Sopenharmony_ci offset += data_len; 22648c2ecf20Sopenharmony_ci if (offset >= firmware_len) { 22658c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 22668c2ecf20Sopenharmony_ci "extract wifi-only fw failure!\n"); 22678c2ecf20Sopenharmony_ci ret = -1; 22688c2ecf20Sopenharmony_ci } else { 22698c2ecf20Sopenharmony_ci ret = offset; 22708c2ecf20Sopenharmony_ci } 22718c2ecf20Sopenharmony_ci goto done; 22728c2ecf20Sopenharmony_ci case MWIFIEX_FW_DNLD_CMD_7: 22738c2ecf20Sopenharmony_ci first_cmd = true; 22748c2ecf20Sopenharmony_ci cmd7_before = true; 22758c2ecf20Sopenharmony_ci break; 22768c2ecf20Sopenharmony_ci default: 22778c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "unknown dnld_cmd %d\n", 22788c2ecf20Sopenharmony_ci dnld_cmd); 22798c2ecf20Sopenharmony_ci ret = -1; 22808c2ecf20Sopenharmony_ci goto done; 22818c2ecf20Sopenharmony_ci } 22828c2ecf20Sopenharmony_ci } 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_cidone: 22858c2ecf20Sopenharmony_ci return ret; 22868c2ecf20Sopenharmony_ci} 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci/* 22898c2ecf20Sopenharmony_ci * This function downloads the firmware to the card. 22908c2ecf20Sopenharmony_ci * 22918c2ecf20Sopenharmony_ci * Firmware is downloaded to the card in blocks. Every block download 22928c2ecf20Sopenharmony_ci * is tested for CRC errors, and retried a number of times before 22938c2ecf20Sopenharmony_ci * returning failure. 22948c2ecf20Sopenharmony_ci */ 22958c2ecf20Sopenharmony_cistatic int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, 22968c2ecf20Sopenharmony_ci struct mwifiex_fw_image *fw) 22978c2ecf20Sopenharmony_ci{ 22988c2ecf20Sopenharmony_ci int ret; 22998c2ecf20Sopenharmony_ci u8 *firmware = fw->fw_buf; 23008c2ecf20Sopenharmony_ci u32 firmware_len = fw->fw_len; 23018c2ecf20Sopenharmony_ci u32 offset = 0; 23028c2ecf20Sopenharmony_ci struct sk_buff *skb; 23038c2ecf20Sopenharmony_ci u32 txlen, tx_blocks = 0, tries, len, val; 23048c2ecf20Sopenharmony_ci u32 block_retry_cnt = 0; 23058c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 23068c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci if (!firmware || !firmware_len) { 23098c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 23108c2ecf20Sopenharmony_ci "No firmware image found! Terminating download\n"); 23118c2ecf20Sopenharmony_ci return -1; 23128c2ecf20Sopenharmony_ci } 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 23158c2ecf20Sopenharmony_ci "info: Downloading FW image (%d bytes)\n", 23168c2ecf20Sopenharmony_ci firmware_len); 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci if (mwifiex_pcie_disable_host_int(adapter)) { 23198c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 23208c2ecf20Sopenharmony_ci "%s: Disabling interrupts failed.\n", __func__); 23218c2ecf20Sopenharmony_ci return -1; 23228c2ecf20Sopenharmony_ci } 23238c2ecf20Sopenharmony_ci 23248c2ecf20Sopenharmony_ci skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); 23258c2ecf20Sopenharmony_ci if (!skb) { 23268c2ecf20Sopenharmony_ci ret = -ENOMEM; 23278c2ecf20Sopenharmony_ci goto done; 23288c2ecf20Sopenharmony_ci } 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci ret = mwifiex_read_reg(adapter, PCIE_SCRATCH_13_REG, &val); 23318c2ecf20Sopenharmony_ci if (ret) { 23328c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, FATAL, "Failed to read scratch register 13\n"); 23338c2ecf20Sopenharmony_ci goto done; 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci /* PCIE FLR case: extract wifi part from combo firmware*/ 23378c2ecf20Sopenharmony_ci if (val == MWIFIEX_PCIE_FLR_HAPPENS) { 23388c2ecf20Sopenharmony_ci ret = mwifiex_extract_wifi_fw(adapter, firmware, firmware_len); 23398c2ecf20Sopenharmony_ci if (ret < 0) { 23408c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Failed to extract wifi fw\n"); 23418c2ecf20Sopenharmony_ci goto done; 23428c2ecf20Sopenharmony_ci } 23438c2ecf20Sopenharmony_ci offset = ret; 23448c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, 23458c2ecf20Sopenharmony_ci "info: dnld wifi firmware from %d bytes\n", offset); 23468c2ecf20Sopenharmony_ci } 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci /* Perform firmware data transfer */ 23498c2ecf20Sopenharmony_ci do { 23508c2ecf20Sopenharmony_ci u32 ireg_intr = 0; 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci /* More data? */ 23538c2ecf20Sopenharmony_ci if (offset >= firmware_len) 23548c2ecf20Sopenharmony_ci break; 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 23578c2ecf20Sopenharmony_ci ret = mwifiex_read_reg(adapter, reg->cmd_size, 23588c2ecf20Sopenharmony_ci &len); 23598c2ecf20Sopenharmony_ci if (ret) { 23608c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, FATAL, 23618c2ecf20Sopenharmony_ci "Failed reading len from boot code\n"); 23628c2ecf20Sopenharmony_ci goto done; 23638c2ecf20Sopenharmony_ci } 23648c2ecf20Sopenharmony_ci if (len) 23658c2ecf20Sopenharmony_ci break; 23668c2ecf20Sopenharmony_ci usleep_range(10, 20); 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci if (!len) { 23708c2ecf20Sopenharmony_ci break; 23718c2ecf20Sopenharmony_ci } else if (len > MWIFIEX_UPLD_SIZE) { 23728c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 23738c2ecf20Sopenharmony_ci "FW download failure @ %d, invalid length %d\n", 23748c2ecf20Sopenharmony_ci offset, len); 23758c2ecf20Sopenharmony_ci ret = -1; 23768c2ecf20Sopenharmony_ci goto done; 23778c2ecf20Sopenharmony_ci } 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci txlen = len; 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci if (len & BIT(0)) { 23828c2ecf20Sopenharmony_ci block_retry_cnt++; 23838c2ecf20Sopenharmony_ci if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) { 23848c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 23858c2ecf20Sopenharmony_ci "FW download failure @ %d, over max\t" 23868c2ecf20Sopenharmony_ci "retry count\n", offset); 23878c2ecf20Sopenharmony_ci ret = -1; 23888c2ecf20Sopenharmony_ci goto done; 23898c2ecf20Sopenharmony_ci } 23908c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 23918c2ecf20Sopenharmony_ci "FW CRC error indicated by the\t" 23928c2ecf20Sopenharmony_ci "helper: len = 0x%04X, txlen = %d\n", 23938c2ecf20Sopenharmony_ci len, txlen); 23948c2ecf20Sopenharmony_ci len &= ~BIT(0); 23958c2ecf20Sopenharmony_ci /* Setting this to 0 to resend from same offset */ 23968c2ecf20Sopenharmony_ci txlen = 0; 23978c2ecf20Sopenharmony_ci } else { 23988c2ecf20Sopenharmony_ci block_retry_cnt = 0; 23998c2ecf20Sopenharmony_ci /* Set blocksize to transfer - checking for 24008c2ecf20Sopenharmony_ci last block */ 24018c2ecf20Sopenharmony_ci if (firmware_len - offset < txlen) 24028c2ecf20Sopenharmony_ci txlen = firmware_len - offset; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci tx_blocks = (txlen + card->pcie.blksz_fw_dl - 1) / 24058c2ecf20Sopenharmony_ci card->pcie.blksz_fw_dl; 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* Copy payload to buffer */ 24088c2ecf20Sopenharmony_ci memmove(skb->data, &firmware[offset], txlen); 24098c2ecf20Sopenharmony_ci } 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); 24128c2ecf20Sopenharmony_ci skb_trim(skb, tx_blocks * card->pcie.blksz_fw_dl); 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci /* Send the boot command to device */ 24158c2ecf20Sopenharmony_ci if (mwifiex_pcie_send_boot_cmd(adapter, skb)) { 24168c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 24178c2ecf20Sopenharmony_ci "Failed to send firmware download command\n"); 24188c2ecf20Sopenharmony_ci ret = -1; 24198c2ecf20Sopenharmony_ci goto done; 24208c2ecf20Sopenharmony_ci } 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci /* Wait for the command done interrupt */ 24238c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 24248c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, 24258c2ecf20Sopenharmony_ci &ireg_intr)) { 24268c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 24278c2ecf20Sopenharmony_ci "%s: Failed to read\t" 24288c2ecf20Sopenharmony_ci "interrupt status during fw dnld.\n", 24298c2ecf20Sopenharmony_ci __func__); 24308c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 24318c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 24328c2ecf20Sopenharmony_ci ret = -1; 24338c2ecf20Sopenharmony_ci goto done; 24348c2ecf20Sopenharmony_ci } 24358c2ecf20Sopenharmony_ci if (!(ireg_intr & CPU_INTR_DOOR_BELL)) 24368c2ecf20Sopenharmony_ci break; 24378c2ecf20Sopenharmony_ci usleep_range(10, 20); 24388c2ecf20Sopenharmony_ci } 24398c2ecf20Sopenharmony_ci if (ireg_intr & CPU_INTR_DOOR_BELL) { 24408c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "%s: Card failed to ACK download\n", 24418c2ecf20Sopenharmony_ci __func__); 24428c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, 24438c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 24448c2ecf20Sopenharmony_ci ret = -1; 24458c2ecf20Sopenharmony_ci goto done; 24468c2ecf20Sopenharmony_ci } 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci mwifiex_unmap_pci_memory(adapter, skb, DMA_TO_DEVICE); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci offset += txlen; 24518c2ecf20Sopenharmony_ci } while (true); 24528c2ecf20Sopenharmony_ci 24538c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, 24548c2ecf20Sopenharmony_ci "info: FW download over, size %d bytes\n", offset); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci ret = 0; 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_cidone: 24598c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 24608c2ecf20Sopenharmony_ci return ret; 24618c2ecf20Sopenharmony_ci} 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci/* 24648c2ecf20Sopenharmony_ci * This function checks the firmware status in card. 24658c2ecf20Sopenharmony_ci */ 24668c2ecf20Sopenharmony_cistatic int 24678c2ecf20Sopenharmony_cimwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) 24688c2ecf20Sopenharmony_ci{ 24698c2ecf20Sopenharmony_ci int ret = 0; 24708c2ecf20Sopenharmony_ci u32 firmware_stat; 24718c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 24728c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 24738c2ecf20Sopenharmony_ci u32 tries; 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci /* Mask spurios interrupts */ 24768c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK, 24778c2ecf20Sopenharmony_ci HOST_INTR_MASK)) { 24788c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 24798c2ecf20Sopenharmony_ci "Write register failed\n"); 24808c2ecf20Sopenharmony_ci return -1; 24818c2ecf20Sopenharmony_ci } 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 24848c2ecf20Sopenharmony_ci "Setting driver ready signature\n"); 24858c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->drv_rdy, 24868c2ecf20Sopenharmony_ci FIRMWARE_READY_PCIE)) { 24878c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 24888c2ecf20Sopenharmony_ci "Failed to write driver ready signature\n"); 24898c2ecf20Sopenharmony_ci return -1; 24908c2ecf20Sopenharmony_ci } 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci /* Wait for firmware initialization event */ 24938c2ecf20Sopenharmony_ci for (tries = 0; tries < poll_num; tries++) { 24948c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->fw_status, 24958c2ecf20Sopenharmony_ci &firmware_stat)) 24968c2ecf20Sopenharmony_ci ret = -1; 24978c2ecf20Sopenharmony_ci else 24988c2ecf20Sopenharmony_ci ret = 0; 24998c2ecf20Sopenharmony_ci 25008c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "Try %d if FW is ready <%d,%#x>", 25018c2ecf20Sopenharmony_ci tries, ret, firmware_stat); 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci if (ret) 25048c2ecf20Sopenharmony_ci continue; 25058c2ecf20Sopenharmony_ci if (firmware_stat == FIRMWARE_READY_PCIE) { 25068c2ecf20Sopenharmony_ci ret = 0; 25078c2ecf20Sopenharmony_ci break; 25088c2ecf20Sopenharmony_ci } else { 25098c2ecf20Sopenharmony_ci msleep(100); 25108c2ecf20Sopenharmony_ci ret = -1; 25118c2ecf20Sopenharmony_ci } 25128c2ecf20Sopenharmony_ci } 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci return ret; 25158c2ecf20Sopenharmony_ci} 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci/* This function checks if WLAN is the winner. 25188c2ecf20Sopenharmony_ci */ 25198c2ecf20Sopenharmony_cistatic int 25208c2ecf20Sopenharmony_cimwifiex_check_winner_status(struct mwifiex_adapter *adapter) 25218c2ecf20Sopenharmony_ci{ 25228c2ecf20Sopenharmony_ci u32 winner = 0; 25238c2ecf20Sopenharmony_ci int ret = 0; 25248c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 25258c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->fw_status, &winner)) { 25288c2ecf20Sopenharmony_ci ret = -1; 25298c2ecf20Sopenharmony_ci } else if (!winner) { 25308c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "PCI-E is the winner\n"); 25318c2ecf20Sopenharmony_ci adapter->winner = 1; 25328c2ecf20Sopenharmony_ci } else { 25338c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 25348c2ecf20Sopenharmony_ci "PCI-E is not the winner <%#x>", winner); 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci return ret; 25388c2ecf20Sopenharmony_ci} 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci/* 25418c2ecf20Sopenharmony_ci * This function reads the interrupt status from card. 25428c2ecf20Sopenharmony_ci */ 25438c2ecf20Sopenharmony_cistatic void mwifiex_interrupt_status(struct mwifiex_adapter *adapter, 25448c2ecf20Sopenharmony_ci int msg_id) 25458c2ecf20Sopenharmony_ci{ 25468c2ecf20Sopenharmony_ci u32 pcie_ireg; 25478c2ecf20Sopenharmony_ci unsigned long flags; 25488c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci if (card->msi_enable) { 25518c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->int_lock, flags); 25528c2ecf20Sopenharmony_ci adapter->int_status = 1; 25538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->int_lock, flags); 25548c2ecf20Sopenharmony_ci return; 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_ci if (!mwifiex_pcie_ok_to_access_hw(adapter)) 25588c2ecf20Sopenharmony_ci return; 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_ci if (card->msix_enable && msg_id >= 0) { 25618c2ecf20Sopenharmony_ci pcie_ireg = BIT(msg_id); 25628c2ecf20Sopenharmony_ci } else { 25638c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, 25648c2ecf20Sopenharmony_ci &pcie_ireg)) { 25658c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Read register failed\n"); 25668c2ecf20Sopenharmony_ci return; 25678c2ecf20Sopenharmony_ci } 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci if ((pcie_ireg == 0xFFFFFFFF) || !pcie_ireg) 25708c2ecf20Sopenharmony_ci return; 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci mwifiex_pcie_disable_host_int(adapter); 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci /* Clear the pending interrupts */ 25768c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS, 25778c2ecf20Sopenharmony_ci ~pcie_ireg)) { 25788c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 25798c2ecf20Sopenharmony_ci "Write register failed\n"); 25808c2ecf20Sopenharmony_ci return; 25818c2ecf20Sopenharmony_ci } 25828c2ecf20Sopenharmony_ci } 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci if (!adapter->pps_uapsd_mode && 25858c2ecf20Sopenharmony_ci adapter->ps_state == PS_STATE_SLEEP && 25868c2ecf20Sopenharmony_ci mwifiex_pcie_ok_to_access_hw(adapter)) { 25878c2ecf20Sopenharmony_ci /* Potentially for PCIe we could get other 25888c2ecf20Sopenharmony_ci * interrupts like shared. Don't change power 25898c2ecf20Sopenharmony_ci * state until cookie is set 25908c2ecf20Sopenharmony_ci */ 25918c2ecf20Sopenharmony_ci adapter->ps_state = PS_STATE_AWAKE; 25928c2ecf20Sopenharmony_ci adapter->pm_wakeup_fw_try = false; 25938c2ecf20Sopenharmony_ci del_timer(&adapter->wakeup_timer); 25948c2ecf20Sopenharmony_ci } 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->int_lock, flags); 25978c2ecf20Sopenharmony_ci adapter->int_status |= pcie_ireg; 25988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->int_lock, flags); 25998c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, "ireg: 0x%08x\n", pcie_ireg); 26008c2ecf20Sopenharmony_ci} 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci/* 26038c2ecf20Sopenharmony_ci * Interrupt handler for PCIe root port 26048c2ecf20Sopenharmony_ci * 26058c2ecf20Sopenharmony_ci * This function reads the interrupt status from firmware and assigns 26068c2ecf20Sopenharmony_ci * the main process in workqueue which will handle the interrupt. 26078c2ecf20Sopenharmony_ci */ 26088c2ecf20Sopenharmony_cistatic irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) 26098c2ecf20Sopenharmony_ci{ 26108c2ecf20Sopenharmony_ci struct mwifiex_msix_context *ctx = context; 26118c2ecf20Sopenharmony_ci struct pci_dev *pdev = ctx->dev; 26128c2ecf20Sopenharmony_ci struct pcie_service_card *card; 26138c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci card = pci_get_drvdata(pdev); 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci if (!card->adapter) { 26188c2ecf20Sopenharmony_ci pr_err("info: %s: card=%p adapter=%p\n", __func__, card, 26198c2ecf20Sopenharmony_ci card ? card->adapter : NULL); 26208c2ecf20Sopenharmony_ci goto exit; 26218c2ecf20Sopenharmony_ci } 26228c2ecf20Sopenharmony_ci adapter = card->adapter; 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci if (test_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags)) 26258c2ecf20Sopenharmony_ci goto exit; 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_ci if (card->msix_enable) 26288c2ecf20Sopenharmony_ci mwifiex_interrupt_status(adapter, ctx->msg_id); 26298c2ecf20Sopenharmony_ci else 26308c2ecf20Sopenharmony_ci mwifiex_interrupt_status(adapter, -1); 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci mwifiex_queue_main_work(adapter); 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ciexit: 26358c2ecf20Sopenharmony_ci return IRQ_HANDLED; 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci/* 26398c2ecf20Sopenharmony_ci * This function checks the current interrupt status. 26408c2ecf20Sopenharmony_ci * 26418c2ecf20Sopenharmony_ci * The following interrupts are checked and handled by this function - 26428c2ecf20Sopenharmony_ci * - Data sent 26438c2ecf20Sopenharmony_ci * - Command sent 26448c2ecf20Sopenharmony_ci * - Command received 26458c2ecf20Sopenharmony_ci * - Packets received 26468c2ecf20Sopenharmony_ci * - Events received 26478c2ecf20Sopenharmony_ci * 26488c2ecf20Sopenharmony_ci * In case of Rx packets received, the packets are uploaded from card to 26498c2ecf20Sopenharmony_ci * host and processed accordingly. 26508c2ecf20Sopenharmony_ci */ 26518c2ecf20Sopenharmony_cistatic int mwifiex_process_int_status(struct mwifiex_adapter *adapter) 26528c2ecf20Sopenharmony_ci{ 26538c2ecf20Sopenharmony_ci int ret; 26548c2ecf20Sopenharmony_ci u32 pcie_ireg = 0; 26558c2ecf20Sopenharmony_ci unsigned long flags; 26568c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 26578c2ecf20Sopenharmony_ci 26588c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->int_lock, flags); 26598c2ecf20Sopenharmony_ci if (!card->msi_enable) { 26608c2ecf20Sopenharmony_ci /* Clear out unused interrupts */ 26618c2ecf20Sopenharmony_ci pcie_ireg = adapter->int_status; 26628c2ecf20Sopenharmony_ci } 26638c2ecf20Sopenharmony_ci adapter->int_status = 0; 26648c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->int_lock, flags); 26658c2ecf20Sopenharmony_ci 26668c2ecf20Sopenharmony_ci if (card->msi_enable) { 26678c2ecf20Sopenharmony_ci if (mwifiex_pcie_ok_to_access_hw(adapter)) { 26688c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, 26698c2ecf20Sopenharmony_ci &pcie_ireg)) { 26708c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 26718c2ecf20Sopenharmony_ci "Read register failed\n"); 26728c2ecf20Sopenharmony_ci return -1; 26738c2ecf20Sopenharmony_ci } 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { 26768c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, 26778c2ecf20Sopenharmony_ci PCIE_HOST_INT_STATUS, 26788c2ecf20Sopenharmony_ci ~pcie_ireg)) { 26798c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 26808c2ecf20Sopenharmony_ci "Write register failed\n"); 26818c2ecf20Sopenharmony_ci return -1; 26828c2ecf20Sopenharmony_ci } 26838c2ecf20Sopenharmony_ci if (!adapter->pps_uapsd_mode && 26848c2ecf20Sopenharmony_ci adapter->ps_state == PS_STATE_SLEEP) { 26858c2ecf20Sopenharmony_ci adapter->ps_state = PS_STATE_AWAKE; 26868c2ecf20Sopenharmony_ci adapter->pm_wakeup_fw_try = false; 26878c2ecf20Sopenharmony_ci del_timer(&adapter->wakeup_timer); 26888c2ecf20Sopenharmony_ci } 26898c2ecf20Sopenharmony_ci } 26908c2ecf20Sopenharmony_ci } 26918c2ecf20Sopenharmony_ci } 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci if (pcie_ireg & HOST_INTR_DNLD_DONE) { 26948c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, "info: TX DNLD Done\n"); 26958c2ecf20Sopenharmony_ci ret = mwifiex_pcie_send_data_complete(adapter); 26968c2ecf20Sopenharmony_ci if (ret) 26978c2ecf20Sopenharmony_ci return ret; 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci if (pcie_ireg & HOST_INTR_UPLD_RDY) { 27008c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, "info: Rx DATA\n"); 27018c2ecf20Sopenharmony_ci ret = mwifiex_pcie_process_recv_data(adapter); 27028c2ecf20Sopenharmony_ci if (ret) 27038c2ecf20Sopenharmony_ci return ret; 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci if (pcie_ireg & HOST_INTR_EVENT_RDY) { 27068c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, "info: Rx EVENT\n"); 27078c2ecf20Sopenharmony_ci ret = mwifiex_pcie_process_event_ready(adapter); 27088c2ecf20Sopenharmony_ci if (ret) 27098c2ecf20Sopenharmony_ci return ret; 27108c2ecf20Sopenharmony_ci } 27118c2ecf20Sopenharmony_ci if (pcie_ireg & HOST_INTR_CMD_DONE) { 27128c2ecf20Sopenharmony_ci if (adapter->cmd_sent) { 27138c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, 27148c2ecf20Sopenharmony_ci "info: CMD sent Interrupt\n"); 27158c2ecf20Sopenharmony_ci adapter->cmd_sent = false; 27168c2ecf20Sopenharmony_ci } 27178c2ecf20Sopenharmony_ci /* Handle command response */ 27188c2ecf20Sopenharmony_ci ret = mwifiex_pcie_process_cmd_complete(adapter); 27198c2ecf20Sopenharmony_ci if (ret) 27208c2ecf20Sopenharmony_ci return ret; 27218c2ecf20Sopenharmony_ci } 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, 27248c2ecf20Sopenharmony_ci "info: cmd_sent=%d data_sent=%d\n", 27258c2ecf20Sopenharmony_ci adapter->cmd_sent, adapter->data_sent); 27268c2ecf20Sopenharmony_ci if (!card->msi_enable && !card->msix_enable && 27278c2ecf20Sopenharmony_ci adapter->ps_state != PS_STATE_SLEEP) 27288c2ecf20Sopenharmony_ci mwifiex_pcie_enable_host_int(adapter); 27298c2ecf20Sopenharmony_ci 27308c2ecf20Sopenharmony_ci return 0; 27318c2ecf20Sopenharmony_ci} 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci/* 27348c2ecf20Sopenharmony_ci * This function downloads data from driver to card. 27358c2ecf20Sopenharmony_ci * 27368c2ecf20Sopenharmony_ci * Both commands and data packets are transferred to the card by this 27378c2ecf20Sopenharmony_ci * function. 27388c2ecf20Sopenharmony_ci * 27398c2ecf20Sopenharmony_ci * This function adds the PCIE specific header to the front of the buffer 27408c2ecf20Sopenharmony_ci * before transferring. The header contains the length of the packet and 27418c2ecf20Sopenharmony_ci * the type. The firmware handles the packets based upon this set type. 27428c2ecf20Sopenharmony_ci */ 27438c2ecf20Sopenharmony_cistatic int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, 27448c2ecf20Sopenharmony_ci struct sk_buff *skb, 27458c2ecf20Sopenharmony_ci struct mwifiex_tx_param *tx_param) 27468c2ecf20Sopenharmony_ci{ 27478c2ecf20Sopenharmony_ci if (!skb) { 27488c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 27498c2ecf20Sopenharmony_ci "Passed NULL skb to %s\n", __func__); 27508c2ecf20Sopenharmony_ci return -1; 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci if (type == MWIFIEX_TYPE_DATA) 27548c2ecf20Sopenharmony_ci return mwifiex_pcie_send_data(adapter, skb, tx_param); 27558c2ecf20Sopenharmony_ci else if (type == MWIFIEX_TYPE_CMD) 27568c2ecf20Sopenharmony_ci return mwifiex_pcie_send_cmd(adapter, skb); 27578c2ecf20Sopenharmony_ci 27588c2ecf20Sopenharmony_ci return 0; 27598c2ecf20Sopenharmony_ci} 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci/* Function to dump PCIE scratch registers in case of FW crash 27628c2ecf20Sopenharmony_ci */ 27638c2ecf20Sopenharmony_cistatic int 27648c2ecf20Sopenharmony_cimwifiex_pcie_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) 27658c2ecf20Sopenharmony_ci{ 27668c2ecf20Sopenharmony_ci char *p = drv_buf; 27678c2ecf20Sopenharmony_ci char buf[256], *ptr; 27688c2ecf20Sopenharmony_ci int i; 27698c2ecf20Sopenharmony_ci u32 value; 27708c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 27718c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 27728c2ecf20Sopenharmony_ci int pcie_scratch_reg[] = {PCIE_SCRATCH_12_REG, 27738c2ecf20Sopenharmony_ci PCIE_SCRATCH_14_REG, 27748c2ecf20Sopenharmony_ci PCIE_SCRATCH_15_REG}; 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci if (!p) 27778c2ecf20Sopenharmony_ci return 0; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "PCIE register dump start\n"); 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->fw_status, &value)) { 27828c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "failed to read firmware status"); 27838c2ecf20Sopenharmony_ci return 0; 27848c2ecf20Sopenharmony_ci } 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci ptr = buf; 27878c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "pcie scratch register:"); 27888c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pcie_scratch_reg); i++) { 27898c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, pcie_scratch_reg[i], &value); 27908c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "reg:0x%x, value=0x%x\n", 27918c2ecf20Sopenharmony_ci pcie_scratch_reg[i], value); 27928c2ecf20Sopenharmony_ci } 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "%s\n", buf); 27958c2ecf20Sopenharmony_ci p += sprintf(p, "%s\n", buf); 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "PCIE register dump end\n"); 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci return p - drv_buf; 28008c2ecf20Sopenharmony_ci} 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci/* This function read/write firmware */ 28038c2ecf20Sopenharmony_cistatic enum rdwr_status 28048c2ecf20Sopenharmony_cimwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) 28058c2ecf20Sopenharmony_ci{ 28068c2ecf20Sopenharmony_ci int ret, tries; 28078c2ecf20Sopenharmony_ci u8 ctrl_data; 28088c2ecf20Sopenharmony_ci u32 fw_status; 28098c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 28108c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) 28138c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 28148c2ecf20Sopenharmony_ci 28158c2ecf20Sopenharmony_ci ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, 28168c2ecf20Sopenharmony_ci reg->fw_dump_host_ready); 28178c2ecf20Sopenharmony_ci if (ret) { 28188c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 28198c2ecf20Sopenharmony_ci "PCIE write err\n"); 28208c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 28218c2ecf20Sopenharmony_ci } 28228c2ecf20Sopenharmony_ci 28238c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 28248c2ecf20Sopenharmony_ci mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data); 28258c2ecf20Sopenharmony_ci if (ctrl_data == FW_DUMP_DONE) 28268c2ecf20Sopenharmony_ci return RDWR_STATUS_SUCCESS; 28278c2ecf20Sopenharmony_ci if (doneflag && ctrl_data == doneflag) 28288c2ecf20Sopenharmony_ci return RDWR_STATUS_DONE; 28298c2ecf20Sopenharmony_ci if (ctrl_data != reg->fw_dump_host_ready) { 28308c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 28318c2ecf20Sopenharmony_ci "The ctrl reg was changed, re-try again!\n"); 28328c2ecf20Sopenharmony_ci ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, 28338c2ecf20Sopenharmony_ci reg->fw_dump_host_ready); 28348c2ecf20Sopenharmony_ci if (ret) { 28358c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 28368c2ecf20Sopenharmony_ci "PCIE write err\n"); 28378c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 28388c2ecf20Sopenharmony_ci } 28398c2ecf20Sopenharmony_ci } 28408c2ecf20Sopenharmony_ci usleep_range(100, 200); 28418c2ecf20Sopenharmony_ci } 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Fail to pull ctrl_data\n"); 28448c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 28458c2ecf20Sopenharmony_ci} 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci/* This function dump firmware memory to file */ 28488c2ecf20Sopenharmony_cistatic void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) 28498c2ecf20Sopenharmony_ci{ 28508c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 28518c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; 28528c2ecf20Sopenharmony_ci unsigned int reg, reg_start, reg_end; 28538c2ecf20Sopenharmony_ci u8 *dbg_ptr, *end_ptr, *tmp_ptr, fw_dump_num, dump_num; 28548c2ecf20Sopenharmony_ci u8 idx, i, read_reg, doneflag = 0; 28558c2ecf20Sopenharmony_ci enum rdwr_status stat; 28568c2ecf20Sopenharmony_ci u32 memory_size; 28578c2ecf20Sopenharmony_ci int ret; 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci if (!card->pcie.can_dump_fw) 28608c2ecf20Sopenharmony_ci return; 28618c2ecf20Sopenharmony_ci 28628c2ecf20Sopenharmony_ci for (idx = 0; idx < adapter->num_mem_types; idx++) { 28638c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = 28648c2ecf20Sopenharmony_ci &adapter->mem_type_mapping_tbl[idx]; 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci if (entry->mem_ptr) { 28678c2ecf20Sopenharmony_ci vfree(entry->mem_ptr); 28688c2ecf20Sopenharmony_ci entry->mem_ptr = NULL; 28698c2ecf20Sopenharmony_ci } 28708c2ecf20Sopenharmony_ci entry->mem_size = 0; 28718c2ecf20Sopenharmony_ci } 28728c2ecf20Sopenharmony_ci 28738c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci /* Read the number of the memories which will dump */ 28768c2ecf20Sopenharmony_ci stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); 28778c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 28788c2ecf20Sopenharmony_ci return; 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci reg = creg->fw_dump_start; 28818c2ecf20Sopenharmony_ci mwifiex_read_reg_byte(adapter, reg, &fw_dump_num); 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci /* W8997 chipset firmware dump will be restore in single region*/ 28848c2ecf20Sopenharmony_ci if (fw_dump_num == 0) 28858c2ecf20Sopenharmony_ci dump_num = 1; 28868c2ecf20Sopenharmony_ci else 28878c2ecf20Sopenharmony_ci dump_num = fw_dump_num; 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_ci /* Read the length of every memory which will dump */ 28908c2ecf20Sopenharmony_ci for (idx = 0; idx < dump_num; idx++) { 28918c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = 28928c2ecf20Sopenharmony_ci &adapter->mem_type_mapping_tbl[idx]; 28938c2ecf20Sopenharmony_ci memory_size = 0; 28948c2ecf20Sopenharmony_ci if (fw_dump_num != 0) { 28958c2ecf20Sopenharmony_ci stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); 28968c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 28978c2ecf20Sopenharmony_ci return; 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci reg = creg->fw_dump_start; 29008c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 29018c2ecf20Sopenharmony_ci mwifiex_read_reg_byte(adapter, reg, &read_reg); 29028c2ecf20Sopenharmony_ci memory_size |= (read_reg << (i * 8)); 29038c2ecf20Sopenharmony_ci reg++; 29048c2ecf20Sopenharmony_ci } 29058c2ecf20Sopenharmony_ci } else { 29068c2ecf20Sopenharmony_ci memory_size = MWIFIEX_FW_DUMP_MAX_MEMSIZE; 29078c2ecf20Sopenharmony_ci } 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci if (memory_size == 0) { 29108c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n"); 29118c2ecf20Sopenharmony_ci ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl, 29128c2ecf20Sopenharmony_ci creg->fw_dump_read_done); 29138c2ecf20Sopenharmony_ci if (ret) { 29148c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); 29158c2ecf20Sopenharmony_ci return; 29168c2ecf20Sopenharmony_ci } 29178c2ecf20Sopenharmony_ci break; 29188c2ecf20Sopenharmony_ci } 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 29218c2ecf20Sopenharmony_ci "%s_SIZE=0x%x\n", entry->mem_name, memory_size); 29228c2ecf20Sopenharmony_ci entry->mem_ptr = vmalloc(memory_size + 1); 29238c2ecf20Sopenharmony_ci entry->mem_size = memory_size; 29248c2ecf20Sopenharmony_ci if (!entry->mem_ptr) { 29258c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 29268c2ecf20Sopenharmony_ci "Vmalloc %s failed\n", entry->mem_name); 29278c2ecf20Sopenharmony_ci return; 29288c2ecf20Sopenharmony_ci } 29298c2ecf20Sopenharmony_ci dbg_ptr = entry->mem_ptr; 29308c2ecf20Sopenharmony_ci end_ptr = dbg_ptr + memory_size; 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci doneflag = entry->done_flag; 29338c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, "Start %s output, please wait...\n", 29348c2ecf20Sopenharmony_ci entry->mem_name); 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci do { 29378c2ecf20Sopenharmony_ci stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); 29388c2ecf20Sopenharmony_ci if (RDWR_STATUS_FAILURE == stat) 29398c2ecf20Sopenharmony_ci return; 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci reg_start = creg->fw_dump_start; 29428c2ecf20Sopenharmony_ci reg_end = creg->fw_dump_end; 29438c2ecf20Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 29448c2ecf20Sopenharmony_ci mwifiex_read_reg_byte(adapter, reg, dbg_ptr); 29458c2ecf20Sopenharmony_ci if (dbg_ptr < end_ptr) { 29468c2ecf20Sopenharmony_ci dbg_ptr++; 29478c2ecf20Sopenharmony_ci continue; 29488c2ecf20Sopenharmony_ci } 29498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 29508c2ecf20Sopenharmony_ci "pre-allocated buf not enough\n"); 29518c2ecf20Sopenharmony_ci tmp_ptr = 29528c2ecf20Sopenharmony_ci vzalloc(memory_size + MWIFIEX_SIZE_4K); 29538c2ecf20Sopenharmony_ci if (!tmp_ptr) 29548c2ecf20Sopenharmony_ci return; 29558c2ecf20Sopenharmony_ci memcpy(tmp_ptr, entry->mem_ptr, memory_size); 29568c2ecf20Sopenharmony_ci vfree(entry->mem_ptr); 29578c2ecf20Sopenharmony_ci entry->mem_ptr = tmp_ptr; 29588c2ecf20Sopenharmony_ci tmp_ptr = NULL; 29598c2ecf20Sopenharmony_ci dbg_ptr = entry->mem_ptr + memory_size; 29608c2ecf20Sopenharmony_ci memory_size += MWIFIEX_SIZE_4K; 29618c2ecf20Sopenharmony_ci end_ptr = entry->mem_ptr + memory_size; 29628c2ecf20Sopenharmony_ci } 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci if (stat != RDWR_STATUS_DONE) 29658c2ecf20Sopenharmony_ci continue; 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 29688c2ecf20Sopenharmony_ci "%s done: size=0x%tx\n", 29698c2ecf20Sopenharmony_ci entry->mem_name, dbg_ptr - entry->mem_ptr); 29708c2ecf20Sopenharmony_ci break; 29718c2ecf20Sopenharmony_ci } while (true); 29728c2ecf20Sopenharmony_ci } 29738c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); 29748c2ecf20Sopenharmony_ci} 29758c2ecf20Sopenharmony_ci 29768c2ecf20Sopenharmony_cistatic void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) 29778c2ecf20Sopenharmony_ci{ 29788c2ecf20Sopenharmony_ci adapter->devdump_data = vzalloc(MWIFIEX_FW_DUMP_SIZE); 29798c2ecf20Sopenharmony_ci if (!adapter->devdump_data) { 29808c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 29818c2ecf20Sopenharmony_ci "vzalloc devdump data failure!\n"); 29828c2ecf20Sopenharmony_ci return; 29838c2ecf20Sopenharmony_ci } 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ci mwifiex_drv_info_dump(adapter); 29868c2ecf20Sopenharmony_ci mwifiex_pcie_fw_dump(adapter); 29878c2ecf20Sopenharmony_ci mwifiex_prepare_fw_dump_info(adapter); 29888c2ecf20Sopenharmony_ci mwifiex_upload_device_dump(adapter); 29898c2ecf20Sopenharmony_ci} 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_cistatic void mwifiex_pcie_card_reset_work(struct mwifiex_adapter *adapter) 29928c2ecf20Sopenharmony_ci{ 29938c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci /* We can't afford to wait here; remove() might be waiting on us. If we 29968c2ecf20Sopenharmony_ci * can't grab the device lock, maybe we'll get another chance later. 29978c2ecf20Sopenharmony_ci */ 29988c2ecf20Sopenharmony_ci pci_try_reset_function(card->dev); 29998c2ecf20Sopenharmony_ci} 30008c2ecf20Sopenharmony_ci 30018c2ecf20Sopenharmony_cistatic void mwifiex_pcie_work(struct work_struct *work) 30028c2ecf20Sopenharmony_ci{ 30038c2ecf20Sopenharmony_ci struct pcie_service_card *card = 30048c2ecf20Sopenharmony_ci container_of(work, struct pcie_service_card, work); 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 30078c2ecf20Sopenharmony_ci &card->work_flags)) 30088c2ecf20Sopenharmony_ci mwifiex_pcie_device_dump_work(card->adapter); 30098c2ecf20Sopenharmony_ci if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, 30108c2ecf20Sopenharmony_ci &card->work_flags)) 30118c2ecf20Sopenharmony_ci mwifiex_pcie_card_reset_work(card->adapter); 30128c2ecf20Sopenharmony_ci} 30138c2ecf20Sopenharmony_ci 30148c2ecf20Sopenharmony_ci/* This function dumps FW information */ 30158c2ecf20Sopenharmony_cistatic void mwifiex_pcie_device_dump(struct mwifiex_adapter *adapter) 30168c2ecf20Sopenharmony_ci{ 30178c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 30208c2ecf20Sopenharmony_ci &card->work_flags)) 30218c2ecf20Sopenharmony_ci schedule_work(&card->work); 30228c2ecf20Sopenharmony_ci} 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_cistatic void mwifiex_pcie_card_reset(struct mwifiex_adapter *adapter) 30258c2ecf20Sopenharmony_ci{ 30268c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags)) 30298c2ecf20Sopenharmony_ci schedule_work(&card->work); 30308c2ecf20Sopenharmony_ci} 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_cistatic int mwifiex_pcie_alloc_buffers(struct mwifiex_adapter *adapter) 30338c2ecf20Sopenharmony_ci{ 30348c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 30358c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 30368c2ecf20Sopenharmony_ci int ret; 30378c2ecf20Sopenharmony_ci 30388c2ecf20Sopenharmony_ci card->cmdrsp_buf = NULL; 30398c2ecf20Sopenharmony_ci ret = mwifiex_pcie_create_txbd_ring(adapter); 30408c2ecf20Sopenharmony_ci if (ret) { 30418c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Failed to create txbd ring\n"); 30428c2ecf20Sopenharmony_ci goto err_cre_txbd; 30438c2ecf20Sopenharmony_ci } 30448c2ecf20Sopenharmony_ci 30458c2ecf20Sopenharmony_ci ret = mwifiex_pcie_create_rxbd_ring(adapter); 30468c2ecf20Sopenharmony_ci if (ret) { 30478c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Failed to create rxbd ring\n"); 30488c2ecf20Sopenharmony_ci goto err_cre_rxbd; 30498c2ecf20Sopenharmony_ci } 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci ret = mwifiex_pcie_create_evtbd_ring(adapter); 30528c2ecf20Sopenharmony_ci if (ret) { 30538c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Failed to create evtbd ring\n"); 30548c2ecf20Sopenharmony_ci goto err_cre_evtbd; 30558c2ecf20Sopenharmony_ci } 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter); 30588c2ecf20Sopenharmony_ci if (ret) { 30598c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Failed to allocate cmdbuf buffer\n"); 30608c2ecf20Sopenharmony_ci goto err_alloc_cmdbuf; 30618c2ecf20Sopenharmony_ci } 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci if (reg->sleep_cookie) { 30648c2ecf20Sopenharmony_ci ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter); 30658c2ecf20Sopenharmony_ci if (ret) { 30668c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Failed to allocate sleep_cookie buffer\n"); 30678c2ecf20Sopenharmony_ci goto err_alloc_cookie; 30688c2ecf20Sopenharmony_ci } 30698c2ecf20Sopenharmony_ci } else { 30708c2ecf20Sopenharmony_ci card->sleep_cookie_vbase = NULL; 30718c2ecf20Sopenharmony_ci } 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci return 0; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_cierr_alloc_cookie: 30768c2ecf20Sopenharmony_ci mwifiex_pcie_delete_cmdrsp_buf(adapter); 30778c2ecf20Sopenharmony_cierr_alloc_cmdbuf: 30788c2ecf20Sopenharmony_ci mwifiex_pcie_delete_evtbd_ring(adapter); 30798c2ecf20Sopenharmony_cierr_cre_evtbd: 30808c2ecf20Sopenharmony_ci mwifiex_pcie_delete_rxbd_ring(adapter); 30818c2ecf20Sopenharmony_cierr_cre_rxbd: 30828c2ecf20Sopenharmony_ci mwifiex_pcie_delete_txbd_ring(adapter); 30838c2ecf20Sopenharmony_cierr_cre_txbd: 30848c2ecf20Sopenharmony_ci return ret; 30858c2ecf20Sopenharmony_ci} 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_cistatic void mwifiex_pcie_free_buffers(struct mwifiex_adapter *adapter) 30888c2ecf20Sopenharmony_ci{ 30898c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 30908c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_ci if (reg->sleep_cookie) 30938c2ecf20Sopenharmony_ci mwifiex_pcie_delete_sleep_cookie_buf(adapter); 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci mwifiex_pcie_delete_cmdrsp_buf(adapter); 30968c2ecf20Sopenharmony_ci mwifiex_pcie_delete_evtbd_ring(adapter); 30978c2ecf20Sopenharmony_ci mwifiex_pcie_delete_rxbd_ring(adapter); 30988c2ecf20Sopenharmony_ci mwifiex_pcie_delete_txbd_ring(adapter); 30998c2ecf20Sopenharmony_ci} 31008c2ecf20Sopenharmony_ci 31018c2ecf20Sopenharmony_ci/* 31028c2ecf20Sopenharmony_ci * This function initializes the PCI-E host memory space, WCB rings, etc. 31038c2ecf20Sopenharmony_ci */ 31048c2ecf20Sopenharmony_cistatic int mwifiex_init_pcie(struct mwifiex_adapter *adapter) 31058c2ecf20Sopenharmony_ci{ 31068c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 31078c2ecf20Sopenharmony_ci int ret; 31088c2ecf20Sopenharmony_ci struct pci_dev *pdev = card->dev; 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, card); 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 31138c2ecf20Sopenharmony_ci if (ret) 31148c2ecf20Sopenharmony_ci goto err_enable_dev; 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci pci_set_master(pdev); 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 31198c2ecf20Sopenharmony_ci if (ret) { 31208c2ecf20Sopenharmony_ci pr_err("dma_set_mask(32) failed: %d\n", ret); 31218c2ecf20Sopenharmony_ci goto err_set_dma_mask; 31228c2ecf20Sopenharmony_ci } 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci ret = pci_request_region(pdev, 0, DRV_NAME); 31258c2ecf20Sopenharmony_ci if (ret) { 31268c2ecf20Sopenharmony_ci pr_err("req_reg(0) error\n"); 31278c2ecf20Sopenharmony_ci goto err_req_region0; 31288c2ecf20Sopenharmony_ci } 31298c2ecf20Sopenharmony_ci card->pci_mmap = pci_iomap(pdev, 0, 0); 31308c2ecf20Sopenharmony_ci if (!card->pci_mmap) { 31318c2ecf20Sopenharmony_ci pr_err("iomap(0) error\n"); 31328c2ecf20Sopenharmony_ci ret = -EIO; 31338c2ecf20Sopenharmony_ci goto err_iomap0; 31348c2ecf20Sopenharmony_ci } 31358c2ecf20Sopenharmony_ci ret = pci_request_region(pdev, 2, DRV_NAME); 31368c2ecf20Sopenharmony_ci if (ret) { 31378c2ecf20Sopenharmony_ci pr_err("req_reg(2) error\n"); 31388c2ecf20Sopenharmony_ci goto err_req_region2; 31398c2ecf20Sopenharmony_ci } 31408c2ecf20Sopenharmony_ci card->pci_mmap1 = pci_iomap(pdev, 2, 0); 31418c2ecf20Sopenharmony_ci if (!card->pci_mmap1) { 31428c2ecf20Sopenharmony_ci pr_err("iomap(2) error\n"); 31438c2ecf20Sopenharmony_ci ret = -EIO; 31448c2ecf20Sopenharmony_ci goto err_iomap2; 31458c2ecf20Sopenharmony_ci } 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci pr_notice("PCI memory map Virt0: %pK PCI memory map Virt2: %pK\n", 31488c2ecf20Sopenharmony_ci card->pci_mmap, card->pci_mmap1); 31498c2ecf20Sopenharmony_ci 31508c2ecf20Sopenharmony_ci ret = mwifiex_pcie_alloc_buffers(adapter); 31518c2ecf20Sopenharmony_ci if (ret) 31528c2ecf20Sopenharmony_ci goto err_alloc_buffers; 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci if (pdev->device == PCIE_DEVICE_ID_MARVELL_88W8897) 31558c2ecf20Sopenharmony_ci adapter->ignore_btcoex_events = true; 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci return 0; 31588c2ecf20Sopenharmony_ci 31598c2ecf20Sopenharmony_cierr_alloc_buffers: 31608c2ecf20Sopenharmony_ci pci_iounmap(pdev, card->pci_mmap1); 31618c2ecf20Sopenharmony_cierr_iomap2: 31628c2ecf20Sopenharmony_ci pci_release_region(pdev, 2); 31638c2ecf20Sopenharmony_cierr_req_region2: 31648c2ecf20Sopenharmony_ci pci_iounmap(pdev, card->pci_mmap); 31658c2ecf20Sopenharmony_cierr_iomap0: 31668c2ecf20Sopenharmony_ci pci_release_region(pdev, 0); 31678c2ecf20Sopenharmony_cierr_req_region0: 31688c2ecf20Sopenharmony_cierr_set_dma_mask: 31698c2ecf20Sopenharmony_ci pci_disable_device(pdev); 31708c2ecf20Sopenharmony_cierr_enable_dev: 31718c2ecf20Sopenharmony_ci return ret; 31728c2ecf20Sopenharmony_ci} 31738c2ecf20Sopenharmony_ci 31748c2ecf20Sopenharmony_ci/* 31758c2ecf20Sopenharmony_ci * This function cleans up the allocated card buffers. 31768c2ecf20Sopenharmony_ci */ 31778c2ecf20Sopenharmony_cistatic void mwifiex_cleanup_pcie(struct mwifiex_adapter *adapter) 31788c2ecf20Sopenharmony_ci{ 31798c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 31808c2ecf20Sopenharmony_ci struct pci_dev *pdev = card->dev; 31818c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 31828c2ecf20Sopenharmony_ci int ret; 31838c2ecf20Sopenharmony_ci u32 fw_status; 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci /* Perform the cancel_work_sync() only when we're not resetting 31868c2ecf20Sopenharmony_ci * the card. It's because that function never returns if we're 31878c2ecf20Sopenharmony_ci * in reset path. If we're here when resetting the card, it means 31888c2ecf20Sopenharmony_ci * that we failed to reset the card (reset failure path). 31898c2ecf20Sopenharmony_ci */ 31908c2ecf20Sopenharmony_ci if (!card->pci_reset_ongoing) { 31918c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "performing cancel_work_sync()...\n"); 31928c2ecf20Sopenharmony_ci cancel_work_sync(&card->work); 31938c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "cancel_work_sync() done\n"); 31948c2ecf20Sopenharmony_ci } else { 31958c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, 31968c2ecf20Sopenharmony_ci "skipped cancel_work_sync() because we're in card reset failure path\n"); 31978c2ecf20Sopenharmony_ci } 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci ret = mwifiex_read_reg(adapter, reg->fw_status, &fw_status); 32008c2ecf20Sopenharmony_ci if (fw_status == FIRMWARE_READY_PCIE) { 32018c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 32028c2ecf20Sopenharmony_ci "Clearing driver ready signature\n"); 32038c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000)) 32048c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 32058c2ecf20Sopenharmony_ci "Failed to write driver not-ready signature\n"); 32068c2ecf20Sopenharmony_ci } 32078c2ecf20Sopenharmony_ci 32088c2ecf20Sopenharmony_ci pci_disable_device(pdev); 32098c2ecf20Sopenharmony_ci 32108c2ecf20Sopenharmony_ci pci_iounmap(pdev, card->pci_mmap); 32118c2ecf20Sopenharmony_ci pci_iounmap(pdev, card->pci_mmap1); 32128c2ecf20Sopenharmony_ci pci_release_region(pdev, 2); 32138c2ecf20Sopenharmony_ci pci_release_region(pdev, 0); 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci mwifiex_pcie_free_buffers(adapter); 32168c2ecf20Sopenharmony_ci} 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_cistatic int mwifiex_pcie_request_irq(struct mwifiex_adapter *adapter) 32198c2ecf20Sopenharmony_ci{ 32208c2ecf20Sopenharmony_ci int ret, i, j; 32218c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 32228c2ecf20Sopenharmony_ci struct pci_dev *pdev = card->dev; 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci if (card->pcie.reg->msix_support) { 32258c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) 32268c2ecf20Sopenharmony_ci card->msix_entries[i].entry = i; 32278c2ecf20Sopenharmony_ci ret = pci_enable_msix_exact(pdev, card->msix_entries, 32288c2ecf20Sopenharmony_ci MWIFIEX_NUM_MSIX_VECTORS); 32298c2ecf20Sopenharmony_ci if (!ret) { 32308c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) { 32318c2ecf20Sopenharmony_ci card->msix_ctx[i].dev = pdev; 32328c2ecf20Sopenharmony_ci card->msix_ctx[i].msg_id = i; 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci ret = request_irq(card->msix_entries[i].vector, 32358c2ecf20Sopenharmony_ci mwifiex_pcie_interrupt, 0, 32368c2ecf20Sopenharmony_ci "MWIFIEX_PCIE_MSIX", 32378c2ecf20Sopenharmony_ci &card->msix_ctx[i]); 32388c2ecf20Sopenharmony_ci if (ret) 32398c2ecf20Sopenharmony_ci break; 32408c2ecf20Sopenharmony_ci } 32418c2ecf20Sopenharmony_ci 32428c2ecf20Sopenharmony_ci if (ret) { 32438c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "request_irq fail: %d\n", 32448c2ecf20Sopenharmony_ci ret); 32458c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) 32468c2ecf20Sopenharmony_ci free_irq(card->msix_entries[j].vector, 32478c2ecf20Sopenharmony_ci &card->msix_ctx[i]); 32488c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 32498c2ecf20Sopenharmony_ci } else { 32508c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "MSIx enabled!"); 32518c2ecf20Sopenharmony_ci card->msix_enable = 1; 32528c2ecf20Sopenharmony_ci return 0; 32538c2ecf20Sopenharmony_ci } 32548c2ecf20Sopenharmony_ci } 32558c2ecf20Sopenharmony_ci } 32568c2ecf20Sopenharmony_ci 32578c2ecf20Sopenharmony_ci if (pci_enable_msi(pdev) != 0) 32588c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 32598c2ecf20Sopenharmony_ci else 32608c2ecf20Sopenharmony_ci card->msi_enable = 1; 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "msi_enable = %d\n", card->msi_enable); 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci card->share_irq_ctx.dev = pdev; 32658c2ecf20Sopenharmony_ci card->share_irq_ctx.msg_id = -1; 32668c2ecf20Sopenharmony_ci ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, 32678c2ecf20Sopenharmony_ci "MRVL_PCIE", &card->share_irq_ctx); 32688c2ecf20Sopenharmony_ci if (ret) { 32698c2ecf20Sopenharmony_ci pr_err("request_irq failed: ret=%d\n", ret); 32708c2ecf20Sopenharmony_ci return -1; 32718c2ecf20Sopenharmony_ci } 32728c2ecf20Sopenharmony_ci 32738c2ecf20Sopenharmony_ci return 0; 32748c2ecf20Sopenharmony_ci} 32758c2ecf20Sopenharmony_ci 32768c2ecf20Sopenharmony_ci/* 32778c2ecf20Sopenharmony_ci * This function gets the firmware name for downloading by revision id 32788c2ecf20Sopenharmony_ci * 32798c2ecf20Sopenharmony_ci * Read revision id register to get revision id 32808c2ecf20Sopenharmony_ci */ 32818c2ecf20Sopenharmony_cistatic void mwifiex_pcie_get_fw_name(struct mwifiex_adapter *adapter) 32828c2ecf20Sopenharmony_ci{ 32838c2ecf20Sopenharmony_ci int revision_id = 0; 32848c2ecf20Sopenharmony_ci int version, magic; 32858c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 32868c2ecf20Sopenharmony_ci 32878c2ecf20Sopenharmony_ci switch (card->dev->device) { 32888c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8766P: 32898c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, PCIE8766_DEFAULT_FW_NAME); 32908c2ecf20Sopenharmony_ci break; 32918c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8897: 32928c2ecf20Sopenharmony_ci mwifiex_write_reg(adapter, 0x0c58, 0x80c00000); 32938c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, 0x0c58, &revision_id); 32948c2ecf20Sopenharmony_ci revision_id &= 0xff00; 32958c2ecf20Sopenharmony_ci switch (revision_id) { 32968c2ecf20Sopenharmony_ci case PCIE8897_A0: 32978c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, PCIE8897_A0_FW_NAME); 32988c2ecf20Sopenharmony_ci break; 32998c2ecf20Sopenharmony_ci case PCIE8897_B0: 33008c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, PCIE8897_B0_FW_NAME); 33018c2ecf20Sopenharmony_ci break; 33028c2ecf20Sopenharmony_ci default: 33038c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, PCIE8897_DEFAULT_FW_NAME); 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci break; 33068c2ecf20Sopenharmony_ci } 33078c2ecf20Sopenharmony_ci break; 33088c2ecf20Sopenharmony_ci case PCIE_DEVICE_ID_MARVELL_88W8997: 33098c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, 0x8, &revision_id); 33108c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, 0x0cd0, &version); 33118c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, 0x0cd4, &magic); 33128c2ecf20Sopenharmony_ci revision_id &= 0xff; 33138c2ecf20Sopenharmony_ci version &= 0x7; 33148c2ecf20Sopenharmony_ci magic &= 0xff; 33158c2ecf20Sopenharmony_ci if (revision_id == PCIE8997_A1 && 33168c2ecf20Sopenharmony_ci magic == CHIP_MAGIC_VALUE && 33178c2ecf20Sopenharmony_ci version == CHIP_VER_PCIEUART) 33188c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, PCIEUART8997_FW_NAME_V4); 33198c2ecf20Sopenharmony_ci else 33208c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, PCIEUSB8997_FW_NAME_V4); 33218c2ecf20Sopenharmony_ci break; 33228c2ecf20Sopenharmony_ci default: 33238c2ecf20Sopenharmony_ci break; 33248c2ecf20Sopenharmony_ci } 33258c2ecf20Sopenharmony_ci} 33268c2ecf20Sopenharmony_ci 33278c2ecf20Sopenharmony_ci/* 33288c2ecf20Sopenharmony_ci * This function registers the PCIE device. 33298c2ecf20Sopenharmony_ci * 33308c2ecf20Sopenharmony_ci * PCIE IRQ is claimed, block size is set and driver data is initialized. 33318c2ecf20Sopenharmony_ci */ 33328c2ecf20Sopenharmony_cistatic int mwifiex_register_dev(struct mwifiex_adapter *adapter) 33338c2ecf20Sopenharmony_ci{ 33348c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_ci /* save adapter pointer in card */ 33378c2ecf20Sopenharmony_ci card->adapter = adapter; 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_ci if (mwifiex_pcie_request_irq(adapter)) 33408c2ecf20Sopenharmony_ci return -1; 33418c2ecf20Sopenharmony_ci 33428c2ecf20Sopenharmony_ci adapter->tx_buf_size = card->pcie.tx_buf_size; 33438c2ecf20Sopenharmony_ci adapter->mem_type_mapping_tbl = card->pcie.mem_type_mapping_tbl; 33448c2ecf20Sopenharmony_ci adapter->num_mem_types = card->pcie.num_mem_types; 33458c2ecf20Sopenharmony_ci adapter->ext_scan = card->pcie.can_ext_scan; 33468c2ecf20Sopenharmony_ci mwifiex_pcie_get_fw_name(adapter); 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci return 0; 33498c2ecf20Sopenharmony_ci} 33508c2ecf20Sopenharmony_ci 33518c2ecf20Sopenharmony_ci/* 33528c2ecf20Sopenharmony_ci * This function unregisters the PCIE device. 33538c2ecf20Sopenharmony_ci * 33548c2ecf20Sopenharmony_ci * The PCIE IRQ is released, the function is disabled and driver 33558c2ecf20Sopenharmony_ci * data is set to null. 33568c2ecf20Sopenharmony_ci */ 33578c2ecf20Sopenharmony_cistatic void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) 33588c2ecf20Sopenharmony_ci{ 33598c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 33608c2ecf20Sopenharmony_ci struct pci_dev *pdev = card->dev; 33618c2ecf20Sopenharmony_ci int i; 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_ci if (card->msix_enable) { 33648c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) 33658c2ecf20Sopenharmony_ci synchronize_irq(card->msix_entries[i].vector); 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_ci for (i = 0; i < MWIFIEX_NUM_MSIX_VECTORS; i++) 33688c2ecf20Sopenharmony_ci free_irq(card->msix_entries[i].vector, 33698c2ecf20Sopenharmony_ci &card->msix_ctx[i]); 33708c2ecf20Sopenharmony_ci 33718c2ecf20Sopenharmony_ci card->msix_enable = 0; 33728c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 33738c2ecf20Sopenharmony_ci } else { 33748c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 33758c2ecf20Sopenharmony_ci "%s(): calling free_irq()\n", __func__); 33768c2ecf20Sopenharmony_ci free_irq(card->dev->irq, &card->share_irq_ctx); 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_ci if (card->msi_enable) 33798c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 33808c2ecf20Sopenharmony_ci } 33818c2ecf20Sopenharmony_ci card->adapter = NULL; 33828c2ecf20Sopenharmony_ci} 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci/* 33858c2ecf20Sopenharmony_ci * This function initializes the PCI-E host memory space, WCB rings, etc., 33868c2ecf20Sopenharmony_ci * similar to mwifiex_init_pcie(), but without resetting PCI-E state. 33878c2ecf20Sopenharmony_ci */ 33888c2ecf20Sopenharmony_cistatic void mwifiex_pcie_up_dev(struct mwifiex_adapter *adapter) 33898c2ecf20Sopenharmony_ci{ 33908c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 33918c2ecf20Sopenharmony_ci struct pci_dev *pdev = card->dev; 33928c2ecf20Sopenharmony_ci 33938c2ecf20Sopenharmony_ci /* tx_buf_size might be changed to 3584 by firmware during 33948c2ecf20Sopenharmony_ci * data transfer, we should reset it to default size. 33958c2ecf20Sopenharmony_ci */ 33968c2ecf20Sopenharmony_ci adapter->tx_buf_size = card->pcie.tx_buf_size; 33978c2ecf20Sopenharmony_ci 33988c2ecf20Sopenharmony_ci mwifiex_pcie_alloc_buffers(adapter); 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci pci_set_master(pdev); 34018c2ecf20Sopenharmony_ci} 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_ci/* This function cleans up the PCI-E host memory space. */ 34048c2ecf20Sopenharmony_cistatic void mwifiex_pcie_down_dev(struct mwifiex_adapter *adapter) 34058c2ecf20Sopenharmony_ci{ 34068c2ecf20Sopenharmony_ci struct pcie_service_card *card = adapter->card; 34078c2ecf20Sopenharmony_ci const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; 34088c2ecf20Sopenharmony_ci struct pci_dev *pdev = card->dev; 34098c2ecf20Sopenharmony_ci 34108c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000)) 34118c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Failed to write driver not-ready signature\n"); 34128c2ecf20Sopenharmony_ci 34138c2ecf20Sopenharmony_ci pci_clear_master(pdev); 34148c2ecf20Sopenharmony_ci 34158c2ecf20Sopenharmony_ci adapter->seq_num = 0; 34168c2ecf20Sopenharmony_ci 34178c2ecf20Sopenharmony_ci mwifiex_pcie_free_buffers(adapter); 34188c2ecf20Sopenharmony_ci} 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_cistatic struct mwifiex_if_ops pcie_ops = { 34218c2ecf20Sopenharmony_ci .init_if = mwifiex_init_pcie, 34228c2ecf20Sopenharmony_ci .cleanup_if = mwifiex_cleanup_pcie, 34238c2ecf20Sopenharmony_ci .check_fw_status = mwifiex_check_fw_status, 34248c2ecf20Sopenharmony_ci .check_winner_status = mwifiex_check_winner_status, 34258c2ecf20Sopenharmony_ci .prog_fw = mwifiex_prog_fw_w_helper, 34268c2ecf20Sopenharmony_ci .register_dev = mwifiex_register_dev, 34278c2ecf20Sopenharmony_ci .unregister_dev = mwifiex_unregister_dev, 34288c2ecf20Sopenharmony_ci .enable_int = mwifiex_pcie_enable_host_int, 34298c2ecf20Sopenharmony_ci .disable_int = mwifiex_pcie_disable_host_int_noerr, 34308c2ecf20Sopenharmony_ci .process_int_status = mwifiex_process_int_status, 34318c2ecf20Sopenharmony_ci .host_to_card = mwifiex_pcie_host_to_card, 34328c2ecf20Sopenharmony_ci .wakeup = mwifiex_pm_wakeup_card, 34338c2ecf20Sopenharmony_ci .wakeup_complete = mwifiex_pm_wakeup_card_complete, 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci /* PCIE specific */ 34368c2ecf20Sopenharmony_ci .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete, 34378c2ecf20Sopenharmony_ci .event_complete = mwifiex_pcie_event_complete, 34388c2ecf20Sopenharmony_ci .update_mp_end_port = NULL, 34398c2ecf20Sopenharmony_ci .cleanup_mpa_buf = NULL, 34408c2ecf20Sopenharmony_ci .init_fw_port = mwifiex_pcie_init_fw_port, 34418c2ecf20Sopenharmony_ci .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, 34428c2ecf20Sopenharmony_ci .card_reset = mwifiex_pcie_card_reset, 34438c2ecf20Sopenharmony_ci .reg_dump = mwifiex_pcie_reg_dump, 34448c2ecf20Sopenharmony_ci .device_dump = mwifiex_pcie_device_dump, 34458c2ecf20Sopenharmony_ci .down_dev = mwifiex_pcie_down_dev, 34468c2ecf20Sopenharmony_ci .up_dev = mwifiex_pcie_up_dev, 34478c2ecf20Sopenharmony_ci}; 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_cimodule_pci_driver(mwifiex_pcie); 34508c2ecf20Sopenharmony_ci 34518c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 34528c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION); 34538c2ecf20Sopenharmony_ciMODULE_VERSION(PCIE_VERSION); 34548c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3455