18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * NXP Wireless LAN device driver: SDIO 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/firmware.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "decl.h" 238c2ecf20Sopenharmony_ci#include "ioctl.h" 248c2ecf20Sopenharmony_ci#include "util.h" 258c2ecf20Sopenharmony_ci#include "fw.h" 268c2ecf20Sopenharmony_ci#include "main.h" 278c2ecf20Sopenharmony_ci#include "wmm.h" 288c2ecf20Sopenharmony_ci#include "11n.h" 298c2ecf20Sopenharmony_ci#include "sdio.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define SDIO_VERSION "1.0" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void mwifiex_sdio_work(struct work_struct *work); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic struct mwifiex_if_ops sdio_ops; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { 398c2ecf20Sopenharmony_ci .start_rd_port = 1, 408c2ecf20Sopenharmony_ci .start_wr_port = 1, 418c2ecf20Sopenharmony_ci .base_0_reg = 0x0040, 428c2ecf20Sopenharmony_ci .base_1_reg = 0x0041, 438c2ecf20Sopenharmony_ci .poll_reg = 0x30, 448c2ecf20Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, 458c2ecf20Sopenharmony_ci .host_int_rsr_reg = 0x1, 468c2ecf20Sopenharmony_ci .host_int_mask_reg = 0x02, 478c2ecf20Sopenharmony_ci .host_int_status_reg = 0x03, 488c2ecf20Sopenharmony_ci .status_reg_0 = 0x60, 498c2ecf20Sopenharmony_ci .status_reg_1 = 0x61, 508c2ecf20Sopenharmony_ci .sdio_int_mask = 0x3f, 518c2ecf20Sopenharmony_ci .data_port_mask = 0x0000fffe, 528c2ecf20Sopenharmony_ci .io_port_0_reg = 0x78, 538c2ecf20Sopenharmony_ci .io_port_1_reg = 0x79, 548c2ecf20Sopenharmony_ci .io_port_2_reg = 0x7A, 558c2ecf20Sopenharmony_ci .max_mp_regs = 64, 568c2ecf20Sopenharmony_ci .rd_bitmap_l = 0x04, 578c2ecf20Sopenharmony_ci .rd_bitmap_u = 0x05, 588c2ecf20Sopenharmony_ci .wr_bitmap_l = 0x06, 598c2ecf20Sopenharmony_ci .wr_bitmap_u = 0x07, 608c2ecf20Sopenharmony_ci .rd_len_p0_l = 0x08, 618c2ecf20Sopenharmony_ci .rd_len_p0_u = 0x09, 628c2ecf20Sopenharmony_ci .card_misc_cfg_reg = 0x6c, 638c2ecf20Sopenharmony_ci .func1_dump_reg_start = 0x0, 648c2ecf20Sopenharmony_ci .func1_dump_reg_end = 0x9, 658c2ecf20Sopenharmony_ci .func1_scratch_reg = 0x60, 668c2ecf20Sopenharmony_ci .func1_spec_reg_num = 5, 678c2ecf20Sopenharmony_ci .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { 718c2ecf20Sopenharmony_ci .start_rd_port = 0, 728c2ecf20Sopenharmony_ci .start_wr_port = 0, 738c2ecf20Sopenharmony_ci .base_0_reg = 0x60, 748c2ecf20Sopenharmony_ci .base_1_reg = 0x61, 758c2ecf20Sopenharmony_ci .poll_reg = 0x50, 768c2ecf20Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 778c2ecf20Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 788c2ecf20Sopenharmony_ci .host_int_rsr_reg = 0x1, 798c2ecf20Sopenharmony_ci .host_int_status_reg = 0x03, 808c2ecf20Sopenharmony_ci .host_int_mask_reg = 0x02, 818c2ecf20Sopenharmony_ci .status_reg_0 = 0xc0, 828c2ecf20Sopenharmony_ci .status_reg_1 = 0xc1, 838c2ecf20Sopenharmony_ci .sdio_int_mask = 0xff, 848c2ecf20Sopenharmony_ci .data_port_mask = 0xffffffff, 858c2ecf20Sopenharmony_ci .io_port_0_reg = 0xD8, 868c2ecf20Sopenharmony_ci .io_port_1_reg = 0xD9, 878c2ecf20Sopenharmony_ci .io_port_2_reg = 0xDA, 888c2ecf20Sopenharmony_ci .max_mp_regs = 184, 898c2ecf20Sopenharmony_ci .rd_bitmap_l = 0x04, 908c2ecf20Sopenharmony_ci .rd_bitmap_u = 0x05, 918c2ecf20Sopenharmony_ci .rd_bitmap_1l = 0x06, 928c2ecf20Sopenharmony_ci .rd_bitmap_1u = 0x07, 938c2ecf20Sopenharmony_ci .wr_bitmap_l = 0x08, 948c2ecf20Sopenharmony_ci .wr_bitmap_u = 0x09, 958c2ecf20Sopenharmony_ci .wr_bitmap_1l = 0x0a, 968c2ecf20Sopenharmony_ci .wr_bitmap_1u = 0x0b, 978c2ecf20Sopenharmony_ci .rd_len_p0_l = 0x0c, 988c2ecf20Sopenharmony_ci .rd_len_p0_u = 0x0d, 998c2ecf20Sopenharmony_ci .card_misc_cfg_reg = 0xcc, 1008c2ecf20Sopenharmony_ci .card_cfg_2_1_reg = 0xcd, 1018c2ecf20Sopenharmony_ci .cmd_rd_len_0 = 0xb4, 1028c2ecf20Sopenharmony_ci .cmd_rd_len_1 = 0xb5, 1038c2ecf20Sopenharmony_ci .cmd_rd_len_2 = 0xb6, 1048c2ecf20Sopenharmony_ci .cmd_rd_len_3 = 0xb7, 1058c2ecf20Sopenharmony_ci .cmd_cfg_0 = 0xb8, 1068c2ecf20Sopenharmony_ci .cmd_cfg_1 = 0xb9, 1078c2ecf20Sopenharmony_ci .cmd_cfg_2 = 0xba, 1088c2ecf20Sopenharmony_ci .cmd_cfg_3 = 0xbb, 1098c2ecf20Sopenharmony_ci .fw_dump_host_ready = 0xee, 1108c2ecf20Sopenharmony_ci .fw_dump_ctrl = 0xe2, 1118c2ecf20Sopenharmony_ci .fw_dump_start = 0xe3, 1128c2ecf20Sopenharmony_ci .fw_dump_end = 0xea, 1138c2ecf20Sopenharmony_ci .func1_dump_reg_start = 0x0, 1148c2ecf20Sopenharmony_ci .func1_dump_reg_end = 0xb, 1158c2ecf20Sopenharmony_ci .func1_scratch_reg = 0xc0, 1168c2ecf20Sopenharmony_ci .func1_spec_reg_num = 8, 1178c2ecf20Sopenharmony_ci .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, 1188c2ecf20Sopenharmony_ci 0x59, 0x5c, 0x5d}, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8977 = { 1228c2ecf20Sopenharmony_ci .start_rd_port = 0, 1238c2ecf20Sopenharmony_ci .start_wr_port = 0, 1248c2ecf20Sopenharmony_ci .base_0_reg = 0xF8, 1258c2ecf20Sopenharmony_ci .base_1_reg = 0xF9, 1268c2ecf20Sopenharmony_ci .poll_reg = 0x5C, 1278c2ecf20Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 1288c2ecf20Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 1298c2ecf20Sopenharmony_ci .host_int_rsr_reg = 0x4, 1308c2ecf20Sopenharmony_ci .host_int_status_reg = 0x0C, 1318c2ecf20Sopenharmony_ci .host_int_mask_reg = 0x08, 1328c2ecf20Sopenharmony_ci .status_reg_0 = 0xE8, 1338c2ecf20Sopenharmony_ci .status_reg_1 = 0xE9, 1348c2ecf20Sopenharmony_ci .sdio_int_mask = 0xff, 1358c2ecf20Sopenharmony_ci .data_port_mask = 0xffffffff, 1368c2ecf20Sopenharmony_ci .io_port_0_reg = 0xE4, 1378c2ecf20Sopenharmony_ci .io_port_1_reg = 0xE5, 1388c2ecf20Sopenharmony_ci .io_port_2_reg = 0xE6, 1398c2ecf20Sopenharmony_ci .max_mp_regs = 196, 1408c2ecf20Sopenharmony_ci .rd_bitmap_l = 0x10, 1418c2ecf20Sopenharmony_ci .rd_bitmap_u = 0x11, 1428c2ecf20Sopenharmony_ci .rd_bitmap_1l = 0x12, 1438c2ecf20Sopenharmony_ci .rd_bitmap_1u = 0x13, 1448c2ecf20Sopenharmony_ci .wr_bitmap_l = 0x14, 1458c2ecf20Sopenharmony_ci .wr_bitmap_u = 0x15, 1468c2ecf20Sopenharmony_ci .wr_bitmap_1l = 0x16, 1478c2ecf20Sopenharmony_ci .wr_bitmap_1u = 0x17, 1488c2ecf20Sopenharmony_ci .rd_len_p0_l = 0x18, 1498c2ecf20Sopenharmony_ci .rd_len_p0_u = 0x19, 1508c2ecf20Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 1518c2ecf20Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 1528c2ecf20Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 1538c2ecf20Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 1548c2ecf20Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 1558c2ecf20Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 1568c2ecf20Sopenharmony_ci .cmd_cfg_0 = 0xc4, 1578c2ecf20Sopenharmony_ci .cmd_cfg_1 = 0xc5, 1588c2ecf20Sopenharmony_ci .cmd_cfg_2 = 0xc6, 1598c2ecf20Sopenharmony_ci .cmd_cfg_3 = 0xc7, 1608c2ecf20Sopenharmony_ci .fw_dump_host_ready = 0xcc, 1618c2ecf20Sopenharmony_ci .fw_dump_ctrl = 0xf0, 1628c2ecf20Sopenharmony_ci .fw_dump_start = 0xf1, 1638c2ecf20Sopenharmony_ci .fw_dump_end = 0xf8, 1648c2ecf20Sopenharmony_ci .func1_dump_reg_start = 0x10, 1658c2ecf20Sopenharmony_ci .func1_dump_reg_end = 0x17, 1668c2ecf20Sopenharmony_ci .func1_scratch_reg = 0xe8, 1678c2ecf20Sopenharmony_ci .func1_spec_reg_num = 13, 1688c2ecf20Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 1698c2ecf20Sopenharmony_ci 0x60, 0x61, 0x62, 0x64, 1708c2ecf20Sopenharmony_ci 0x65, 0x66, 0x68, 0x69, 1718c2ecf20Sopenharmony_ci 0x6a}, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = { 1758c2ecf20Sopenharmony_ci .start_rd_port = 0, 1768c2ecf20Sopenharmony_ci .start_wr_port = 0, 1778c2ecf20Sopenharmony_ci .base_0_reg = 0xF8, 1788c2ecf20Sopenharmony_ci .base_1_reg = 0xF9, 1798c2ecf20Sopenharmony_ci .poll_reg = 0x5C, 1808c2ecf20Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 1818c2ecf20Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 1828c2ecf20Sopenharmony_ci .host_int_rsr_reg = 0x4, 1838c2ecf20Sopenharmony_ci .host_int_status_reg = 0x0C, 1848c2ecf20Sopenharmony_ci .host_int_mask_reg = 0x08, 1858c2ecf20Sopenharmony_ci .status_reg_0 = 0xE8, 1868c2ecf20Sopenharmony_ci .status_reg_1 = 0xE9, 1878c2ecf20Sopenharmony_ci .sdio_int_mask = 0xff, 1888c2ecf20Sopenharmony_ci .data_port_mask = 0xffffffff, 1898c2ecf20Sopenharmony_ci .io_port_0_reg = 0xE4, 1908c2ecf20Sopenharmony_ci .io_port_1_reg = 0xE5, 1918c2ecf20Sopenharmony_ci .io_port_2_reg = 0xE6, 1928c2ecf20Sopenharmony_ci .max_mp_regs = 196, 1938c2ecf20Sopenharmony_ci .rd_bitmap_l = 0x10, 1948c2ecf20Sopenharmony_ci .rd_bitmap_u = 0x11, 1958c2ecf20Sopenharmony_ci .rd_bitmap_1l = 0x12, 1968c2ecf20Sopenharmony_ci .rd_bitmap_1u = 0x13, 1978c2ecf20Sopenharmony_ci .wr_bitmap_l = 0x14, 1988c2ecf20Sopenharmony_ci .wr_bitmap_u = 0x15, 1998c2ecf20Sopenharmony_ci .wr_bitmap_1l = 0x16, 2008c2ecf20Sopenharmony_ci .wr_bitmap_1u = 0x17, 2018c2ecf20Sopenharmony_ci .rd_len_p0_l = 0x18, 2028c2ecf20Sopenharmony_ci .rd_len_p0_u = 0x19, 2038c2ecf20Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 2048c2ecf20Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 2058c2ecf20Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 2068c2ecf20Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 2078c2ecf20Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 2088c2ecf20Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 2098c2ecf20Sopenharmony_ci .cmd_cfg_0 = 0xc4, 2108c2ecf20Sopenharmony_ci .cmd_cfg_1 = 0xc5, 2118c2ecf20Sopenharmony_ci .cmd_cfg_2 = 0xc6, 2128c2ecf20Sopenharmony_ci .cmd_cfg_3 = 0xc7, 2138c2ecf20Sopenharmony_ci .fw_dump_host_ready = 0xcc, 2148c2ecf20Sopenharmony_ci .fw_dump_ctrl = 0xf0, 2158c2ecf20Sopenharmony_ci .fw_dump_start = 0xf1, 2168c2ecf20Sopenharmony_ci .fw_dump_end = 0xf8, 2178c2ecf20Sopenharmony_ci .func1_dump_reg_start = 0x10, 2188c2ecf20Sopenharmony_ci .func1_dump_reg_end = 0x17, 2198c2ecf20Sopenharmony_ci .func1_scratch_reg = 0xe8, 2208c2ecf20Sopenharmony_ci .func1_spec_reg_num = 13, 2218c2ecf20Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 2228c2ecf20Sopenharmony_ci 0x60, 0x61, 0x62, 0x64, 2238c2ecf20Sopenharmony_ci 0x65, 0x66, 0x68, 0x69, 2248c2ecf20Sopenharmony_ci 0x6a}, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { 2288c2ecf20Sopenharmony_ci .start_rd_port = 0, 2298c2ecf20Sopenharmony_ci .start_wr_port = 0, 2308c2ecf20Sopenharmony_ci .base_0_reg = 0x6C, 2318c2ecf20Sopenharmony_ci .base_1_reg = 0x6D, 2328c2ecf20Sopenharmony_ci .poll_reg = 0x5C, 2338c2ecf20Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 2348c2ecf20Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 2358c2ecf20Sopenharmony_ci .host_int_rsr_reg = 0x4, 2368c2ecf20Sopenharmony_ci .host_int_status_reg = 0x0C, 2378c2ecf20Sopenharmony_ci .host_int_mask_reg = 0x08, 2388c2ecf20Sopenharmony_ci .status_reg_0 = 0x90, 2398c2ecf20Sopenharmony_ci .status_reg_1 = 0x91, 2408c2ecf20Sopenharmony_ci .sdio_int_mask = 0xff, 2418c2ecf20Sopenharmony_ci .data_port_mask = 0xffffffff, 2428c2ecf20Sopenharmony_ci .io_port_0_reg = 0xE4, 2438c2ecf20Sopenharmony_ci .io_port_1_reg = 0xE5, 2448c2ecf20Sopenharmony_ci .io_port_2_reg = 0xE6, 2458c2ecf20Sopenharmony_ci .max_mp_regs = 196, 2468c2ecf20Sopenharmony_ci .rd_bitmap_l = 0x10, 2478c2ecf20Sopenharmony_ci .rd_bitmap_u = 0x11, 2488c2ecf20Sopenharmony_ci .rd_bitmap_1l = 0x12, 2498c2ecf20Sopenharmony_ci .rd_bitmap_1u = 0x13, 2508c2ecf20Sopenharmony_ci .wr_bitmap_l = 0x14, 2518c2ecf20Sopenharmony_ci .wr_bitmap_u = 0x15, 2528c2ecf20Sopenharmony_ci .wr_bitmap_1l = 0x16, 2538c2ecf20Sopenharmony_ci .wr_bitmap_1u = 0x17, 2548c2ecf20Sopenharmony_ci .rd_len_p0_l = 0x18, 2558c2ecf20Sopenharmony_ci .rd_len_p0_u = 0x19, 2568c2ecf20Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 2578c2ecf20Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 2588c2ecf20Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 2598c2ecf20Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 2608c2ecf20Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 2618c2ecf20Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 2628c2ecf20Sopenharmony_ci .cmd_cfg_0 = 0xc4, 2638c2ecf20Sopenharmony_ci .cmd_cfg_1 = 0xc5, 2648c2ecf20Sopenharmony_ci .cmd_cfg_2 = 0xc6, 2658c2ecf20Sopenharmony_ci .cmd_cfg_3 = 0xc7, 2668c2ecf20Sopenharmony_ci .func1_dump_reg_start = 0x10, 2678c2ecf20Sopenharmony_ci .func1_dump_reg_end = 0x17, 2688c2ecf20Sopenharmony_ci .func1_scratch_reg = 0x90, 2698c2ecf20Sopenharmony_ci .func1_spec_reg_num = 13, 2708c2ecf20Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 2718c2ecf20Sopenharmony_ci 0x61, 0x62, 0x64, 0x65, 0x66, 2728c2ecf20Sopenharmony_ci 0x68, 0x69, 0x6a}, 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_card_reg mwifiex_reg_sd8987 = { 2768c2ecf20Sopenharmony_ci .start_rd_port = 0, 2778c2ecf20Sopenharmony_ci .start_wr_port = 0, 2788c2ecf20Sopenharmony_ci .base_0_reg = 0xF8, 2798c2ecf20Sopenharmony_ci .base_1_reg = 0xF9, 2808c2ecf20Sopenharmony_ci .poll_reg = 0x5C, 2818c2ecf20Sopenharmony_ci .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | 2828c2ecf20Sopenharmony_ci CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, 2838c2ecf20Sopenharmony_ci .host_int_rsr_reg = 0x4, 2848c2ecf20Sopenharmony_ci .host_int_status_reg = 0x0C, 2858c2ecf20Sopenharmony_ci .host_int_mask_reg = 0x08, 2868c2ecf20Sopenharmony_ci .status_reg_0 = 0xE8, 2878c2ecf20Sopenharmony_ci .status_reg_1 = 0xE9, 2888c2ecf20Sopenharmony_ci .sdio_int_mask = 0xff, 2898c2ecf20Sopenharmony_ci .data_port_mask = 0xffffffff, 2908c2ecf20Sopenharmony_ci .io_port_0_reg = 0xE4, 2918c2ecf20Sopenharmony_ci .io_port_1_reg = 0xE5, 2928c2ecf20Sopenharmony_ci .io_port_2_reg = 0xE6, 2938c2ecf20Sopenharmony_ci .max_mp_regs = 196, 2948c2ecf20Sopenharmony_ci .rd_bitmap_l = 0x10, 2958c2ecf20Sopenharmony_ci .rd_bitmap_u = 0x11, 2968c2ecf20Sopenharmony_ci .rd_bitmap_1l = 0x12, 2978c2ecf20Sopenharmony_ci .rd_bitmap_1u = 0x13, 2988c2ecf20Sopenharmony_ci .wr_bitmap_l = 0x14, 2998c2ecf20Sopenharmony_ci .wr_bitmap_u = 0x15, 3008c2ecf20Sopenharmony_ci .wr_bitmap_1l = 0x16, 3018c2ecf20Sopenharmony_ci .wr_bitmap_1u = 0x17, 3028c2ecf20Sopenharmony_ci .rd_len_p0_l = 0x18, 3038c2ecf20Sopenharmony_ci .rd_len_p0_u = 0x19, 3048c2ecf20Sopenharmony_ci .card_misc_cfg_reg = 0xd8, 3058c2ecf20Sopenharmony_ci .card_cfg_2_1_reg = 0xd9, 3068c2ecf20Sopenharmony_ci .cmd_rd_len_0 = 0xc0, 3078c2ecf20Sopenharmony_ci .cmd_rd_len_1 = 0xc1, 3088c2ecf20Sopenharmony_ci .cmd_rd_len_2 = 0xc2, 3098c2ecf20Sopenharmony_ci .cmd_rd_len_3 = 0xc3, 3108c2ecf20Sopenharmony_ci .cmd_cfg_0 = 0xc4, 3118c2ecf20Sopenharmony_ci .cmd_cfg_1 = 0xc5, 3128c2ecf20Sopenharmony_ci .cmd_cfg_2 = 0xc6, 3138c2ecf20Sopenharmony_ci .cmd_cfg_3 = 0xc7, 3148c2ecf20Sopenharmony_ci .fw_dump_host_ready = 0xcc, 3158c2ecf20Sopenharmony_ci .fw_dump_ctrl = 0xf9, 3168c2ecf20Sopenharmony_ci .fw_dump_start = 0xf1, 3178c2ecf20Sopenharmony_ci .fw_dump_end = 0xf8, 3188c2ecf20Sopenharmony_ci .func1_dump_reg_start = 0x10, 3198c2ecf20Sopenharmony_ci .func1_dump_reg_end = 0x17, 3208c2ecf20Sopenharmony_ci .func1_scratch_reg = 0xE8, 3218c2ecf20Sopenharmony_ci .func1_spec_reg_num = 13, 3228c2ecf20Sopenharmony_ci .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 3238c2ecf20Sopenharmony_ci 0x61, 0x62, 0x64, 0x65, 0x66, 3248c2ecf20Sopenharmony_ci 0x68, 0x69, 0x6a}, 3258c2ecf20Sopenharmony_ci}; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { 3288c2ecf20Sopenharmony_ci .firmware = SD8786_DEFAULT_FW_NAME, 3298c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 3308c2ecf20Sopenharmony_ci .max_ports = 16, 3318c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 8, 3328c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 3338c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 3348c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 3358c2ecf20Sopenharmony_ci .supports_sdio_new_mode = false, 3368c2ecf20Sopenharmony_ci .has_control_mask = true, 3378c2ecf20Sopenharmony_ci .can_dump_fw = false, 3388c2ecf20Sopenharmony_ci .can_auto_tdls = false, 3398c2ecf20Sopenharmony_ci .can_ext_scan = false, 3408c2ecf20Sopenharmony_ci}; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { 3438c2ecf20Sopenharmony_ci .firmware = SD8787_DEFAULT_FW_NAME, 3448c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 3458c2ecf20Sopenharmony_ci .max_ports = 16, 3468c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 8, 3478c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 3488c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 3498c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 3508c2ecf20Sopenharmony_ci .supports_sdio_new_mode = false, 3518c2ecf20Sopenharmony_ci .has_control_mask = true, 3528c2ecf20Sopenharmony_ci .can_dump_fw = false, 3538c2ecf20Sopenharmony_ci .can_auto_tdls = false, 3548c2ecf20Sopenharmony_ci .can_ext_scan = true, 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { 3588c2ecf20Sopenharmony_ci .firmware = SD8797_DEFAULT_FW_NAME, 3598c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 3608c2ecf20Sopenharmony_ci .max_ports = 16, 3618c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 8, 3628c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 3638c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 3648c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 3658c2ecf20Sopenharmony_ci .supports_sdio_new_mode = false, 3668c2ecf20Sopenharmony_ci .has_control_mask = true, 3678c2ecf20Sopenharmony_ci .can_dump_fw = false, 3688c2ecf20Sopenharmony_ci .can_auto_tdls = false, 3698c2ecf20Sopenharmony_ci .can_ext_scan = true, 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { 3738c2ecf20Sopenharmony_ci .firmware = SD8897_DEFAULT_FW_NAME, 3748c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd8897, 3758c2ecf20Sopenharmony_ci .max_ports = 32, 3768c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 16, 3778c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 3788c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 3798c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 3808c2ecf20Sopenharmony_ci .supports_sdio_new_mode = true, 3818c2ecf20Sopenharmony_ci .has_control_mask = false, 3828c2ecf20Sopenharmony_ci .can_dump_fw = true, 3838c2ecf20Sopenharmony_ci .can_auto_tdls = false, 3848c2ecf20Sopenharmony_ci .can_ext_scan = true, 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = { 3888c2ecf20Sopenharmony_ci .firmware = SD8977_DEFAULT_FW_NAME, 3898c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd8977, 3908c2ecf20Sopenharmony_ci .max_ports = 32, 3918c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 16, 3928c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 3938c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 3948c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 3958c2ecf20Sopenharmony_ci .supports_sdio_new_mode = true, 3968c2ecf20Sopenharmony_ci .has_control_mask = false, 3978c2ecf20Sopenharmony_ci .can_dump_fw = true, 3988c2ecf20Sopenharmony_ci .fw_dump_enh = true, 3998c2ecf20Sopenharmony_ci .can_auto_tdls = false, 4008c2ecf20Sopenharmony_ci .can_ext_scan = true, 4018c2ecf20Sopenharmony_ci}; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = { 4048c2ecf20Sopenharmony_ci .firmware = SD8997_DEFAULT_FW_NAME, 4058c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd8997, 4068c2ecf20Sopenharmony_ci .max_ports = 32, 4078c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 16, 4088c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, 4098c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 4108c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 4118c2ecf20Sopenharmony_ci .supports_sdio_new_mode = true, 4128c2ecf20Sopenharmony_ci .has_control_mask = false, 4138c2ecf20Sopenharmony_ci .can_dump_fw = true, 4148c2ecf20Sopenharmony_ci .fw_dump_enh = true, 4158c2ecf20Sopenharmony_ci .can_auto_tdls = false, 4168c2ecf20Sopenharmony_ci .can_ext_scan = true, 4178c2ecf20Sopenharmony_ci}; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { 4208c2ecf20Sopenharmony_ci .firmware = SD8887_DEFAULT_FW_NAME, 4218c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd8887, 4228c2ecf20Sopenharmony_ci .max_ports = 32, 4238c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 16, 4248c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 4258c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, 4268c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, 4278c2ecf20Sopenharmony_ci .supports_sdio_new_mode = true, 4288c2ecf20Sopenharmony_ci .has_control_mask = false, 4298c2ecf20Sopenharmony_ci .can_dump_fw = false, 4308c2ecf20Sopenharmony_ci .can_auto_tdls = true, 4318c2ecf20Sopenharmony_ci .can_ext_scan = true, 4328c2ecf20Sopenharmony_ci}; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = { 4358c2ecf20Sopenharmony_ci .firmware = SD8987_DEFAULT_FW_NAME, 4368c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd8987, 4378c2ecf20Sopenharmony_ci .max_ports = 32, 4388c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 16, 4398c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 4408c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 4418c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, 4428c2ecf20Sopenharmony_ci .supports_sdio_new_mode = true, 4438c2ecf20Sopenharmony_ci .has_control_mask = false, 4448c2ecf20Sopenharmony_ci .can_dump_fw = true, 4458c2ecf20Sopenharmony_ci .fw_dump_enh = true, 4468c2ecf20Sopenharmony_ci .can_auto_tdls = true, 4478c2ecf20Sopenharmony_ci .can_ext_scan = true, 4488c2ecf20Sopenharmony_ci}; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = { 4518c2ecf20Sopenharmony_ci .firmware = SD8801_DEFAULT_FW_NAME, 4528c2ecf20Sopenharmony_ci .reg = &mwifiex_reg_sd87xx, 4538c2ecf20Sopenharmony_ci .max_ports = 16, 4548c2ecf20Sopenharmony_ci .mp_agg_pkt_limit = 8, 4558c2ecf20Sopenharmony_ci .supports_sdio_new_mode = false, 4568c2ecf20Sopenharmony_ci .has_control_mask = true, 4578c2ecf20Sopenharmony_ci .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, 4588c2ecf20Sopenharmony_ci .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 4598c2ecf20Sopenharmony_ci .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, 4608c2ecf20Sopenharmony_ci .can_dump_fw = false, 4618c2ecf20Sopenharmony_ci .can_auto_tdls = false, 4628c2ecf20Sopenharmony_ci .can_ext_scan = true, 4638c2ecf20Sopenharmony_ci}; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic struct memory_type_mapping generic_mem_type_map[] = { 4668c2ecf20Sopenharmony_ci {"DUMP", NULL, 0, 0xDD}, 4678c2ecf20Sopenharmony_ci}; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic struct memory_type_mapping mem_type_mapping_tbl[] = { 4708c2ecf20Sopenharmony_ci {"ITCM", NULL, 0, 0xF0}, 4718c2ecf20Sopenharmony_ci {"DTCM", NULL, 0, 0xF1}, 4728c2ecf20Sopenharmony_ci {"SQRAM", NULL, 0, 0xF2}, 4738c2ecf20Sopenharmony_ci {"APU", NULL, 0, 0xF3}, 4748c2ecf20Sopenharmony_ci {"CIU", NULL, 0, 0xF4}, 4758c2ecf20Sopenharmony_ci {"ICU", NULL, 0, 0xF5}, 4768c2ecf20Sopenharmony_ci {"MAC", NULL, 0, 0xF6}, 4778c2ecf20Sopenharmony_ci {"EXT7", NULL, 0, 0xF7}, 4788c2ecf20Sopenharmony_ci {"EXT8", NULL, 0, 0xF8}, 4798c2ecf20Sopenharmony_ci {"EXT9", NULL, 0, 0xF9}, 4808c2ecf20Sopenharmony_ci {"EXT10", NULL, 0, 0xFA}, 4818c2ecf20Sopenharmony_ci {"EXT11", NULL, 0, 0xFB}, 4828c2ecf20Sopenharmony_ci {"EXT12", NULL, 0, 0xFC}, 4838c2ecf20Sopenharmony_ci {"EXT13", NULL, 0, 0xFD}, 4848c2ecf20Sopenharmony_ci {"EXTLAST", NULL, 0, 0xFE}, 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic const struct of_device_id mwifiex_sdio_of_match_table[] __maybe_unused = { 4888c2ecf20Sopenharmony_ci { .compatible = "marvell,sd8787" }, 4898c2ecf20Sopenharmony_ci { .compatible = "marvell,sd8897" }, 4908c2ecf20Sopenharmony_ci { .compatible = "marvell,sd8997" }, 4918c2ecf20Sopenharmony_ci { } 4928c2ecf20Sopenharmony_ci}; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci/* This function parse device tree node using mmc subnode devicetree API. 4958c2ecf20Sopenharmony_ci * The device node is saved in card->plt_of_node. 4968c2ecf20Sopenharmony_ci * if the device tree node exist and include interrupts attributes, this 4978c2ecf20Sopenharmony_ci * function will also request platform specific wakeup interrupt. 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_cistatic int mwifiex_sdio_probe_of(struct device *dev) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci if (!of_match_node(mwifiex_sdio_of_match_table, dev->of_node)) { 5028c2ecf20Sopenharmony_ci dev_err(dev, "required compatible string missing\n"); 5038c2ecf20Sopenharmony_ci return -EINVAL; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci return 0; 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci/* 5108c2ecf20Sopenharmony_ci * SDIO probe. 5118c2ecf20Sopenharmony_ci * 5128c2ecf20Sopenharmony_ci * This function probes an mwifiex device and registers it. It allocates 5138c2ecf20Sopenharmony_ci * the card structure, enables SDIO function number and initiates the 5148c2ecf20Sopenharmony_ci * device registration and initialization procedure by adding a logical 5158c2ecf20Sopenharmony_ci * interface. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_cistatic int 5188c2ecf20Sopenharmony_cimwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci int ret; 5218c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = NULL; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci pr_debug("info: vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", 5248c2ecf20Sopenharmony_ci func->vendor, func->device, func->class, func->num); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL); 5278c2ecf20Sopenharmony_ci if (!card) 5288c2ecf20Sopenharmony_ci return -ENOMEM; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci init_completion(&card->fw_done); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci card->func = func; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (id->driver_data) { 5378c2ecf20Sopenharmony_ci struct mwifiex_sdio_device *data = (void *)id->driver_data; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci card->firmware = data->firmware; 5408c2ecf20Sopenharmony_ci card->reg = data->reg; 5418c2ecf20Sopenharmony_ci card->max_ports = data->max_ports; 5428c2ecf20Sopenharmony_ci card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; 5438c2ecf20Sopenharmony_ci card->supports_sdio_new_mode = data->supports_sdio_new_mode; 5448c2ecf20Sopenharmony_ci card->has_control_mask = data->has_control_mask; 5458c2ecf20Sopenharmony_ci card->tx_buf_size = data->tx_buf_size; 5468c2ecf20Sopenharmony_ci card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; 5478c2ecf20Sopenharmony_ci card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; 5488c2ecf20Sopenharmony_ci card->can_dump_fw = data->can_dump_fw; 5498c2ecf20Sopenharmony_ci card->fw_dump_enh = data->fw_dump_enh; 5508c2ecf20Sopenharmony_ci card->can_auto_tdls = data->can_auto_tdls; 5518c2ecf20Sopenharmony_ci card->can_ext_scan = data->can_ext_scan; 5528c2ecf20Sopenharmony_ci INIT_WORK(&card->work, mwifiex_sdio_work); 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci sdio_claim_host(func); 5568c2ecf20Sopenharmony_ci ret = sdio_enable_func(func); 5578c2ecf20Sopenharmony_ci sdio_release_host(func); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (ret) { 5608c2ecf20Sopenharmony_ci dev_err(&func->dev, "failed to enable function\n"); 5618c2ecf20Sopenharmony_ci return ret; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* device tree node parsing and platform specific configuration*/ 5658c2ecf20Sopenharmony_ci if (func->dev.of_node) { 5668c2ecf20Sopenharmony_ci ret = mwifiex_sdio_probe_of(&func->dev); 5678c2ecf20Sopenharmony_ci if (ret) 5688c2ecf20Sopenharmony_ci goto err_disable; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci ret = mwifiex_add_card(card, &card->fw_done, &sdio_ops, 5728c2ecf20Sopenharmony_ci MWIFIEX_SDIO, &func->dev); 5738c2ecf20Sopenharmony_ci if (ret) { 5748c2ecf20Sopenharmony_ci dev_err(&func->dev, "add card failed\n"); 5758c2ecf20Sopenharmony_ci goto err_disable; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return 0; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cierr_disable: 5818c2ecf20Sopenharmony_ci sdio_claim_host(func); 5828c2ecf20Sopenharmony_ci sdio_disable_func(func); 5838c2ecf20Sopenharmony_ci sdio_release_host(func); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return ret; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/* 5898c2ecf20Sopenharmony_ci * SDIO resume. 5908c2ecf20Sopenharmony_ci * 5918c2ecf20Sopenharmony_ci * Kernel needs to suspend all functions separately. Therefore all 5928c2ecf20Sopenharmony_ci * registered functions must have drivers with suspend and resume 5938c2ecf20Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 5948c2ecf20Sopenharmony_ci * 5958c2ecf20Sopenharmony_ci * If already not resumed, this function turns on the traffic and 5968c2ecf20Sopenharmony_ci * sends a host sleep cancel request to the firmware. 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_cistatic int mwifiex_sdio_resume(struct device *dev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 6018c2ecf20Sopenharmony_ci struct sdio_mmc_card *card; 6028c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 6058c2ecf20Sopenharmony_ci if (!card || !card->adapter) { 6068c2ecf20Sopenharmony_ci dev_err(dev, "resume: invalid card or adapter\n"); 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci adapter = card->adapter; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (!test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 6138c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 6148c2ecf20Sopenharmony_ci "device already resumed\n"); 6158c2ecf20Sopenharmony_ci return 0; 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* Disable Host Sleep */ 6218c2ecf20Sopenharmony_ci mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), 6228c2ecf20Sopenharmony_ci MWIFIEX_SYNC_CMD); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci mwifiex_disable_wake(adapter); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci/* Write data into SDIO card register. Caller claims SDIO device. */ 6308c2ecf20Sopenharmony_cistatic int 6318c2ecf20Sopenharmony_cimwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci int ret = -1; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci sdio_writeb(func, data, reg, &ret); 6368c2ecf20Sopenharmony_ci return ret; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci/* This function writes data into SDIO card register. 6408c2ecf20Sopenharmony_ci */ 6418c2ecf20Sopenharmony_cistatic int 6428c2ecf20Sopenharmony_cimwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 6458c2ecf20Sopenharmony_ci int ret; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 6488c2ecf20Sopenharmony_ci ret = mwifiex_write_reg_locked(card->func, reg, data); 6498c2ecf20Sopenharmony_ci sdio_release_host(card->func); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return ret; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/* This function reads data from SDIO card register. 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_cistatic int 6578c2ecf20Sopenharmony_cimwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 6608c2ecf20Sopenharmony_ci int ret = -1; 6618c2ecf20Sopenharmony_ci u8 val; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 6648c2ecf20Sopenharmony_ci val = sdio_readb(card->func, reg, &ret); 6658c2ecf20Sopenharmony_ci sdio_release_host(card->func); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci *data = val; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return ret; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci/* This function writes multiple data into SDIO card memory. 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * This does not work in suspended mode. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_cistatic int 6778c2ecf20Sopenharmony_cimwifiex_write_data_sync(struct mwifiex_adapter *adapter, 6788c2ecf20Sopenharmony_ci u8 *buffer, u32 pkt_len, u32 port) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 6818c2ecf20Sopenharmony_ci int ret; 6828c2ecf20Sopenharmony_ci u8 blk_mode = 6838c2ecf20Sopenharmony_ci (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; 6848c2ecf20Sopenharmony_ci u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; 6858c2ecf20Sopenharmony_ci u32 blk_cnt = 6868c2ecf20Sopenharmony_ci (blk_mode == 6878c2ecf20Sopenharmony_ci BLOCK_MODE) ? (pkt_len / 6888c2ecf20Sopenharmony_ci MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; 6898c2ecf20Sopenharmony_ci u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (test_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags)) { 6928c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 6938c2ecf20Sopenharmony_ci "%s: not allowed while suspended\n", __func__); 6948c2ecf20Sopenharmony_ci return -1; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci sdio_release_host(card->func); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci return ret; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci/* This function reads multiple data from SDIO card memory. 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_cistatic int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, 7098c2ecf20Sopenharmony_ci u32 len, u32 port, u8 claim) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 7128c2ecf20Sopenharmony_ci int ret; 7138c2ecf20Sopenharmony_ci u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE 7148c2ecf20Sopenharmony_ci : BLOCK_MODE; 7158c2ecf20Sopenharmony_ci u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; 7168c2ecf20Sopenharmony_ci u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) 7178c2ecf20Sopenharmony_ci : len; 7188c2ecf20Sopenharmony_ci u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (claim) 7218c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (claim) 7268c2ecf20Sopenharmony_ci sdio_release_host(card->func); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci return ret; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci/* This function reads the firmware status. 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_cistatic int 7348c2ecf20Sopenharmony_cimwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 7378c2ecf20Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 7388c2ecf20Sopenharmony_ci u8 fws0, fws1; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) 7418c2ecf20Sopenharmony_ci return -1; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) 7448c2ecf20Sopenharmony_ci return -1; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci *dat = (u16)((fws1 << 8) | fws0); 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci/* This function checks the firmware status in card. 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_cistatic int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, 7538c2ecf20Sopenharmony_ci u32 poll_num) 7548c2ecf20Sopenharmony_ci{ 7558c2ecf20Sopenharmony_ci int ret = 0; 7568c2ecf20Sopenharmony_ci u16 firmware_stat; 7578c2ecf20Sopenharmony_ci u32 tries; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci for (tries = 0; tries < poll_num; tries++) { 7608c2ecf20Sopenharmony_ci ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); 7618c2ecf20Sopenharmony_ci if (ret) 7628c2ecf20Sopenharmony_ci continue; 7638c2ecf20Sopenharmony_ci if (firmware_stat == FIRMWARE_READY_SDIO) { 7648c2ecf20Sopenharmony_ci ret = 0; 7658c2ecf20Sopenharmony_ci break; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci msleep(100); 7698c2ecf20Sopenharmony_ci ret = -1; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci return ret; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci/* This function checks if WLAN is the winner. 7768c2ecf20Sopenharmony_ci */ 7778c2ecf20Sopenharmony_cistatic int mwifiex_check_winner_status(struct mwifiex_adapter *adapter) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci int ret = 0; 7808c2ecf20Sopenharmony_ci u8 winner = 0; 7818c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->status_reg_0, &winner)) 7848c2ecf20Sopenharmony_ci return -1; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci if (winner) 7878c2ecf20Sopenharmony_ci adapter->winner = 0; 7888c2ecf20Sopenharmony_ci else 7898c2ecf20Sopenharmony_ci adapter->winner = 1; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci return ret; 7928c2ecf20Sopenharmony_ci} 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci/* 7958c2ecf20Sopenharmony_ci * SDIO remove. 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * This function removes the interface and frees up the card structure. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_cistatic void 8008c2ecf20Sopenharmony_cimwifiex_sdio_remove(struct sdio_func *func) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct sdio_mmc_card *card; 8038c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 8048c2ecf20Sopenharmony_ci struct mwifiex_private *priv; 8058c2ecf20Sopenharmony_ci int ret = 0; 8068c2ecf20Sopenharmony_ci u16 firmware_stat; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 8098c2ecf20Sopenharmony_ci if (!card) 8108c2ecf20Sopenharmony_ci return; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci wait_for_completion(&card->fw_done); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci adapter = card->adapter; 8158c2ecf20Sopenharmony_ci if (!adapter || !adapter->priv_num) 8168c2ecf20Sopenharmony_ci return; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); 8218c2ecf20Sopenharmony_ci if (!ret && firmware_stat == FIRMWARE_READY_SDIO && 8228c2ecf20Sopenharmony_ci !adapter->mfg_mode) { 8238c2ecf20Sopenharmony_ci mwifiex_deauthenticate_all(adapter); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); 8268c2ecf20Sopenharmony_ci mwifiex_disable_auto_ds(priv); 8278c2ecf20Sopenharmony_ci mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci mwifiex_remove_card(adapter); 8318c2ecf20Sopenharmony_ci} 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci/* 8348c2ecf20Sopenharmony_ci * SDIO suspend. 8358c2ecf20Sopenharmony_ci * 8368c2ecf20Sopenharmony_ci * Kernel needs to suspend all functions separately. Therefore all 8378c2ecf20Sopenharmony_ci * registered functions must have drivers with suspend and resume 8388c2ecf20Sopenharmony_ci * methods. Failing that the kernel simply removes the whole card. 8398c2ecf20Sopenharmony_ci * 8408c2ecf20Sopenharmony_ci * If already not suspended, this function allocates and sends a host 8418c2ecf20Sopenharmony_ci * sleep activate request to the firmware and turns off the traffic. 8428c2ecf20Sopenharmony_ci */ 8438c2ecf20Sopenharmony_cistatic int mwifiex_sdio_suspend(struct device *dev) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 8468c2ecf20Sopenharmony_ci struct sdio_mmc_card *card; 8478c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 8488c2ecf20Sopenharmony_ci mmc_pm_flag_t pm_flag = 0; 8498c2ecf20Sopenharmony_ci int ret = 0; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci pm_flag = sdio_get_host_pm_caps(func); 8528c2ecf20Sopenharmony_ci pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", 8538c2ecf20Sopenharmony_ci sdio_func_id(func), pm_flag); 8548c2ecf20Sopenharmony_ci if (!(pm_flag & MMC_PM_KEEP_POWER)) { 8558c2ecf20Sopenharmony_ci dev_err(dev, "%s: cannot remain alive while host is" 8568c2ecf20Sopenharmony_ci " suspended\n", sdio_func_id(func)); 8578c2ecf20Sopenharmony_ci return -ENOSYS; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 8618c2ecf20Sopenharmony_ci if (!card) { 8628c2ecf20Sopenharmony_ci dev_err(dev, "suspend: invalid card\n"); 8638c2ecf20Sopenharmony_ci return 0; 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Might still be loading firmware */ 8678c2ecf20Sopenharmony_ci wait_for_completion(&card->fw_done); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci adapter = card->adapter; 8708c2ecf20Sopenharmony_ci if (!adapter) { 8718c2ecf20Sopenharmony_ci dev_err(dev, "adapter is not valid\n"); 8728c2ecf20Sopenharmony_ci return 0; 8738c2ecf20Sopenharmony_ci } 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (!adapter->is_up) 8768c2ecf20Sopenharmony_ci return -EBUSY; 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci mwifiex_enable_wake(adapter); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* Enable the Host Sleep */ 8818c2ecf20Sopenharmony_ci if (!mwifiex_enable_hs(adapter)) { 8828c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 8838c2ecf20Sopenharmony_ci "cmd: failed to suspend\n"); 8848c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 8858c2ecf20Sopenharmony_ci mwifiex_disable_wake(adapter); 8868c2ecf20Sopenharmony_ci return -EFAULT; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 8908c2ecf20Sopenharmony_ci "cmd: suspend with MMC_PM_KEEP_POWER\n"); 8918c2ecf20Sopenharmony_ci ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci /* Indicate device suspended */ 8948c2ecf20Sopenharmony_ci set_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); 8958c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IS_HS_ENABLING, &adapter->work_flags); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci return ret; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic void mwifiex_sdio_coredump(struct device *dev) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct sdio_func *func = dev_to_sdio_func(dev); 9038c2ecf20Sopenharmony_ci struct sdio_mmc_card *card; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 9068c2ecf20Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 9078c2ecf20Sopenharmony_ci &card->work_flags)) 9088c2ecf20Sopenharmony_ci schedule_work(&card->work); 9098c2ecf20Sopenharmony_ci} 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci/* WLAN IDs */ 9128c2ecf20Sopenharmony_cistatic const struct sdio_device_id mwifiex_ids[] = { 9138c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786_WLAN), 9148c2ecf20Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, 9158c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_WLAN), 9168c2ecf20Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, 9178c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_WLAN), 9188c2ecf20Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, 9198c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_WLAN), 9208c2ecf20Sopenharmony_ci .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, 9218c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_WLAN), 9228c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8887}, 9238c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801_WLAN), 9248c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8801}, 9258c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_WLAN), 9268c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8977}, 9278c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_WLAN), 9288c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8987}, 9298c2ecf20Sopenharmony_ci {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_WLAN), 9308c2ecf20Sopenharmony_ci .driver_data = (unsigned long)&mwifiex_sdio_sd8997}, 9318c2ecf20Sopenharmony_ci {}, 9328c2ecf20Sopenharmony_ci}; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(sdio, mwifiex_ids); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic const struct dev_pm_ops mwifiex_sdio_pm_ops = { 9378c2ecf20Sopenharmony_ci .suspend = mwifiex_sdio_suspend, 9388c2ecf20Sopenharmony_ci .resume = mwifiex_sdio_resume, 9398c2ecf20Sopenharmony_ci}; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic struct sdio_driver mwifiex_sdio = { 9428c2ecf20Sopenharmony_ci .name = "mwifiex_sdio", 9438c2ecf20Sopenharmony_ci .id_table = mwifiex_ids, 9448c2ecf20Sopenharmony_ci .probe = mwifiex_sdio_probe, 9458c2ecf20Sopenharmony_ci .remove = mwifiex_sdio_remove, 9468c2ecf20Sopenharmony_ci .drv = { 9478c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9488c2ecf20Sopenharmony_ci .coredump = mwifiex_sdio_coredump, 9498c2ecf20Sopenharmony_ci .pm = &mwifiex_sdio_pm_ops, 9508c2ecf20Sopenharmony_ci } 9518c2ecf20Sopenharmony_ci}; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci/* 9548c2ecf20Sopenharmony_ci * This function wakes up the card. 9558c2ecf20Sopenharmony_ci * 9568c2ecf20Sopenharmony_ci * A host power up command is written to the card configuration 9578c2ecf20Sopenharmony_ci * register to wake up the card. 9588c2ecf20Sopenharmony_ci */ 9598c2ecf20Sopenharmony_cistatic int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) 9608c2ecf20Sopenharmony_ci{ 9618c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 9628c2ecf20Sopenharmony_ci "event: wakeup device...\n"); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); 9658c2ecf20Sopenharmony_ci} 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci/* 9688c2ecf20Sopenharmony_ci * This function is called after the card has woken up. 9698c2ecf20Sopenharmony_ci * 9708c2ecf20Sopenharmony_ci * The card configuration register is reset. 9718c2ecf20Sopenharmony_ci */ 9728c2ecf20Sopenharmony_cistatic int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 9758c2ecf20Sopenharmony_ci "cmd: wakeup device completed\n"); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); 9788c2ecf20Sopenharmony_ci} 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cistatic int mwifiex_sdio_dnld_fw(struct mwifiex_adapter *adapter, 9818c2ecf20Sopenharmony_ci struct mwifiex_fw_image *fw) 9828c2ecf20Sopenharmony_ci{ 9838c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 9848c2ecf20Sopenharmony_ci int ret; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 9878c2ecf20Sopenharmony_ci ret = mwifiex_dnld_fw(adapter, fw); 9888c2ecf20Sopenharmony_ci sdio_release_host(card->func); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return ret; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/* 9948c2ecf20Sopenharmony_ci * This function is used to initialize IO ports for the 9958c2ecf20Sopenharmony_ci * chipsets supporting SDIO new mode eg SD8897. 9968c2ecf20Sopenharmony_ci */ 9978c2ecf20Sopenharmony_cistatic int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci u8 reg; 10008c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci adapter->ioport = MEM_PORT; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* enable sdio new mode */ 10058c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®)) 10068c2ecf20Sopenharmony_ci return -1; 10078c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg, 10088c2ecf20Sopenharmony_ci reg | CMD53_NEW_MODE)) 10098c2ecf20Sopenharmony_ci return -1; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci /* Configure cmd port and enable reading rx length from the register */ 10128c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, ®)) 10138c2ecf20Sopenharmony_ci return -1; 10148c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0, 10158c2ecf20Sopenharmony_ci reg | CMD_PORT_RD_LEN_EN)) 10168c2ecf20Sopenharmony_ci return -1; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is 10198c2ecf20Sopenharmony_ci * completed 10208c2ecf20Sopenharmony_ci */ 10218c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, ®)) 10228c2ecf20Sopenharmony_ci return -1; 10238c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1, 10248c2ecf20Sopenharmony_ci reg | CMD_PORT_AUTO_EN)) 10258c2ecf20Sopenharmony_ci return -1; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci/* This function initializes the IO ports. 10318c2ecf20Sopenharmony_ci * 10328c2ecf20Sopenharmony_ci * The following operations are performed - 10338c2ecf20Sopenharmony_ci * - Read the IO ports (0, 1 and 2) 10348c2ecf20Sopenharmony_ci * - Set host interrupt Reset-To-Read to clear 10358c2ecf20Sopenharmony_ci * - Set auto re-enable interrupt 10368c2ecf20Sopenharmony_ci */ 10378c2ecf20Sopenharmony_cistatic int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci u8 reg; 10408c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci adapter->ioport = 0; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (card->supports_sdio_new_mode) { 10458c2ecf20Sopenharmony_ci if (mwifiex_init_sdio_new_mode(adapter)) 10468c2ecf20Sopenharmony_ci return -1; 10478c2ecf20Sopenharmony_ci goto cont; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci /* Read the IO port */ 10518c2ecf20Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, ®)) 10528c2ecf20Sopenharmony_ci adapter->ioport |= (reg & 0xff); 10538c2ecf20Sopenharmony_ci else 10548c2ecf20Sopenharmony_ci return -1; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, ®)) 10578c2ecf20Sopenharmony_ci adapter->ioport |= ((reg & 0xff) << 8); 10588c2ecf20Sopenharmony_ci else 10598c2ecf20Sopenharmony_ci return -1; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, ®)) 10628c2ecf20Sopenharmony_ci adapter->ioport |= ((reg & 0xff) << 16); 10638c2ecf20Sopenharmony_ci else 10648c2ecf20Sopenharmony_ci return -1; 10658c2ecf20Sopenharmony_cicont: 10668c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 10678c2ecf20Sopenharmony_ci "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci /* Set Host interrupt reset to read to clear */ 10708c2ecf20Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, ®)) 10718c2ecf20Sopenharmony_ci mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg, 10728c2ecf20Sopenharmony_ci reg | card->reg->sdio_int_mask); 10738c2ecf20Sopenharmony_ci else 10748c2ecf20Sopenharmony_ci return -1; 10758c2ecf20Sopenharmony_ci 10768c2ecf20Sopenharmony_ci /* Dnld/Upld ready set to auto reset */ 10778c2ecf20Sopenharmony_ci if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) 10788c2ecf20Sopenharmony_ci mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, 10798c2ecf20Sopenharmony_ci reg | AUTO_RE_ENABLE_INT); 10808c2ecf20Sopenharmony_ci else 10818c2ecf20Sopenharmony_ci return -1; 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci return 0; 10848c2ecf20Sopenharmony_ci} 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci/* 10878c2ecf20Sopenharmony_ci * This function sends data to the card. 10888c2ecf20Sopenharmony_ci */ 10898c2ecf20Sopenharmony_cistatic int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, 10908c2ecf20Sopenharmony_ci u8 *payload, u32 pkt_len, u32 port) 10918c2ecf20Sopenharmony_ci{ 10928c2ecf20Sopenharmony_ci u32 i = 0; 10938c2ecf20Sopenharmony_ci int ret; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci do { 10968c2ecf20Sopenharmony_ci ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); 10978c2ecf20Sopenharmony_ci if (ret) { 10988c2ecf20Sopenharmony_ci i++; 10998c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 11008c2ecf20Sopenharmony_ci "host_to_card, write iomem\t" 11018c2ecf20Sopenharmony_ci "(%d) failed: %d\n", i, ret); 11028c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) 11038c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 11048c2ecf20Sopenharmony_ci "write CFG reg failed\n"); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci ret = -1; 11078c2ecf20Sopenharmony_ci if (i > MAX_WRITE_IOMEM_RETRY) 11088c2ecf20Sopenharmony_ci return ret; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci } while (ret == -1); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return ret; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci/* 11168c2ecf20Sopenharmony_ci * This function gets the read port. 11178c2ecf20Sopenharmony_ci * 11188c2ecf20Sopenharmony_ci * If control port bit is set in MP read bitmap, the control port 11198c2ecf20Sopenharmony_ci * is returned, otherwise the current read port is returned and 11208c2ecf20Sopenharmony_ci * the value is increased (provided it does not reach the maximum 11218c2ecf20Sopenharmony_ci * limit, in which case it is reset to 1) 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_cistatic int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) 11248c2ecf20Sopenharmony_ci{ 11258c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 11268c2ecf20Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 11278c2ecf20Sopenharmony_ci u32 rd_bitmap = card->mp_rd_bitmap; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 11308c2ecf20Sopenharmony_ci "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (card->supports_sdio_new_mode) { 11338c2ecf20Sopenharmony_ci if (!(rd_bitmap & reg->data_port_mask)) 11348c2ecf20Sopenharmony_ci return -1; 11358c2ecf20Sopenharmony_ci } else { 11368c2ecf20Sopenharmony_ci if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) 11378c2ecf20Sopenharmony_ci return -1; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if ((card->has_control_mask) && 11418c2ecf20Sopenharmony_ci (card->mp_rd_bitmap & CTRL_PORT_MASK)) { 11428c2ecf20Sopenharmony_ci card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); 11438c2ecf20Sopenharmony_ci *port = CTRL_PORT; 11448c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 11458c2ecf20Sopenharmony_ci "data: port=%d mp_rd_bitmap=0x%08x\n", 11468c2ecf20Sopenharmony_ci *port, card->mp_rd_bitmap); 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci } 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) 11518c2ecf20Sopenharmony_ci return -1; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci /* We are now handling the SDIO data ports */ 11548c2ecf20Sopenharmony_ci card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); 11558c2ecf20Sopenharmony_ci *port = card->curr_rd_port; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (++card->curr_rd_port == card->max_ports) 11588c2ecf20Sopenharmony_ci card->curr_rd_port = reg->start_rd_port; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 11618c2ecf20Sopenharmony_ci "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", 11628c2ecf20Sopenharmony_ci *port, rd_bitmap, card->mp_rd_bitmap); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci return 0; 11658c2ecf20Sopenharmony_ci} 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci/* 11688c2ecf20Sopenharmony_ci * This function gets the write port for data. 11698c2ecf20Sopenharmony_ci * 11708c2ecf20Sopenharmony_ci * The current write port is returned if available and the value is 11718c2ecf20Sopenharmony_ci * increased (provided it does not reach the maximum limit, in which 11728c2ecf20Sopenharmony_ci * case it is reset to 1) 11738c2ecf20Sopenharmony_ci */ 11748c2ecf20Sopenharmony_cistatic int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 11778c2ecf20Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 11788c2ecf20Sopenharmony_ci u32 wr_bitmap = card->mp_wr_bitmap; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 11818c2ecf20Sopenharmony_ci "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (!(wr_bitmap & card->mp_data_port_mask)) { 11848c2ecf20Sopenharmony_ci adapter->data_sent = true; 11858c2ecf20Sopenharmony_ci return -EBUSY; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { 11898c2ecf20Sopenharmony_ci card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); 11908c2ecf20Sopenharmony_ci *port = card->curr_wr_port; 11918c2ecf20Sopenharmony_ci if (++card->curr_wr_port == card->mp_end_port) 11928c2ecf20Sopenharmony_ci card->curr_wr_port = reg->start_wr_port; 11938c2ecf20Sopenharmony_ci } else { 11948c2ecf20Sopenharmony_ci adapter->data_sent = true; 11958c2ecf20Sopenharmony_ci return -EBUSY; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci if ((card->has_control_mask) && (*port == CTRL_PORT)) { 11998c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 12008c2ecf20Sopenharmony_ci "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", 12018c2ecf20Sopenharmony_ci *port, card->curr_wr_port, wr_bitmap, 12028c2ecf20Sopenharmony_ci card->mp_wr_bitmap); 12038c2ecf20Sopenharmony_ci return -1; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 12078c2ecf20Sopenharmony_ci "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", 12088c2ecf20Sopenharmony_ci *port, wr_bitmap, card->mp_wr_bitmap); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci return 0; 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci/* 12148c2ecf20Sopenharmony_ci * This function polls the card status. 12158c2ecf20Sopenharmony_ci */ 12168c2ecf20Sopenharmony_cistatic int 12178c2ecf20Sopenharmony_cimwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 12208c2ecf20Sopenharmony_ci u32 tries; 12218c2ecf20Sopenharmony_ci u8 cs; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 12248c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) 12258c2ecf20Sopenharmony_ci break; 12268c2ecf20Sopenharmony_ci else if ((cs & bits) == bits) 12278c2ecf20Sopenharmony_ci return 0; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci usleep_range(10, 20); 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 12338c2ecf20Sopenharmony_ci "poll card status failed, tries = %d\n", tries); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci return -1; 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci/* 12398c2ecf20Sopenharmony_ci * This function disables the host interrupt. 12408c2ecf20Sopenharmony_ci * 12418c2ecf20Sopenharmony_ci * The host interrupt mask is read, the disable bit is reset and 12428c2ecf20Sopenharmony_ci * written back to the card host interrupt mask register. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_cistatic void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) 12458c2ecf20Sopenharmony_ci{ 12468c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 12478c2ecf20Sopenharmony_ci struct sdio_func *func = card->func; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci sdio_claim_host(func); 12508c2ecf20Sopenharmony_ci mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0); 12518c2ecf20Sopenharmony_ci sdio_release_irq(func); 12528c2ecf20Sopenharmony_ci sdio_release_host(func); 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci/* 12568c2ecf20Sopenharmony_ci * This function reads the interrupt status from card. 12578c2ecf20Sopenharmony_ci */ 12588c2ecf20Sopenharmony_cistatic void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 12618c2ecf20Sopenharmony_ci u8 sdio_ireg; 12628c2ecf20Sopenharmony_ci unsigned long flags; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci if (mwifiex_read_data_sync(adapter, card->mp_regs, 12658c2ecf20Sopenharmony_ci card->reg->max_mp_regs, 12668c2ecf20Sopenharmony_ci REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { 12678c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n"); 12688c2ecf20Sopenharmony_ci return; 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci sdio_ireg = card->mp_regs[card->reg->host_int_status_reg]; 12728c2ecf20Sopenharmony_ci if (sdio_ireg) { 12738c2ecf20Sopenharmony_ci /* 12748c2ecf20Sopenharmony_ci * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS 12758c2ecf20Sopenharmony_ci * For SDIO new mode CMD port interrupts 12768c2ecf20Sopenharmony_ci * DN_LD_CMD_PORT_HOST_INT_STATUS and/or 12778c2ecf20Sopenharmony_ci * UP_LD_CMD_PORT_HOST_INT_STATUS 12788c2ecf20Sopenharmony_ci * Clear the interrupt status register 12798c2ecf20Sopenharmony_ci */ 12808c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, 12818c2ecf20Sopenharmony_ci "int: sdio_ireg = %#x\n", sdio_ireg); 12828c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->int_lock, flags); 12838c2ecf20Sopenharmony_ci adapter->int_status |= sdio_ireg; 12848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->int_lock, flags); 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci/* 12898c2ecf20Sopenharmony_ci * SDIO interrupt handler. 12908c2ecf20Sopenharmony_ci * 12918c2ecf20Sopenharmony_ci * This function reads the interrupt status from firmware and handles 12928c2ecf20Sopenharmony_ci * the interrupt in current thread (ksdioirqd) right away. 12938c2ecf20Sopenharmony_ci */ 12948c2ecf20Sopenharmony_cistatic void 12958c2ecf20Sopenharmony_cimwifiex_sdio_interrupt(struct sdio_func *func) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci struct mwifiex_adapter *adapter; 12988c2ecf20Sopenharmony_ci struct sdio_mmc_card *card; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci card = sdio_get_drvdata(func); 13018c2ecf20Sopenharmony_ci if (!card || !card->adapter) { 13028c2ecf20Sopenharmony_ci pr_err("int: func=%p card=%p adapter=%p\n", 13038c2ecf20Sopenharmony_ci func, card, card ? card->adapter : NULL); 13048c2ecf20Sopenharmony_ci return; 13058c2ecf20Sopenharmony_ci } 13068c2ecf20Sopenharmony_ci adapter = card->adapter; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) 13098c2ecf20Sopenharmony_ci adapter->ps_state = PS_STATE_AWAKE; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci mwifiex_interrupt_status(adapter); 13128c2ecf20Sopenharmony_ci mwifiex_main_process(adapter); 13138c2ecf20Sopenharmony_ci} 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci/* 13168c2ecf20Sopenharmony_ci * This function enables the host interrupt. 13178c2ecf20Sopenharmony_ci * 13188c2ecf20Sopenharmony_ci * The host interrupt enable mask is written to the card 13198c2ecf20Sopenharmony_ci * host interrupt mask register. 13208c2ecf20Sopenharmony_ci */ 13218c2ecf20Sopenharmony_cistatic int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) 13228c2ecf20Sopenharmony_ci{ 13238c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 13248c2ecf20Sopenharmony_ci struct sdio_func *func = card->func; 13258c2ecf20Sopenharmony_ci int ret; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci sdio_claim_host(func); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci /* Request the SDIO IRQ */ 13308c2ecf20Sopenharmony_ci ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); 13318c2ecf20Sopenharmony_ci if (ret) { 13328c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 13338c2ecf20Sopenharmony_ci "claim irq failed: ret=%d\n", ret); 13348c2ecf20Sopenharmony_ci goto out; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci /* Simply write the mask to the register */ 13388c2ecf20Sopenharmony_ci ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 13398c2ecf20Sopenharmony_ci card->reg->host_int_enable); 13408c2ecf20Sopenharmony_ci if (ret) { 13418c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 13428c2ecf20Sopenharmony_ci "enable host interrupt failed\n"); 13438c2ecf20Sopenharmony_ci sdio_release_irq(func); 13448c2ecf20Sopenharmony_ci } 13458c2ecf20Sopenharmony_ci 13468c2ecf20Sopenharmony_ciout: 13478c2ecf20Sopenharmony_ci sdio_release_host(func); 13488c2ecf20Sopenharmony_ci return ret; 13498c2ecf20Sopenharmony_ci} 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci/* 13528c2ecf20Sopenharmony_ci * This function sends a data buffer to the card. 13538c2ecf20Sopenharmony_ci */ 13548c2ecf20Sopenharmony_cistatic int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, 13558c2ecf20Sopenharmony_ci u32 *type, u8 *buffer, 13568c2ecf20Sopenharmony_ci u32 npayload, u32 ioport) 13578c2ecf20Sopenharmony_ci{ 13588c2ecf20Sopenharmony_ci int ret; 13598c2ecf20Sopenharmony_ci u32 nb; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci if (!buffer) { 13628c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 13638c2ecf20Sopenharmony_ci "%s: buffer is NULL\n", __func__); 13648c2ecf20Sopenharmony_ci return -1; 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci if (ret) { 13708c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 13718c2ecf20Sopenharmony_ci "%s: read iomem failed: %d\n", __func__, 13728c2ecf20Sopenharmony_ci ret); 13738c2ecf20Sopenharmony_ci return -1; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci nb = get_unaligned_le16((buffer)); 13778c2ecf20Sopenharmony_ci if (nb > npayload) { 13788c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 13798c2ecf20Sopenharmony_ci "%s: invalid packet, nb=%d npayload=%d\n", 13808c2ecf20Sopenharmony_ci __func__, nb, npayload); 13818c2ecf20Sopenharmony_ci return -1; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci *type = get_unaligned_le16((buffer + 2)); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci return ret; 13878c2ecf20Sopenharmony_ci} 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci/* 13908c2ecf20Sopenharmony_ci * This function downloads the firmware to the card. 13918c2ecf20Sopenharmony_ci * 13928c2ecf20Sopenharmony_ci * Firmware is downloaded to the card in blocks. Every block download 13938c2ecf20Sopenharmony_ci * is tested for CRC errors, and retried a number of times before 13948c2ecf20Sopenharmony_ci * returning failure. 13958c2ecf20Sopenharmony_ci */ 13968c2ecf20Sopenharmony_cistatic int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, 13978c2ecf20Sopenharmony_ci struct mwifiex_fw_image *fw) 13988c2ecf20Sopenharmony_ci{ 13998c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 14008c2ecf20Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 14018c2ecf20Sopenharmony_ci int ret; 14028c2ecf20Sopenharmony_ci u8 *firmware = fw->fw_buf; 14038c2ecf20Sopenharmony_ci u32 firmware_len = fw->fw_len; 14048c2ecf20Sopenharmony_ci u32 offset = 0; 14058c2ecf20Sopenharmony_ci u8 base0, base1; 14068c2ecf20Sopenharmony_ci u8 *fwbuf; 14078c2ecf20Sopenharmony_ci u16 len = 0; 14088c2ecf20Sopenharmony_ci u32 txlen, tx_blocks = 0, tries; 14098c2ecf20Sopenharmony_ci u32 i = 0; 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci if (!firmware_len) { 14128c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14138c2ecf20Sopenharmony_ci "firmware image not found! Terminating download\n"); 14148c2ecf20Sopenharmony_ci return -1; 14158c2ecf20Sopenharmony_ci } 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 14188c2ecf20Sopenharmony_ci "info: downloading FW image (%d bytes)\n", 14198c2ecf20Sopenharmony_ci firmware_len); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci /* Assume that the allocated buffer is 8-byte aligned */ 14228c2ecf20Sopenharmony_ci fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); 14238c2ecf20Sopenharmony_ci if (!fwbuf) 14248c2ecf20Sopenharmony_ci return -ENOMEM; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci /* Perform firmware data transfer */ 14298c2ecf20Sopenharmony_ci do { 14308c2ecf20Sopenharmony_ci /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY 14318c2ecf20Sopenharmony_ci bits */ 14328c2ecf20Sopenharmony_ci ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | 14338c2ecf20Sopenharmony_ci DN_LD_CARD_RDY); 14348c2ecf20Sopenharmony_ci if (ret) { 14358c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14368c2ecf20Sopenharmony_ci "FW download with helper:\t" 14378c2ecf20Sopenharmony_ci "poll status timeout @ %d\n", offset); 14388c2ecf20Sopenharmony_ci goto done; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* More data? */ 14428c2ecf20Sopenharmony_ci if (offset >= firmware_len) 14438c2ecf20Sopenharmony_ci break; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 14468c2ecf20Sopenharmony_ci ret = mwifiex_read_reg(adapter, reg->base_0_reg, 14478c2ecf20Sopenharmony_ci &base0); 14488c2ecf20Sopenharmony_ci if (ret) { 14498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14508c2ecf20Sopenharmony_ci "dev BASE0 register read failed:\t" 14518c2ecf20Sopenharmony_ci "base0=%#04X(%d). Terminating dnld\n", 14528c2ecf20Sopenharmony_ci base0, base0); 14538c2ecf20Sopenharmony_ci goto done; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci ret = mwifiex_read_reg(adapter, reg->base_1_reg, 14568c2ecf20Sopenharmony_ci &base1); 14578c2ecf20Sopenharmony_ci if (ret) { 14588c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14598c2ecf20Sopenharmony_ci "dev BASE1 register read failed:\t" 14608c2ecf20Sopenharmony_ci "base1=%#04X(%d). Terminating dnld\n", 14618c2ecf20Sopenharmony_ci base1, base1); 14628c2ecf20Sopenharmony_ci goto done; 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (len) 14678c2ecf20Sopenharmony_ci break; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci usleep_range(10, 20); 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci if (!len) { 14738c2ecf20Sopenharmony_ci break; 14748c2ecf20Sopenharmony_ci } else if (len > MWIFIEX_UPLD_SIZE) { 14758c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14768c2ecf20Sopenharmony_ci "FW dnld failed @ %d, invalid length %d\n", 14778c2ecf20Sopenharmony_ci offset, len); 14788c2ecf20Sopenharmony_ci ret = -1; 14798c2ecf20Sopenharmony_ci goto done; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci txlen = len; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci if (len & BIT(0)) { 14858c2ecf20Sopenharmony_ci i++; 14868c2ecf20Sopenharmony_ci if (i > MAX_WRITE_IOMEM_RETRY) { 14878c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14888c2ecf20Sopenharmony_ci "FW dnld failed @ %d, over max retry\n", 14898c2ecf20Sopenharmony_ci offset); 14908c2ecf20Sopenharmony_ci ret = -1; 14918c2ecf20Sopenharmony_ci goto done; 14928c2ecf20Sopenharmony_ci } 14938c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 14948c2ecf20Sopenharmony_ci "CRC indicated by the helper:\t" 14958c2ecf20Sopenharmony_ci "len = 0x%04X, txlen = %d\n", len, txlen); 14968c2ecf20Sopenharmony_ci len &= ~BIT(0); 14978c2ecf20Sopenharmony_ci /* Setting this to 0 to resend from same offset */ 14988c2ecf20Sopenharmony_ci txlen = 0; 14998c2ecf20Sopenharmony_ci } else { 15008c2ecf20Sopenharmony_ci i = 0; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci /* Set blocksize to transfer - checking for last 15038c2ecf20Sopenharmony_ci block */ 15048c2ecf20Sopenharmony_ci if (firmware_len - offset < txlen) 15058c2ecf20Sopenharmony_ci txlen = firmware_len - offset; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1) 15088c2ecf20Sopenharmony_ci / MWIFIEX_SDIO_BLOCK_SIZE; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci /* Copy payload to buffer */ 15118c2ecf20Sopenharmony_ci memmove(fwbuf, &firmware[offset], txlen); 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * 15158c2ecf20Sopenharmony_ci MWIFIEX_SDIO_BLOCK_SIZE, 15168c2ecf20Sopenharmony_ci adapter->ioport); 15178c2ecf20Sopenharmony_ci if (ret) { 15188c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15198c2ecf20Sopenharmony_ci "FW download, write iomem (%d) failed @ %d\n", 15208c2ecf20Sopenharmony_ci i, offset); 15218c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) 15228c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15238c2ecf20Sopenharmony_ci "write CFG reg failed\n"); 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci ret = -1; 15268c2ecf20Sopenharmony_ci goto done; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci offset += txlen; 15308c2ecf20Sopenharmony_ci } while (true); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, 15338c2ecf20Sopenharmony_ci "info: FW download over, size %d bytes\n", offset); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci ret = 0; 15368c2ecf20Sopenharmony_cidone: 15378c2ecf20Sopenharmony_ci sdio_release_host(card->func); 15388c2ecf20Sopenharmony_ci kfree(fwbuf); 15398c2ecf20Sopenharmony_ci return ret; 15408c2ecf20Sopenharmony_ci} 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci/* 15438c2ecf20Sopenharmony_ci * This function decode sdio aggreation pkt. 15448c2ecf20Sopenharmony_ci * 15458c2ecf20Sopenharmony_ci * Based on the the data block size and pkt_len, 15468c2ecf20Sopenharmony_ci * skb data will be decoded to few packets. 15478c2ecf20Sopenharmony_ci */ 15488c2ecf20Sopenharmony_cistatic void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, 15498c2ecf20Sopenharmony_ci struct sk_buff *skb) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci u32 total_pkt_len, pkt_len; 15528c2ecf20Sopenharmony_ci struct sk_buff *skb_deaggr; 15538c2ecf20Sopenharmony_ci u16 blk_size; 15548c2ecf20Sopenharmony_ci u8 blk_num; 15558c2ecf20Sopenharmony_ci u8 *data; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci data = skb->data; 15588c2ecf20Sopenharmony_ci total_pkt_len = skb->len; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci while (total_pkt_len >= (SDIO_HEADER_OFFSET + adapter->intf_hdr_len)) { 15618c2ecf20Sopenharmony_ci if (total_pkt_len < adapter->sdio_rx_block_size) 15628c2ecf20Sopenharmony_ci break; 15638c2ecf20Sopenharmony_ci blk_num = *(data + BLOCK_NUMBER_OFFSET); 15648c2ecf20Sopenharmony_ci blk_size = adapter->sdio_rx_block_size * blk_num; 15658c2ecf20Sopenharmony_ci if (blk_size > total_pkt_len) { 15668c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15678c2ecf20Sopenharmony_ci "%s: error in blk_size,\t" 15688c2ecf20Sopenharmony_ci "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", 15698c2ecf20Sopenharmony_ci __func__, blk_num, blk_size, total_pkt_len); 15708c2ecf20Sopenharmony_ci break; 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci pkt_len = get_unaligned_le16((data + 15738c2ecf20Sopenharmony_ci SDIO_HEADER_OFFSET)); 15748c2ecf20Sopenharmony_ci if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { 15758c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 15768c2ecf20Sopenharmony_ci "%s: error in pkt_len,\t" 15778c2ecf20Sopenharmony_ci "pkt_len=%d, blk_size=%d\n", 15788c2ecf20Sopenharmony_ci __func__, pkt_len, blk_size); 15798c2ecf20Sopenharmony_ci break; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci 15828c2ecf20Sopenharmony_ci skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len, GFP_KERNEL); 15838c2ecf20Sopenharmony_ci if (!skb_deaggr) 15848c2ecf20Sopenharmony_ci break; 15858c2ecf20Sopenharmony_ci skb_put(skb_deaggr, pkt_len); 15868c2ecf20Sopenharmony_ci memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); 15878c2ecf20Sopenharmony_ci skb_pull(skb_deaggr, adapter->intf_hdr_len); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci mwifiex_handle_rx_packet(adapter, skb_deaggr); 15908c2ecf20Sopenharmony_ci data += blk_size; 15918c2ecf20Sopenharmony_ci total_pkt_len -= blk_size; 15928c2ecf20Sopenharmony_ci } 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci/* 15968c2ecf20Sopenharmony_ci * This function decodes a received packet. 15978c2ecf20Sopenharmony_ci * 15988c2ecf20Sopenharmony_ci * Based on the type, the packet is treated as either a data, or 15998c2ecf20Sopenharmony_ci * a command response, or an event, and the correct handler 16008c2ecf20Sopenharmony_ci * function is invoked. 16018c2ecf20Sopenharmony_ci */ 16028c2ecf20Sopenharmony_cistatic int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, 16038c2ecf20Sopenharmony_ci struct sk_buff *skb, u32 upld_typ) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci u8 *cmd_buf; 16068c2ecf20Sopenharmony_ci u16 pkt_len; 16078c2ecf20Sopenharmony_ci struct mwifiex_rxinfo *rx_info; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci pkt_len = get_unaligned_le16(skb->data); 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { 16128c2ecf20Sopenharmony_ci skb_trim(skb, pkt_len); 16138c2ecf20Sopenharmony_ci skb_pull(skb, adapter->intf_hdr_len); 16148c2ecf20Sopenharmony_ci } 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci switch (upld_typ) { 16178c2ecf20Sopenharmony_ci case MWIFIEX_TYPE_AGGR_DATA: 16188c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 16198c2ecf20Sopenharmony_ci "info: --- Rx: Aggr Data packet ---\n"); 16208c2ecf20Sopenharmony_ci rx_info = MWIFIEX_SKB_RXCB(skb); 16218c2ecf20Sopenharmony_ci rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA; 16228c2ecf20Sopenharmony_ci if (adapter->rx_work_enabled) { 16238c2ecf20Sopenharmony_ci skb_queue_tail(&adapter->rx_data_q, skb); 16248c2ecf20Sopenharmony_ci atomic_inc(&adapter->rx_pending); 16258c2ecf20Sopenharmony_ci adapter->data_received = true; 16268c2ecf20Sopenharmony_ci } else { 16278c2ecf20Sopenharmony_ci mwifiex_deaggr_sdio_pkt(adapter, skb); 16288c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci break; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci case MWIFIEX_TYPE_DATA: 16338c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 16348c2ecf20Sopenharmony_ci "info: --- Rx: Data packet ---\n"); 16358c2ecf20Sopenharmony_ci if (adapter->rx_work_enabled) { 16368c2ecf20Sopenharmony_ci skb_queue_tail(&adapter->rx_data_q, skb); 16378c2ecf20Sopenharmony_ci adapter->data_received = true; 16388c2ecf20Sopenharmony_ci atomic_inc(&adapter->rx_pending); 16398c2ecf20Sopenharmony_ci } else { 16408c2ecf20Sopenharmony_ci mwifiex_handle_rx_packet(adapter, skb); 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci break; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci case MWIFIEX_TYPE_CMD: 16458c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, CMD, 16468c2ecf20Sopenharmony_ci "info: --- Rx: Cmd Response ---\n"); 16478c2ecf20Sopenharmony_ci /* take care of curr_cmd = NULL case */ 16488c2ecf20Sopenharmony_ci if (!adapter->curr_cmd) { 16498c2ecf20Sopenharmony_ci cmd_buf = adapter->upld_buf; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (adapter->ps_state == PS_STATE_SLEEP_CFM) 16528c2ecf20Sopenharmony_ci mwifiex_process_sleep_confirm_resp(adapter, 16538c2ecf20Sopenharmony_ci skb->data, 16548c2ecf20Sopenharmony_ci skb->len); 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci memcpy(cmd_buf, skb->data, 16578c2ecf20Sopenharmony_ci min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, 16588c2ecf20Sopenharmony_ci skb->len)); 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 16618c2ecf20Sopenharmony_ci } else { 16628c2ecf20Sopenharmony_ci adapter->cmd_resp_received = true; 16638c2ecf20Sopenharmony_ci adapter->curr_cmd->resp_skb = skb; 16648c2ecf20Sopenharmony_ci } 16658c2ecf20Sopenharmony_ci break; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci case MWIFIEX_TYPE_EVENT: 16688c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, EVENT, 16698c2ecf20Sopenharmony_ci "info: --- Rx: Event ---\n"); 16708c2ecf20Sopenharmony_ci adapter->event_cause = get_unaligned_le32(skb->data); 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) 16738c2ecf20Sopenharmony_ci memcpy(adapter->event_body, 16748c2ecf20Sopenharmony_ci skb->data + MWIFIEX_EVENT_HEADER_LEN, 16758c2ecf20Sopenharmony_ci skb->len); 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_ci /* event cause has been saved to adapter->event_cause */ 16788c2ecf20Sopenharmony_ci adapter->event_received = true; 16798c2ecf20Sopenharmony_ci adapter->event_skb = skb; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci break; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci default: 16848c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 16858c2ecf20Sopenharmony_ci "unknown upload type %#x\n", upld_typ); 16868c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 16878c2ecf20Sopenharmony_ci break; 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci return 0; 16918c2ecf20Sopenharmony_ci} 16928c2ecf20Sopenharmony_ci 16938c2ecf20Sopenharmony_ci/* 16948c2ecf20Sopenharmony_ci * This function transfers received packets from card to driver, performing 16958c2ecf20Sopenharmony_ci * aggregation if required. 16968c2ecf20Sopenharmony_ci * 16978c2ecf20Sopenharmony_ci * For data received on control port, or if aggregation is disabled, the 16988c2ecf20Sopenharmony_ci * received buffers are uploaded as separate packets. However, if aggregation 16998c2ecf20Sopenharmony_ci * is enabled and required, the buffers are copied onto an aggregation buffer, 17008c2ecf20Sopenharmony_ci * provided there is space left, processed and finally uploaded. 17018c2ecf20Sopenharmony_ci */ 17028c2ecf20Sopenharmony_cistatic int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, 17038c2ecf20Sopenharmony_ci u16 rx_len, u8 port) 17048c2ecf20Sopenharmony_ci{ 17058c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 17068c2ecf20Sopenharmony_ci s32 f_do_rx_aggr = 0; 17078c2ecf20Sopenharmony_ci s32 f_do_rx_cur = 0; 17088c2ecf20Sopenharmony_ci s32 f_aggr_cur = 0; 17098c2ecf20Sopenharmony_ci s32 f_post_aggr_cur = 0; 17108c2ecf20Sopenharmony_ci struct sk_buff *skb_deaggr; 17118c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 17128c2ecf20Sopenharmony_ci u32 pkt_len, pkt_type, mport, pind; 17138c2ecf20Sopenharmony_ci u8 *curr_ptr; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci if ((card->has_control_mask) && (port == CTRL_PORT)) { 17168c2ecf20Sopenharmony_ci /* Read the command Resp without aggr */ 17178c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, CMD, 17188c2ecf20Sopenharmony_ci "info: %s: no aggregation for cmd\t" 17198c2ecf20Sopenharmony_ci "response\n", __func__); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci f_do_rx_cur = 1; 17228c2ecf20Sopenharmony_ci goto rx_curr_single; 17238c2ecf20Sopenharmony_ci } 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (!card->mpa_rx.enabled) { 17268c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 17278c2ecf20Sopenharmony_ci "info: %s: rx aggregation disabled\n", 17288c2ecf20Sopenharmony_ci __func__); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci f_do_rx_cur = 1; 17318c2ecf20Sopenharmony_ci goto rx_curr_single; 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci if ((!card->has_control_mask && (card->mp_rd_bitmap & 17358c2ecf20Sopenharmony_ci card->reg->data_port_mask)) || 17368c2ecf20Sopenharmony_ci (card->has_control_mask && (card->mp_rd_bitmap & 17378c2ecf20Sopenharmony_ci (~((u32) CTRL_PORT_MASK))))) { 17388c2ecf20Sopenharmony_ci /* Some more data RX pending */ 17398c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 17408c2ecf20Sopenharmony_ci "info: %s: not last packet\n", __func__); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci if (MP_RX_AGGR_IN_PROGRESS(card)) { 17438c2ecf20Sopenharmony_ci if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) { 17448c2ecf20Sopenharmony_ci f_aggr_cur = 1; 17458c2ecf20Sopenharmony_ci } else { 17468c2ecf20Sopenharmony_ci /* No room in Aggr buf, do rx aggr now */ 17478c2ecf20Sopenharmony_ci f_do_rx_aggr = 1; 17488c2ecf20Sopenharmony_ci f_post_aggr_cur = 1; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci } else { 17518c2ecf20Sopenharmony_ci /* Rx aggr not in progress */ 17528c2ecf20Sopenharmony_ci f_aggr_cur = 1; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci } else { 17568c2ecf20Sopenharmony_ci /* No more data RX pending */ 17578c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 17588c2ecf20Sopenharmony_ci "info: %s: last packet\n", __func__); 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if (MP_RX_AGGR_IN_PROGRESS(card)) { 17618c2ecf20Sopenharmony_ci f_do_rx_aggr = 1; 17628c2ecf20Sopenharmony_ci if (MP_RX_AGGR_BUF_HAS_ROOM(card, rx_len)) 17638c2ecf20Sopenharmony_ci f_aggr_cur = 1; 17648c2ecf20Sopenharmony_ci else 17658c2ecf20Sopenharmony_ci /* No room in Aggr buf, do rx aggr now */ 17668c2ecf20Sopenharmony_ci f_do_rx_cur = 1; 17678c2ecf20Sopenharmony_ci } else { 17688c2ecf20Sopenharmony_ci f_do_rx_cur = 1; 17698c2ecf20Sopenharmony_ci } 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (f_aggr_cur) { 17738c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 17748c2ecf20Sopenharmony_ci "info: current packet aggregation\n"); 17758c2ecf20Sopenharmony_ci /* Curr pkt can be aggregated */ 17768c2ecf20Sopenharmony_ci mp_rx_aggr_setup(card, rx_len, port); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || 17798c2ecf20Sopenharmony_ci mp_rx_aggr_port_limit_reached(card)) { 17808c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 17818c2ecf20Sopenharmony_ci "info: %s: aggregated packet\t" 17828c2ecf20Sopenharmony_ci "limit reached\n", __func__); 17838c2ecf20Sopenharmony_ci /* No more pkts allowed in Aggr buf, rx it */ 17848c2ecf20Sopenharmony_ci f_do_rx_aggr = 1; 17858c2ecf20Sopenharmony_ci } 17868c2ecf20Sopenharmony_ci } 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci if (f_do_rx_aggr) { 17898c2ecf20Sopenharmony_ci /* do aggr RX now */ 17908c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 17918c2ecf20Sopenharmony_ci "info: do_rx_aggr: num of packets: %d\n", 17928c2ecf20Sopenharmony_ci card->mpa_rx.pkt_cnt); 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci if (card->supports_sdio_new_mode) { 17958c2ecf20Sopenharmony_ci int i; 17968c2ecf20Sopenharmony_ci u32 port_count; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci for (i = 0, port_count = 0; i < card->max_ports; i++) 17998c2ecf20Sopenharmony_ci if (card->mpa_rx.ports & BIT(i)) 18008c2ecf20Sopenharmony_ci port_count++; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci /* Reading data from "start_port + 0" to "start_port + 18038c2ecf20Sopenharmony_ci * port_count -1", so decrease the count by 1 18048c2ecf20Sopenharmony_ci */ 18058c2ecf20Sopenharmony_ci port_count--; 18068c2ecf20Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 18078c2ecf20Sopenharmony_ci (port_count << 8)) + card->mpa_rx.start_port; 18088c2ecf20Sopenharmony_ci } else { 18098c2ecf20Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 18108c2ecf20Sopenharmony_ci (card->mpa_rx.ports << 4)) + 18118c2ecf20Sopenharmony_ci card->mpa_rx.start_port; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci if (card->mpa_rx.pkt_cnt == 1) 18158c2ecf20Sopenharmony_ci mport = adapter->ioport + card->mpa_rx.start_port; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, 18188c2ecf20Sopenharmony_ci card->mpa_rx.buf_len, mport, 1)) 18198c2ecf20Sopenharmony_ci goto error; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci curr_ptr = card->mpa_rx.buf; 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { 18248c2ecf20Sopenharmony_ci u32 *len_arr = card->mpa_rx.len_arr; 18258c2ecf20Sopenharmony_ci 18268c2ecf20Sopenharmony_ci /* get curr PKT len & type */ 18278c2ecf20Sopenharmony_ci pkt_len = get_unaligned_le16(&curr_ptr[0]); 18288c2ecf20Sopenharmony_ci pkt_type = get_unaligned_le16(&curr_ptr[2]); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* copy pkt to deaggr buf */ 18318c2ecf20Sopenharmony_ci skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], 18328c2ecf20Sopenharmony_ci GFP_KERNEL); 18338c2ecf20Sopenharmony_ci if (!skb_deaggr) { 18348c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "skb allocation failure\t" 18358c2ecf20Sopenharmony_ci "drop pkt len=%d type=%d\n", 18368c2ecf20Sopenharmony_ci pkt_len, pkt_type); 18378c2ecf20Sopenharmony_ci curr_ptr += len_arr[pind]; 18388c2ecf20Sopenharmony_ci continue; 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci skb_put(skb_deaggr, len_arr[pind]); 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if ((pkt_type == MWIFIEX_TYPE_DATA || 18448c2ecf20Sopenharmony_ci (pkt_type == MWIFIEX_TYPE_AGGR_DATA && 18458c2ecf20Sopenharmony_ci adapter->sdio_rx_aggr_enable)) && 18468c2ecf20Sopenharmony_ci (pkt_len <= len_arr[pind])) { 18478c2ecf20Sopenharmony_ci 18488c2ecf20Sopenharmony_ci memcpy(skb_deaggr->data, curr_ptr, pkt_len); 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci skb_trim(skb_deaggr, pkt_len); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci /* Process de-aggr packet */ 18538c2ecf20Sopenharmony_ci mwifiex_decode_rx_packet(adapter, skb_deaggr, 18548c2ecf20Sopenharmony_ci pkt_type); 18558c2ecf20Sopenharmony_ci } else { 18568c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18578c2ecf20Sopenharmony_ci "drop wrong aggr pkt:\t" 18588c2ecf20Sopenharmony_ci "sdio_single_port_rx_aggr=%d\t" 18598c2ecf20Sopenharmony_ci "type=%d len=%d max_len=%d\n", 18608c2ecf20Sopenharmony_ci adapter->sdio_rx_aggr_enable, 18618c2ecf20Sopenharmony_ci pkt_type, pkt_len, len_arr[pind]); 18628c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb_deaggr); 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci curr_ptr += len_arr[pind]; 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci MP_RX_AGGR_BUF_RESET(card); 18678c2ecf20Sopenharmony_ci } 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_cirx_curr_single: 18708c2ecf20Sopenharmony_ci if (f_do_rx_cur) { 18718c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n", 18728c2ecf20Sopenharmony_ci port, rx_len); 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL); 18758c2ecf20Sopenharmony_ci if (!skb) { 18768c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 18778c2ecf20Sopenharmony_ci "single skb allocated fail,\t" 18788c2ecf20Sopenharmony_ci "drop pkt port=%d len=%d\n", port, rx_len); 18798c2ecf20Sopenharmony_ci if (mwifiex_sdio_card_to_host(adapter, &pkt_type, 18808c2ecf20Sopenharmony_ci card->mpa_rx.buf, rx_len, 18818c2ecf20Sopenharmony_ci adapter->ioport + port)) 18828c2ecf20Sopenharmony_ci goto error; 18838c2ecf20Sopenharmony_ci return 0; 18848c2ecf20Sopenharmony_ci } 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci skb_put(skb, rx_len); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci if (mwifiex_sdio_card_to_host(adapter, &pkt_type, 18898c2ecf20Sopenharmony_ci skb->data, skb->len, 18908c2ecf20Sopenharmony_ci adapter->ioport + port)) 18918c2ecf20Sopenharmony_ci goto error; 18928c2ecf20Sopenharmony_ci if (!adapter->sdio_rx_aggr_enable && 18938c2ecf20Sopenharmony_ci pkt_type == MWIFIEX_TYPE_AGGR_DATA) { 18948c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t" 18958c2ecf20Sopenharmony_ci "current SDIO RX Aggr not enabled\n", 18968c2ecf20Sopenharmony_ci pkt_type); 18978c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 18988c2ecf20Sopenharmony_ci return 0; 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci mwifiex_decode_rx_packet(adapter, skb, pkt_type); 19028c2ecf20Sopenharmony_ci } 19038c2ecf20Sopenharmony_ci if (f_post_aggr_cur) { 19048c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 19058c2ecf20Sopenharmony_ci "info: current packet aggregation\n"); 19068c2ecf20Sopenharmony_ci /* Curr pkt can be aggregated */ 19078c2ecf20Sopenharmony_ci mp_rx_aggr_setup(card, rx_len, port); 19088c2ecf20Sopenharmony_ci } 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci return 0; 19118c2ecf20Sopenharmony_cierror: 19128c2ecf20Sopenharmony_ci if (MP_RX_AGGR_IN_PROGRESS(card)) 19138c2ecf20Sopenharmony_ci MP_RX_AGGR_BUF_RESET(card); 19148c2ecf20Sopenharmony_ci 19158c2ecf20Sopenharmony_ci if (f_do_rx_cur && skb) 19168c2ecf20Sopenharmony_ci /* Single transfer pending. Free curr buff also */ 19178c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci return -1; 19208c2ecf20Sopenharmony_ci} 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci/* 19238c2ecf20Sopenharmony_ci * This function checks the current interrupt status. 19248c2ecf20Sopenharmony_ci * 19258c2ecf20Sopenharmony_ci * The following interrupts are checked and handled by this function - 19268c2ecf20Sopenharmony_ci * - Data sent 19278c2ecf20Sopenharmony_ci * - Command sent 19288c2ecf20Sopenharmony_ci * - Packets received 19298c2ecf20Sopenharmony_ci * 19308c2ecf20Sopenharmony_ci * Since the firmware does not generate download ready interrupt if the 19318c2ecf20Sopenharmony_ci * port updated is command port only, command sent interrupt checking 19328c2ecf20Sopenharmony_ci * should be done manually, and for every SDIO interrupt. 19338c2ecf20Sopenharmony_ci * 19348c2ecf20Sopenharmony_ci * In case of Rx packets received, the packets are uploaded from card to 19358c2ecf20Sopenharmony_ci * host and processed accordingly. 19368c2ecf20Sopenharmony_ci */ 19378c2ecf20Sopenharmony_cistatic int mwifiex_process_int_status(struct mwifiex_adapter *adapter) 19388c2ecf20Sopenharmony_ci{ 19398c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 19408c2ecf20Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 19418c2ecf20Sopenharmony_ci int ret = 0; 19428c2ecf20Sopenharmony_ci u8 sdio_ireg; 19438c2ecf20Sopenharmony_ci struct sk_buff *skb; 19448c2ecf20Sopenharmony_ci u8 port = CTRL_PORT; 19458c2ecf20Sopenharmony_ci u32 len_reg_l, len_reg_u; 19468c2ecf20Sopenharmony_ci u32 rx_blocks; 19478c2ecf20Sopenharmony_ci u16 rx_len; 19488c2ecf20Sopenharmony_ci unsigned long flags; 19498c2ecf20Sopenharmony_ci u32 bitmap; 19508c2ecf20Sopenharmony_ci u8 cr; 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_ci spin_lock_irqsave(&adapter->int_lock, flags); 19538c2ecf20Sopenharmony_ci sdio_ireg = adapter->int_status; 19548c2ecf20Sopenharmony_ci adapter->int_status = 0; 19558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&adapter->int_lock, flags); 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci if (!sdio_ireg) 19588c2ecf20Sopenharmony_ci return ret; 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci /* Following interrupt is only for SDIO new mode */ 19618c2ecf20Sopenharmony_ci if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) 19628c2ecf20Sopenharmony_ci adapter->cmd_sent = false; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci /* Following interrupt is only for SDIO new mode */ 19658c2ecf20Sopenharmony_ci if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { 19668c2ecf20Sopenharmony_ci u32 pkt_type; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci /* read the len of control packet */ 19698c2ecf20Sopenharmony_ci rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8; 19708c2ecf20Sopenharmony_ci rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0]; 19718c2ecf20Sopenharmony_ci rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); 19728c2ecf20Sopenharmony_ci if (rx_len <= adapter->intf_hdr_len || 19738c2ecf20Sopenharmony_ci (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > 19748c2ecf20Sopenharmony_ci MWIFIEX_RX_DATA_BUF_SIZE) 19758c2ecf20Sopenharmony_ci return -1; 19768c2ecf20Sopenharmony_ci rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); 19778c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL); 19808c2ecf20Sopenharmony_ci if (!skb) 19818c2ecf20Sopenharmony_ci return -1; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci skb_put(skb, rx_len); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, 19868c2ecf20Sopenharmony_ci skb->len, adapter->ioport | 19878c2ecf20Sopenharmony_ci CMD_PORT_SLCT)) { 19888c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 19898c2ecf20Sopenharmony_ci "%s: failed to card_to_host", __func__); 19908c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 19918c2ecf20Sopenharmony_ci goto term_cmd; 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci if ((pkt_type != MWIFIEX_TYPE_CMD) && 19958c2ecf20Sopenharmony_ci (pkt_type != MWIFIEX_TYPE_EVENT)) 19968c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 19978c2ecf20Sopenharmony_ci "%s:Received wrong packet on cmd port", 19988c2ecf20Sopenharmony_ci __func__); 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci mwifiex_decode_rx_packet(adapter, skb, pkt_type); 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci if (sdio_ireg & DN_LD_HOST_INT_STATUS) { 20048c2ecf20Sopenharmony_ci bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; 20058c2ecf20Sopenharmony_ci bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; 20068c2ecf20Sopenharmony_ci if (card->supports_sdio_new_mode) { 20078c2ecf20Sopenharmony_ci bitmap |= 20088c2ecf20Sopenharmony_ci ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; 20098c2ecf20Sopenharmony_ci bitmap |= 20108c2ecf20Sopenharmony_ci ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; 20118c2ecf20Sopenharmony_ci } 20128c2ecf20Sopenharmony_ci card->mp_wr_bitmap = bitmap; 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, 20158c2ecf20Sopenharmony_ci "int: DNLD: wr_bitmap=0x%x\n", 20168c2ecf20Sopenharmony_ci card->mp_wr_bitmap); 20178c2ecf20Sopenharmony_ci if (adapter->data_sent && 20188c2ecf20Sopenharmony_ci (card->mp_wr_bitmap & card->mp_data_port_mask)) { 20198c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, 20208c2ecf20Sopenharmony_ci "info: <--- Tx DONE Interrupt --->\n"); 20218c2ecf20Sopenharmony_ci adapter->data_sent = false; 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci } 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci /* As firmware will not generate download ready interrupt if the port 20268c2ecf20Sopenharmony_ci updated is command port only, cmd_sent should be done for any SDIO 20278c2ecf20Sopenharmony_ci interrupt. */ 20288c2ecf20Sopenharmony_ci if (card->has_control_mask && adapter->cmd_sent) { 20298c2ecf20Sopenharmony_ci /* Check if firmware has attach buffer at command port and 20308c2ecf20Sopenharmony_ci update just that in wr_bit_map. */ 20318c2ecf20Sopenharmony_ci card->mp_wr_bitmap |= 20328c2ecf20Sopenharmony_ci (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; 20338c2ecf20Sopenharmony_ci if (card->mp_wr_bitmap & CTRL_PORT_MASK) 20348c2ecf20Sopenharmony_ci adapter->cmd_sent = false; 20358c2ecf20Sopenharmony_ci } 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", 20388c2ecf20Sopenharmony_ci adapter->cmd_sent, adapter->data_sent); 20398c2ecf20Sopenharmony_ci if (sdio_ireg & UP_LD_HOST_INT_STATUS) { 20408c2ecf20Sopenharmony_ci bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; 20418c2ecf20Sopenharmony_ci bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; 20428c2ecf20Sopenharmony_ci if (card->supports_sdio_new_mode) { 20438c2ecf20Sopenharmony_ci bitmap |= 20448c2ecf20Sopenharmony_ci ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; 20458c2ecf20Sopenharmony_ci bitmap |= 20468c2ecf20Sopenharmony_ci ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci card->mp_rd_bitmap = bitmap; 20498c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INTR, 20508c2ecf20Sopenharmony_ci "int: UPLD: rd_bitmap=0x%x\n", 20518c2ecf20Sopenharmony_ci card->mp_rd_bitmap); 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci while (true) { 20548c2ecf20Sopenharmony_ci ret = mwifiex_get_rd_port(adapter, &port); 20558c2ecf20Sopenharmony_ci if (ret) { 20568c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 20578c2ecf20Sopenharmony_ci "info: no more rd_port available\n"); 20588c2ecf20Sopenharmony_ci break; 20598c2ecf20Sopenharmony_ci } 20608c2ecf20Sopenharmony_ci len_reg_l = reg->rd_len_p0_l + (port << 1); 20618c2ecf20Sopenharmony_ci len_reg_u = reg->rd_len_p0_u + (port << 1); 20628c2ecf20Sopenharmony_ci rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; 20638c2ecf20Sopenharmony_ci rx_len |= (u16) card->mp_regs[len_reg_l]; 20648c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 20658c2ecf20Sopenharmony_ci "info: RX: port=%d rx_len=%u\n", 20668c2ecf20Sopenharmony_ci port, rx_len); 20678c2ecf20Sopenharmony_ci rx_blocks = 20688c2ecf20Sopenharmony_ci (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - 20698c2ecf20Sopenharmony_ci 1) / MWIFIEX_SDIO_BLOCK_SIZE; 20708c2ecf20Sopenharmony_ci if (rx_len <= adapter->intf_hdr_len || 20718c2ecf20Sopenharmony_ci (card->mpa_rx.enabled && 20728c2ecf20Sopenharmony_ci ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > 20738c2ecf20Sopenharmony_ci card->mpa_rx.buf_size))) { 20748c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 20758c2ecf20Sopenharmony_ci "invalid rx_len=%d\n", 20768c2ecf20Sopenharmony_ci rx_len); 20778c2ecf20Sopenharmony_ci return -1; 20788c2ecf20Sopenharmony_ci } 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); 20818c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", 20828c2ecf20Sopenharmony_ci rx_len); 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len, 20858c2ecf20Sopenharmony_ci port)) { 20868c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 20878c2ecf20Sopenharmony_ci "card_to_host_mpa failed: int status=%#x\n", 20888c2ecf20Sopenharmony_ci sdio_ireg); 20898c2ecf20Sopenharmony_ci goto term_cmd; 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci } 20928c2ecf20Sopenharmony_ci } 20938c2ecf20Sopenharmony_ci 20948c2ecf20Sopenharmony_ci return 0; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_citerm_cmd: 20978c2ecf20Sopenharmony_ci /* terminate cmd */ 20988c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) 20998c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n"); 21008c2ecf20Sopenharmony_ci else 21018c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 21028c2ecf20Sopenharmony_ci "info: CFG reg val = %d\n", cr); 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) 21058c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 21068c2ecf20Sopenharmony_ci "write CFG reg failed\n"); 21078c2ecf20Sopenharmony_ci else 21088c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, "info: write success\n"); 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) 21118c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 21128c2ecf20Sopenharmony_ci "read CFG reg failed\n"); 21138c2ecf20Sopenharmony_ci else 21148c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 21158c2ecf20Sopenharmony_ci "info: CFG reg val =%x\n", cr); 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci return -1; 21188c2ecf20Sopenharmony_ci} 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci/* 21218c2ecf20Sopenharmony_ci * This function aggregates transmission buffers in driver and downloads 21228c2ecf20Sopenharmony_ci * the aggregated packet to card. 21238c2ecf20Sopenharmony_ci * 21248c2ecf20Sopenharmony_ci * The individual packets are aggregated by copying into an aggregation 21258c2ecf20Sopenharmony_ci * buffer and then downloaded to the card. Previous unsent packets in the 21268c2ecf20Sopenharmony_ci * aggregation buffer are pre-copied first before new packets are added. 21278c2ecf20Sopenharmony_ci * Aggregation is done till there is space left in the aggregation buffer, 21288c2ecf20Sopenharmony_ci * or till new packets are available. 21298c2ecf20Sopenharmony_ci * 21308c2ecf20Sopenharmony_ci * The function will only download the packet to the card when aggregation 21318c2ecf20Sopenharmony_ci * stops, otherwise it will just aggregate the packet in aggregation buffer 21328c2ecf20Sopenharmony_ci * and return. 21338c2ecf20Sopenharmony_ci */ 21348c2ecf20Sopenharmony_cistatic int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, 21358c2ecf20Sopenharmony_ci u8 *payload, u32 pkt_len, u32 port, 21368c2ecf20Sopenharmony_ci u32 next_pkt_len) 21378c2ecf20Sopenharmony_ci{ 21388c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 21398c2ecf20Sopenharmony_ci int ret = 0; 21408c2ecf20Sopenharmony_ci s32 f_send_aggr_buf = 0; 21418c2ecf20Sopenharmony_ci s32 f_send_cur_buf = 0; 21428c2ecf20Sopenharmony_ci s32 f_precopy_cur_buf = 0; 21438c2ecf20Sopenharmony_ci s32 f_postcopy_cur_buf = 0; 21448c2ecf20Sopenharmony_ci u32 mport; 21458c2ecf20Sopenharmony_ci int index; 21468c2ecf20Sopenharmony_ci 21478c2ecf20Sopenharmony_ci if (!card->mpa_tx.enabled || 21488c2ecf20Sopenharmony_ci (card->has_control_mask && (port == CTRL_PORT)) || 21498c2ecf20Sopenharmony_ci (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { 21508c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 21518c2ecf20Sopenharmony_ci "info: %s: tx aggregation disabled\n", 21528c2ecf20Sopenharmony_ci __func__); 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 21558c2ecf20Sopenharmony_ci goto tx_curr_single; 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci if (next_pkt_len) { 21598c2ecf20Sopenharmony_ci /* More pkt in TX queue */ 21608c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 21618c2ecf20Sopenharmony_ci "info: %s: more packets in queue.\n", 21628c2ecf20Sopenharmony_ci __func__); 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci if (MP_TX_AGGR_IN_PROGRESS(card)) { 21658c2ecf20Sopenharmony_ci if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { 21668c2ecf20Sopenharmony_ci f_precopy_cur_buf = 1; 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci if (!(card->mp_wr_bitmap & 21698c2ecf20Sopenharmony_ci (1 << card->curr_wr_port)) || 21708c2ecf20Sopenharmony_ci !MP_TX_AGGR_BUF_HAS_ROOM( 21718c2ecf20Sopenharmony_ci card, pkt_len + next_pkt_len)) 21728c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 21738c2ecf20Sopenharmony_ci } else { 21748c2ecf20Sopenharmony_ci /* No room in Aggr buf, send it */ 21758c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci if (!(card->mp_wr_bitmap & 21788c2ecf20Sopenharmony_ci (1 << card->curr_wr_port))) 21798c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 21808c2ecf20Sopenharmony_ci else 21818c2ecf20Sopenharmony_ci f_postcopy_cur_buf = 1; 21828c2ecf20Sopenharmony_ci } 21838c2ecf20Sopenharmony_ci } else { 21848c2ecf20Sopenharmony_ci if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && 21858c2ecf20Sopenharmony_ci (card->mp_wr_bitmap & (1 << card->curr_wr_port))) 21868c2ecf20Sopenharmony_ci f_precopy_cur_buf = 1; 21878c2ecf20Sopenharmony_ci else 21888c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 21898c2ecf20Sopenharmony_ci } 21908c2ecf20Sopenharmony_ci } else { 21918c2ecf20Sopenharmony_ci /* Last pkt in TX queue */ 21928c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, INFO, 21938c2ecf20Sopenharmony_ci "info: %s: Last packet in Tx Queue.\n", 21948c2ecf20Sopenharmony_ci __func__); 21958c2ecf20Sopenharmony_ci 21968c2ecf20Sopenharmony_ci if (MP_TX_AGGR_IN_PROGRESS(card)) { 21978c2ecf20Sopenharmony_ci /* some packs in Aggr buf already */ 21988c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) 22018c2ecf20Sopenharmony_ci f_precopy_cur_buf = 1; 22028c2ecf20Sopenharmony_ci else 22038c2ecf20Sopenharmony_ci /* No room in Aggr buf, send it */ 22048c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 22058c2ecf20Sopenharmony_ci } else { 22068c2ecf20Sopenharmony_ci f_send_cur_buf = 1; 22078c2ecf20Sopenharmony_ci } 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci if (f_precopy_cur_buf) { 22118c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 22128c2ecf20Sopenharmony_ci "data: %s: precopy current buffer\n", 22138c2ecf20Sopenharmony_ci __func__); 22148c2ecf20Sopenharmony_ci MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || 22178c2ecf20Sopenharmony_ci mp_tx_aggr_port_limit_reached(card)) 22188c2ecf20Sopenharmony_ci /* No more pkts allowed in Aggr buf, send it */ 22198c2ecf20Sopenharmony_ci f_send_aggr_buf = 1; 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci if (f_send_aggr_buf) { 22238c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 22248c2ecf20Sopenharmony_ci "data: %s: send aggr buffer: %d %d\n", 22258c2ecf20Sopenharmony_ci __func__, card->mpa_tx.start_port, 22268c2ecf20Sopenharmony_ci card->mpa_tx.ports); 22278c2ecf20Sopenharmony_ci if (card->supports_sdio_new_mode) { 22288c2ecf20Sopenharmony_ci u32 port_count; 22298c2ecf20Sopenharmony_ci int i; 22308c2ecf20Sopenharmony_ci 22318c2ecf20Sopenharmony_ci for (i = 0, port_count = 0; i < card->max_ports; i++) 22328c2ecf20Sopenharmony_ci if (card->mpa_tx.ports & BIT(i)) 22338c2ecf20Sopenharmony_ci port_count++; 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci /* Writing data from "start_port + 0" to "start_port + 22368c2ecf20Sopenharmony_ci * port_count -1", so decrease the count by 1 22378c2ecf20Sopenharmony_ci */ 22388c2ecf20Sopenharmony_ci port_count--; 22398c2ecf20Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 22408c2ecf20Sopenharmony_ci (port_count << 8)) + card->mpa_tx.start_port; 22418c2ecf20Sopenharmony_ci } else { 22428c2ecf20Sopenharmony_ci mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | 22438c2ecf20Sopenharmony_ci (card->mpa_tx.ports << 4)) + 22448c2ecf20Sopenharmony_ci card->mpa_tx.start_port; 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci if (card->mpa_tx.pkt_cnt == 1) 22488c2ecf20Sopenharmony_ci mport = adapter->ioport + card->mpa_tx.start_port; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, 22518c2ecf20Sopenharmony_ci card->mpa_tx.buf_len, mport); 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci /* Save the last multi port tx aggreagation info to debug log */ 22548c2ecf20Sopenharmony_ci index = adapter->dbg.last_sdio_mp_index; 22558c2ecf20Sopenharmony_ci index = (index + 1) % MWIFIEX_DBG_SDIO_MP_NUM; 22568c2ecf20Sopenharmony_ci adapter->dbg.last_sdio_mp_index = index; 22578c2ecf20Sopenharmony_ci adapter->dbg.last_mp_wr_ports[index] = mport; 22588c2ecf20Sopenharmony_ci adapter->dbg.last_mp_wr_bitmap[index] = card->mp_wr_bitmap; 22598c2ecf20Sopenharmony_ci adapter->dbg.last_mp_wr_len[index] = card->mpa_tx.buf_len; 22608c2ecf20Sopenharmony_ci adapter->dbg.last_mp_curr_wr_port[index] = card->curr_wr_port; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci MP_TX_AGGR_BUF_RESET(card); 22638c2ecf20Sopenharmony_ci } 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_citx_curr_single: 22668c2ecf20Sopenharmony_ci if (f_send_cur_buf) { 22678c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 22688c2ecf20Sopenharmony_ci "data: %s: send current buffer %d\n", 22698c2ecf20Sopenharmony_ci __func__, port); 22708c2ecf20Sopenharmony_ci ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, 22718c2ecf20Sopenharmony_ci adapter->ioport + port); 22728c2ecf20Sopenharmony_ci } 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci if (f_postcopy_cur_buf) { 22758c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DATA, 22768c2ecf20Sopenharmony_ci "data: %s: postcopy current buffer\n", 22778c2ecf20Sopenharmony_ci __func__); 22788c2ecf20Sopenharmony_ci MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci 22818c2ecf20Sopenharmony_ci return ret; 22828c2ecf20Sopenharmony_ci} 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci/* 22858c2ecf20Sopenharmony_ci * This function downloads data from driver to card. 22868c2ecf20Sopenharmony_ci * 22878c2ecf20Sopenharmony_ci * Both commands and data packets are transferred to the card by this 22888c2ecf20Sopenharmony_ci * function. 22898c2ecf20Sopenharmony_ci * 22908c2ecf20Sopenharmony_ci * This function adds the SDIO specific header to the front of the buffer 22918c2ecf20Sopenharmony_ci * before transferring. The header contains the length of the packet and 22928c2ecf20Sopenharmony_ci * the type. The firmware handles the packets based upon this set type. 22938c2ecf20Sopenharmony_ci */ 22948c2ecf20Sopenharmony_cistatic int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, 22958c2ecf20Sopenharmony_ci u8 type, struct sk_buff *skb, 22968c2ecf20Sopenharmony_ci struct mwifiex_tx_param *tx_param) 22978c2ecf20Sopenharmony_ci{ 22988c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 22998c2ecf20Sopenharmony_ci int ret; 23008c2ecf20Sopenharmony_ci u32 buf_block_len; 23018c2ecf20Sopenharmony_ci u32 blk_size; 23028c2ecf20Sopenharmony_ci u32 port = CTRL_PORT; 23038c2ecf20Sopenharmony_ci u8 *payload = (u8 *)skb->data; 23048c2ecf20Sopenharmony_ci u32 pkt_len = skb->len; 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci /* Allocate buffer and copy payload */ 23078c2ecf20Sopenharmony_ci blk_size = MWIFIEX_SDIO_BLOCK_SIZE; 23088c2ecf20Sopenharmony_ci buf_block_len = (pkt_len + blk_size - 1) / blk_size; 23098c2ecf20Sopenharmony_ci put_unaligned_le16((u16)pkt_len, payload + 0); 23108c2ecf20Sopenharmony_ci put_unaligned_le16((u32)type, payload + 2); 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci /* 23148c2ecf20Sopenharmony_ci * This is SDIO specific header 23158c2ecf20Sopenharmony_ci * u16 length, 23168c2ecf20Sopenharmony_ci * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, 23178c2ecf20Sopenharmony_ci * MWIFIEX_TYPE_EVENT = 3) 23188c2ecf20Sopenharmony_ci */ 23198c2ecf20Sopenharmony_ci if (type == MWIFIEX_TYPE_DATA) { 23208c2ecf20Sopenharmony_ci ret = mwifiex_get_wr_port_data(adapter, &port); 23218c2ecf20Sopenharmony_ci if (ret) { 23228c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 23238c2ecf20Sopenharmony_ci "%s: no wr_port available\n", 23248c2ecf20Sopenharmony_ci __func__); 23258c2ecf20Sopenharmony_ci return ret; 23268c2ecf20Sopenharmony_ci } 23278c2ecf20Sopenharmony_ci } else { 23288c2ecf20Sopenharmony_ci adapter->cmd_sent = true; 23298c2ecf20Sopenharmony_ci /* Type must be MWIFIEX_TYPE_CMD */ 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (pkt_len <= adapter->intf_hdr_len || 23328c2ecf20Sopenharmony_ci pkt_len > MWIFIEX_UPLD_SIZE) 23338c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 23348c2ecf20Sopenharmony_ci "%s: payload=%p, nb=%d\n", 23358c2ecf20Sopenharmony_ci __func__, payload, pkt_len); 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci if (card->supports_sdio_new_mode) 23388c2ecf20Sopenharmony_ci port = CMD_PORT_SLCT; 23398c2ecf20Sopenharmony_ci } 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci /* Transfer data to card */ 23428c2ecf20Sopenharmony_ci pkt_len = buf_block_len * blk_size; 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci if (tx_param) 23458c2ecf20Sopenharmony_ci ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, 23468c2ecf20Sopenharmony_ci port, tx_param->next_pkt_len 23478c2ecf20Sopenharmony_ci ); 23488c2ecf20Sopenharmony_ci else 23498c2ecf20Sopenharmony_ci ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, 23508c2ecf20Sopenharmony_ci port, 0); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci if (ret) { 23538c2ecf20Sopenharmony_ci if (type == MWIFIEX_TYPE_CMD) 23548c2ecf20Sopenharmony_ci adapter->cmd_sent = false; 23558c2ecf20Sopenharmony_ci if (type == MWIFIEX_TYPE_DATA) { 23568c2ecf20Sopenharmony_ci adapter->data_sent = false; 23578c2ecf20Sopenharmony_ci /* restore curr_wr_port in error cases */ 23588c2ecf20Sopenharmony_ci card->curr_wr_port = port; 23598c2ecf20Sopenharmony_ci card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); 23608c2ecf20Sopenharmony_ci } 23618c2ecf20Sopenharmony_ci } else { 23628c2ecf20Sopenharmony_ci if (type == MWIFIEX_TYPE_DATA) { 23638c2ecf20Sopenharmony_ci if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) 23648c2ecf20Sopenharmony_ci adapter->data_sent = true; 23658c2ecf20Sopenharmony_ci else 23668c2ecf20Sopenharmony_ci adapter->data_sent = false; 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci } 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci return ret; 23718c2ecf20Sopenharmony_ci} 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci/* 23748c2ecf20Sopenharmony_ci * This function allocates the MPA Tx and Rx buffers. 23758c2ecf20Sopenharmony_ci */ 23768c2ecf20Sopenharmony_cistatic int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, 23778c2ecf20Sopenharmony_ci u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) 23788c2ecf20Sopenharmony_ci{ 23798c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 23808c2ecf20Sopenharmony_ci u32 rx_buf_size; 23818c2ecf20Sopenharmony_ci int ret = 0; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); 23848c2ecf20Sopenharmony_ci if (!card->mpa_tx.buf) { 23858c2ecf20Sopenharmony_ci ret = -1; 23868c2ecf20Sopenharmony_ci goto error; 23878c2ecf20Sopenharmony_ci } 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci card->mpa_tx.buf_size = mpa_tx_buf_size; 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci rx_buf_size = max_t(u32, mpa_rx_buf_size, 23928c2ecf20Sopenharmony_ci (u32)SDIO_MAX_AGGR_BUF_SIZE); 23938c2ecf20Sopenharmony_ci card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); 23948c2ecf20Sopenharmony_ci if (!card->mpa_rx.buf) { 23958c2ecf20Sopenharmony_ci ret = -1; 23968c2ecf20Sopenharmony_ci goto error; 23978c2ecf20Sopenharmony_ci } 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci card->mpa_rx.buf_size = rx_buf_size; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_cierror: 24028c2ecf20Sopenharmony_ci if (ret) { 24038c2ecf20Sopenharmony_ci kfree(card->mpa_tx.buf); 24048c2ecf20Sopenharmony_ci kfree(card->mpa_rx.buf); 24058c2ecf20Sopenharmony_ci card->mpa_tx.buf_size = 0; 24068c2ecf20Sopenharmony_ci card->mpa_rx.buf_size = 0; 24078c2ecf20Sopenharmony_ci card->mpa_tx.buf = NULL; 24088c2ecf20Sopenharmony_ci card->mpa_rx.buf = NULL; 24098c2ecf20Sopenharmony_ci } 24108c2ecf20Sopenharmony_ci 24118c2ecf20Sopenharmony_ci return ret; 24128c2ecf20Sopenharmony_ci} 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_ci/* 24158c2ecf20Sopenharmony_ci * This function unregisters the SDIO device. 24168c2ecf20Sopenharmony_ci * 24178c2ecf20Sopenharmony_ci * The SDIO IRQ is released, the function is disabled and driver 24188c2ecf20Sopenharmony_ci * data is set to null. 24198c2ecf20Sopenharmony_ci */ 24208c2ecf20Sopenharmony_cistatic void 24218c2ecf20Sopenharmony_cimwifiex_unregister_dev(struct mwifiex_adapter *adapter) 24228c2ecf20Sopenharmony_ci{ 24238c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci if (adapter->card) { 24268c2ecf20Sopenharmony_ci card->adapter = NULL; 24278c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 24288c2ecf20Sopenharmony_ci sdio_disable_func(card->func); 24298c2ecf20Sopenharmony_ci sdio_release_host(card->func); 24308c2ecf20Sopenharmony_ci } 24318c2ecf20Sopenharmony_ci} 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci/* 24348c2ecf20Sopenharmony_ci * This function registers the SDIO device. 24358c2ecf20Sopenharmony_ci * 24368c2ecf20Sopenharmony_ci * SDIO IRQ is claimed, block size is set and driver data is initialized. 24378c2ecf20Sopenharmony_ci */ 24388c2ecf20Sopenharmony_cistatic int mwifiex_register_dev(struct mwifiex_adapter *adapter) 24398c2ecf20Sopenharmony_ci{ 24408c2ecf20Sopenharmony_ci int ret; 24418c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 24428c2ecf20Sopenharmony_ci struct sdio_func *func = card->func; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci /* save adapter pointer in card */ 24458c2ecf20Sopenharmony_ci card->adapter = adapter; 24468c2ecf20Sopenharmony_ci adapter->tx_buf_size = card->tx_buf_size; 24478c2ecf20Sopenharmony_ci 24488c2ecf20Sopenharmony_ci sdio_claim_host(func); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci /* Set block size */ 24518c2ecf20Sopenharmony_ci ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); 24528c2ecf20Sopenharmony_ci sdio_release_host(func); 24538c2ecf20Sopenharmony_ci if (ret) { 24548c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 24558c2ecf20Sopenharmony_ci "cannot set SDIO block size\n"); 24568c2ecf20Sopenharmony_ci return ret; 24578c2ecf20Sopenharmony_ci } 24588c2ecf20Sopenharmony_ci 24598c2ecf20Sopenharmony_ci strcpy(adapter->fw_name, card->firmware); 24608c2ecf20Sopenharmony_ci if (card->fw_dump_enh) { 24618c2ecf20Sopenharmony_ci adapter->mem_type_mapping_tbl = generic_mem_type_map; 24628c2ecf20Sopenharmony_ci adapter->num_mem_types = 1; 24638c2ecf20Sopenharmony_ci } else { 24648c2ecf20Sopenharmony_ci adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; 24658c2ecf20Sopenharmony_ci adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); 24668c2ecf20Sopenharmony_ci } 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci return 0; 24698c2ecf20Sopenharmony_ci} 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci/* 24728c2ecf20Sopenharmony_ci * This function initializes the SDIO driver. 24738c2ecf20Sopenharmony_ci * 24748c2ecf20Sopenharmony_ci * The following initializations steps are followed - 24758c2ecf20Sopenharmony_ci * - Read the Host interrupt status register to acknowledge 24768c2ecf20Sopenharmony_ci * the first interrupt got from bootloader 24778c2ecf20Sopenharmony_ci * - Disable host interrupt mask register 24788c2ecf20Sopenharmony_ci * - Get SDIO port 24798c2ecf20Sopenharmony_ci * - Initialize SDIO variables in card 24808c2ecf20Sopenharmony_ci * - Allocate MP registers 24818c2ecf20Sopenharmony_ci * - Allocate MPA Tx and Rx buffers 24828c2ecf20Sopenharmony_ci */ 24838c2ecf20Sopenharmony_cistatic int mwifiex_init_sdio(struct mwifiex_adapter *adapter) 24848c2ecf20Sopenharmony_ci{ 24858c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 24868c2ecf20Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 24878c2ecf20Sopenharmony_ci int ret; 24888c2ecf20Sopenharmony_ci u8 sdio_ireg; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci sdio_set_drvdata(card->func, card); 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci /* 24938c2ecf20Sopenharmony_ci * Read the host_int_status_reg for ACK the first interrupt got 24948c2ecf20Sopenharmony_ci * from the bootloader. If we don't do this we get a interrupt 24958c2ecf20Sopenharmony_ci * as soon as we register the irq. 24968c2ecf20Sopenharmony_ci */ 24978c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci /* Get SDIO ioport */ 25008c2ecf20Sopenharmony_ci mwifiex_init_sdio_ioport(adapter); 25018c2ecf20Sopenharmony_ci 25028c2ecf20Sopenharmony_ci /* Initialize SDIO variables in card */ 25038c2ecf20Sopenharmony_ci card->mp_rd_bitmap = 0; 25048c2ecf20Sopenharmony_ci card->mp_wr_bitmap = 0; 25058c2ecf20Sopenharmony_ci card->curr_rd_port = reg->start_rd_port; 25068c2ecf20Sopenharmony_ci card->curr_wr_port = reg->start_wr_port; 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci card->mp_data_port_mask = reg->data_port_mask; 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci card->mpa_tx.buf_len = 0; 25118c2ecf20Sopenharmony_ci card->mpa_tx.pkt_cnt = 0; 25128c2ecf20Sopenharmony_ci card->mpa_tx.start_port = 0; 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci card->mpa_tx.enabled = 1; 25158c2ecf20Sopenharmony_ci card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci card->mpa_rx.buf_len = 0; 25188c2ecf20Sopenharmony_ci card->mpa_rx.pkt_cnt = 0; 25198c2ecf20Sopenharmony_ci card->mpa_rx.start_port = 0; 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_ci card->mpa_rx.enabled = 1; 25228c2ecf20Sopenharmony_ci card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; 25238c2ecf20Sopenharmony_ci 25248c2ecf20Sopenharmony_ci /* Allocate buffers for SDIO MP-A */ 25258c2ecf20Sopenharmony_ci card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); 25268c2ecf20Sopenharmony_ci if (!card->mp_regs) 25278c2ecf20Sopenharmony_ci return -ENOMEM; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci /* Allocate skb pointer buffers */ 25308c2ecf20Sopenharmony_ci card->mpa_rx.skb_arr = kcalloc(card->mp_agg_pkt_limit, sizeof(void *), 25318c2ecf20Sopenharmony_ci GFP_KERNEL); 25328c2ecf20Sopenharmony_ci if (!card->mpa_rx.skb_arr) { 25338c2ecf20Sopenharmony_ci kfree(card->mp_regs); 25348c2ecf20Sopenharmony_ci return -ENOMEM; 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci card->mpa_rx.len_arr = kcalloc(card->mp_agg_pkt_limit, 25388c2ecf20Sopenharmony_ci sizeof(*card->mpa_rx.len_arr), 25398c2ecf20Sopenharmony_ci GFP_KERNEL); 25408c2ecf20Sopenharmony_ci if (!card->mpa_rx.len_arr) { 25418c2ecf20Sopenharmony_ci kfree(card->mp_regs); 25428c2ecf20Sopenharmony_ci kfree(card->mpa_rx.skb_arr); 25438c2ecf20Sopenharmony_ci return -ENOMEM; 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci ret = mwifiex_alloc_sdio_mpa_buffers(adapter, 25478c2ecf20Sopenharmony_ci card->mp_tx_agg_buf_size, 25488c2ecf20Sopenharmony_ci card->mp_rx_agg_buf_size); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ 25518c2ecf20Sopenharmony_ci if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || 25528c2ecf20Sopenharmony_ci card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { 25538c2ecf20Sopenharmony_ci /* Disable rx single port aggregation */ 25548c2ecf20Sopenharmony_ci adapter->host_disable_sdio_rx_aggr = true; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci ret = mwifiex_alloc_sdio_mpa_buffers 25578c2ecf20Sopenharmony_ci (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, 25588c2ecf20Sopenharmony_ci MWIFIEX_MP_AGGR_BUF_SIZE_32K); 25598c2ecf20Sopenharmony_ci if (ret) { 25608c2ecf20Sopenharmony_ci /* Disable multi port aggregation */ 25618c2ecf20Sopenharmony_ci card->mpa_tx.enabled = 0; 25628c2ecf20Sopenharmony_ci card->mpa_rx.enabled = 0; 25638c2ecf20Sopenharmony_ci } 25648c2ecf20Sopenharmony_ci } 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci adapter->auto_tdls = card->can_auto_tdls; 25678c2ecf20Sopenharmony_ci adapter->ext_scan = card->can_ext_scan; 25688c2ecf20Sopenharmony_ci return 0; 25698c2ecf20Sopenharmony_ci} 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_ci/* 25728c2ecf20Sopenharmony_ci * This function resets the MPA Tx and Rx buffers. 25738c2ecf20Sopenharmony_ci */ 25748c2ecf20Sopenharmony_cistatic void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) 25758c2ecf20Sopenharmony_ci{ 25768c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci MP_TX_AGGR_BUF_RESET(card); 25798c2ecf20Sopenharmony_ci MP_RX_AGGR_BUF_RESET(card); 25808c2ecf20Sopenharmony_ci} 25818c2ecf20Sopenharmony_ci 25828c2ecf20Sopenharmony_ci/* 25838c2ecf20Sopenharmony_ci * This function cleans up the allocated card buffers. 25848c2ecf20Sopenharmony_ci * 25858c2ecf20Sopenharmony_ci * The following are freed by this function - 25868c2ecf20Sopenharmony_ci * - MP registers 25878c2ecf20Sopenharmony_ci * - MPA Tx buffer 25888c2ecf20Sopenharmony_ci * - MPA Rx buffer 25898c2ecf20Sopenharmony_ci */ 25908c2ecf20Sopenharmony_cistatic void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) 25918c2ecf20Sopenharmony_ci{ 25928c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci cancel_work_sync(&card->work); 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_ci kfree(card->mp_regs); 25978c2ecf20Sopenharmony_ci kfree(card->mpa_rx.skb_arr); 25988c2ecf20Sopenharmony_ci kfree(card->mpa_rx.len_arr); 25998c2ecf20Sopenharmony_ci kfree(card->mpa_tx.buf); 26008c2ecf20Sopenharmony_ci kfree(card->mpa_rx.buf); 26018c2ecf20Sopenharmony_ci} 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci/* 26048c2ecf20Sopenharmony_ci * This function updates the MP end port in card. 26058c2ecf20Sopenharmony_ci */ 26068c2ecf20Sopenharmony_cistatic void 26078c2ecf20Sopenharmony_cimwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) 26088c2ecf20Sopenharmony_ci{ 26098c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 26108c2ecf20Sopenharmony_ci const struct mwifiex_sdio_card_reg *reg = card->reg; 26118c2ecf20Sopenharmony_ci int i; 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci card->mp_end_port = port; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci card->mp_data_port_mask = reg->data_port_mask; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci if (reg->start_wr_port) { 26188c2ecf20Sopenharmony_ci for (i = 1; i <= card->max_ports - card->mp_end_port; i++) 26198c2ecf20Sopenharmony_ci card->mp_data_port_mask &= 26208c2ecf20Sopenharmony_ci ~(1 << (card->max_ports - i)); 26218c2ecf20Sopenharmony_ci } 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci card->curr_wr_port = reg->start_wr_port; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, CMD, 26268c2ecf20Sopenharmony_ci "cmd: mp_end_port %d, data port mask 0x%x\n", 26278c2ecf20Sopenharmony_ci port, card->mp_data_port_mask); 26288c2ecf20Sopenharmony_ci} 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_cistatic void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) 26318c2ecf20Sopenharmony_ci{ 26328c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 26338c2ecf20Sopenharmony_ci struct sdio_func *func = card->func; 26348c2ecf20Sopenharmony_ci int ret; 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci /* Prepare the adapter for the reset. */ 26378c2ecf20Sopenharmony_ci mwifiex_shutdown_sw(adapter); 26388c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); 26398c2ecf20Sopenharmony_ci clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci /* Run a HW reset of the SDIO interface. */ 26428c2ecf20Sopenharmony_ci sdio_claim_host(func); 26438c2ecf20Sopenharmony_ci ret = mmc_hw_reset(func->card->host); 26448c2ecf20Sopenharmony_ci sdio_release_host(func); 26458c2ecf20Sopenharmony_ci 26468c2ecf20Sopenharmony_ci switch (ret) { 26478c2ecf20Sopenharmony_ci case 1: 26488c2ecf20Sopenharmony_ci dev_dbg(&func->dev, "SDIO HW reset asynchronous\n"); 26498c2ecf20Sopenharmony_ci complete_all(adapter->fw_done); 26508c2ecf20Sopenharmony_ci break; 26518c2ecf20Sopenharmony_ci case 0: 26528c2ecf20Sopenharmony_ci ret = mwifiex_reinit_sw(adapter); 26538c2ecf20Sopenharmony_ci if (ret) 26548c2ecf20Sopenharmony_ci dev_err(&func->dev, "reinit failed: %d\n", ret); 26558c2ecf20Sopenharmony_ci break; 26568c2ecf20Sopenharmony_ci default: 26578c2ecf20Sopenharmony_ci dev_err(&func->dev, "SDIO HW reset failed: %d\n", ret); 26588c2ecf20Sopenharmony_ci break; 26598c2ecf20Sopenharmony_ci } 26608c2ecf20Sopenharmony_ci} 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci/* This function read/write firmware */ 26638c2ecf20Sopenharmony_cistatic enum 26648c2ecf20Sopenharmony_cirdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, 26658c2ecf20Sopenharmony_ci u8 doneflag) 26668c2ecf20Sopenharmony_ci{ 26678c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 26688c2ecf20Sopenharmony_ci int ret, tries; 26698c2ecf20Sopenharmony_ci u8 ctrl_data = 0; 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci sdio_writeb(card->func, card->reg->fw_dump_host_ready, 26728c2ecf20Sopenharmony_ci card->reg->fw_dump_ctrl, &ret); 26738c2ecf20Sopenharmony_ci if (ret) { 26748c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n"); 26758c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 26768c2ecf20Sopenharmony_ci } 26778c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 26788c2ecf20Sopenharmony_ci ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, 26798c2ecf20Sopenharmony_ci &ret); 26808c2ecf20Sopenharmony_ci if (ret) { 26818c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); 26828c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 26838c2ecf20Sopenharmony_ci } 26848c2ecf20Sopenharmony_ci if (ctrl_data == FW_DUMP_DONE) 26858c2ecf20Sopenharmony_ci break; 26868c2ecf20Sopenharmony_ci if (doneflag && ctrl_data == doneflag) 26878c2ecf20Sopenharmony_ci return RDWR_STATUS_DONE; 26888c2ecf20Sopenharmony_ci if (ctrl_data != card->reg->fw_dump_host_ready) { 26898c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, WARN, 26908c2ecf20Sopenharmony_ci "The ctrl reg was changed, re-try again\n"); 26918c2ecf20Sopenharmony_ci sdio_writeb(card->func, card->reg->fw_dump_host_ready, 26928c2ecf20Sopenharmony_ci card->reg->fw_dump_ctrl, &ret); 26938c2ecf20Sopenharmony_ci if (ret) { 26948c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); 26958c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 26968c2ecf20Sopenharmony_ci } 26978c2ecf20Sopenharmony_ci } 26988c2ecf20Sopenharmony_ci usleep_range(100, 200); 26998c2ecf20Sopenharmony_ci } 27008c2ecf20Sopenharmony_ci if (ctrl_data == card->reg->fw_dump_host_ready) { 27018c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 27028c2ecf20Sopenharmony_ci "Fail to pull ctrl_data\n"); 27038c2ecf20Sopenharmony_ci return RDWR_STATUS_FAILURE; 27048c2ecf20Sopenharmony_ci } 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci return RDWR_STATUS_SUCCESS; 27078c2ecf20Sopenharmony_ci} 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci/* This function dump firmware memory to file */ 27108c2ecf20Sopenharmony_cistatic void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) 27118c2ecf20Sopenharmony_ci{ 27128c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 27138c2ecf20Sopenharmony_ci int ret = 0; 27148c2ecf20Sopenharmony_ci unsigned int reg, reg_start, reg_end; 27158c2ecf20Sopenharmony_ci u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; 27168c2ecf20Sopenharmony_ci enum rdwr_status stat; 27178c2ecf20Sopenharmony_ci u32 memory_size; 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci if (!card->can_dump_fw) 27208c2ecf20Sopenharmony_ci return; 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { 27238c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci if (entry->mem_ptr) { 27268c2ecf20Sopenharmony_ci vfree(entry->mem_ptr); 27278c2ecf20Sopenharmony_ci entry->mem_ptr = NULL; 27288c2ecf20Sopenharmony_ci } 27298c2ecf20Sopenharmony_ci entry->mem_size = 0; 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 27338c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); 27388c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 27398c2ecf20Sopenharmony_ci goto done; 27408c2ecf20Sopenharmony_ci 27418c2ecf20Sopenharmony_ci reg = card->reg->fw_dump_start; 27428c2ecf20Sopenharmony_ci /* Read the number of the memories which will dump */ 27438c2ecf20Sopenharmony_ci dump_num = sdio_readb(card->func, reg, &ret); 27448c2ecf20Sopenharmony_ci if (ret) { 27458c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n"); 27468c2ecf20Sopenharmony_ci goto done; 27478c2ecf20Sopenharmony_ci } 27488c2ecf20Sopenharmony_ci 27498c2ecf20Sopenharmony_ci /* Read the length of every memory which will dump */ 27508c2ecf20Sopenharmony_ci for (idx = 0; idx < dump_num; idx++) { 27518c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); 27548c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 27558c2ecf20Sopenharmony_ci goto done; 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci memory_size = 0; 27588c2ecf20Sopenharmony_ci reg = card->reg->fw_dump_start; 27598c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 27608c2ecf20Sopenharmony_ci read_reg = sdio_readb(card->func, reg, &ret); 27618c2ecf20Sopenharmony_ci if (ret) { 27628c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); 27638c2ecf20Sopenharmony_ci goto done; 27648c2ecf20Sopenharmony_ci } 27658c2ecf20Sopenharmony_ci memory_size |= (read_reg << i*8); 27668c2ecf20Sopenharmony_ci reg++; 27678c2ecf20Sopenharmony_ci } 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci if (memory_size == 0) { 27708c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n"); 27718c2ecf20Sopenharmony_ci ret = mwifiex_write_reg(adapter, 27728c2ecf20Sopenharmony_ci card->reg->fw_dump_ctrl, 27738c2ecf20Sopenharmony_ci FW_DUMP_READ_DONE); 27748c2ecf20Sopenharmony_ci if (ret) { 27758c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); 27768c2ecf20Sopenharmony_ci return; 27778c2ecf20Sopenharmony_ci } 27788c2ecf20Sopenharmony_ci break; 27798c2ecf20Sopenharmony_ci } 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 27828c2ecf20Sopenharmony_ci "%s_SIZE=0x%x\n", entry->mem_name, memory_size); 27838c2ecf20Sopenharmony_ci entry->mem_ptr = vmalloc(memory_size + 1); 27848c2ecf20Sopenharmony_ci entry->mem_size = memory_size; 27858c2ecf20Sopenharmony_ci if (!entry->mem_ptr) { 27868c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n", 27878c2ecf20Sopenharmony_ci entry->mem_name); 27888c2ecf20Sopenharmony_ci goto done; 27898c2ecf20Sopenharmony_ci } 27908c2ecf20Sopenharmony_ci dbg_ptr = entry->mem_ptr; 27918c2ecf20Sopenharmony_ci end_ptr = dbg_ptr + memory_size; 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci doneflag = entry->done_flag; 27948c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 27958c2ecf20Sopenharmony_ci "Start %s output, please wait...\n", 27968c2ecf20Sopenharmony_ci entry->mem_name); 27978c2ecf20Sopenharmony_ci 27988c2ecf20Sopenharmony_ci do { 27998c2ecf20Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); 28008c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 28018c2ecf20Sopenharmony_ci goto done; 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci reg_start = card->reg->fw_dump_start; 28048c2ecf20Sopenharmony_ci reg_end = card->reg->fw_dump_end; 28058c2ecf20Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 28068c2ecf20Sopenharmony_ci *dbg_ptr = sdio_readb(card->func, reg, &ret); 28078c2ecf20Sopenharmony_ci if (ret) { 28088c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 28098c2ecf20Sopenharmony_ci "SDIO read err\n"); 28108c2ecf20Sopenharmony_ci goto done; 28118c2ecf20Sopenharmony_ci } 28128c2ecf20Sopenharmony_ci if (dbg_ptr < end_ptr) 28138c2ecf20Sopenharmony_ci dbg_ptr++; 28148c2ecf20Sopenharmony_ci else 28158c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 28168c2ecf20Sopenharmony_ci "Allocated buf not enough\n"); 28178c2ecf20Sopenharmony_ci } 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_ci if (stat != RDWR_STATUS_DONE) 28208c2ecf20Sopenharmony_ci continue; 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n", 28238c2ecf20Sopenharmony_ci entry->mem_name, dbg_ptr - entry->mem_ptr); 28248c2ecf20Sopenharmony_ci break; 28258c2ecf20Sopenharmony_ci } while (1); 28268c2ecf20Sopenharmony_ci } 28278c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_cidone: 28308c2ecf20Sopenharmony_ci sdio_release_host(card->func); 28318c2ecf20Sopenharmony_ci} 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_cistatic void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter) 28348c2ecf20Sopenharmony_ci{ 28358c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 28368c2ecf20Sopenharmony_ci struct memory_type_mapping *entry = &generic_mem_type_map[0]; 28378c2ecf20Sopenharmony_ci unsigned int reg, reg_start, reg_end; 28388c2ecf20Sopenharmony_ci u8 start_flag = 0, done_flag = 0; 28398c2ecf20Sopenharmony_ci u8 *dbg_ptr, *end_ptr; 28408c2ecf20Sopenharmony_ci enum rdwr_status stat; 28418c2ecf20Sopenharmony_ci int ret = -1, tries; 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci if (!card->fw_dump_enh) 28448c2ecf20Sopenharmony_ci return; 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci if (entry->mem_ptr) { 28478c2ecf20Sopenharmony_ci vfree(entry->mem_ptr); 28488c2ecf20Sopenharmony_ci entry->mem_ptr = NULL; 28498c2ecf20Sopenharmony_ci } 28508c2ecf20Sopenharmony_ci entry->mem_size = 0; 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 28538c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 28548c2ecf20Sopenharmony_ci 28558c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); 28588c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 28598c2ecf20Sopenharmony_ci goto done; 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci reg_start = card->reg->fw_dump_start; 28628c2ecf20Sopenharmony_ci reg_end = card->reg->fw_dump_end; 28638c2ecf20Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 28648c2ecf20Sopenharmony_ci for (tries = 0; tries < MAX_POLL_TRIES; tries++) { 28658c2ecf20Sopenharmony_ci start_flag = sdio_readb(card->func, reg, &ret); 28668c2ecf20Sopenharmony_ci if (ret) { 28678c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 28688c2ecf20Sopenharmony_ci "SDIO read err\n"); 28698c2ecf20Sopenharmony_ci goto done; 28708c2ecf20Sopenharmony_ci } 28718c2ecf20Sopenharmony_ci if (start_flag == 0) 28728c2ecf20Sopenharmony_ci break; 28738c2ecf20Sopenharmony_ci if (tries == MAX_POLL_TRIES) { 28748c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 28758c2ecf20Sopenharmony_ci "FW not ready to dump\n"); 28768c2ecf20Sopenharmony_ci ret = -1; 28778c2ecf20Sopenharmony_ci goto done; 28788c2ecf20Sopenharmony_ci } 28798c2ecf20Sopenharmony_ci } 28808c2ecf20Sopenharmony_ci usleep_range(100, 200); 28818c2ecf20Sopenharmony_ci } 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci entry->mem_ptr = vmalloc(0xf0000 + 1); 28848c2ecf20Sopenharmony_ci if (!entry->mem_ptr) { 28858c2ecf20Sopenharmony_ci ret = -1; 28868c2ecf20Sopenharmony_ci goto done; 28878c2ecf20Sopenharmony_ci } 28888c2ecf20Sopenharmony_ci dbg_ptr = entry->mem_ptr; 28898c2ecf20Sopenharmony_ci entry->mem_size = 0xf0000; 28908c2ecf20Sopenharmony_ci end_ptr = dbg_ptr + entry->mem_size; 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci done_flag = entry->done_flag; 28938c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, 28948c2ecf20Sopenharmony_ci "Start %s output, please wait...\n", entry->mem_name); 28958c2ecf20Sopenharmony_ci 28968c2ecf20Sopenharmony_ci while (true) { 28978c2ecf20Sopenharmony_ci stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); 28988c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_FAILURE) 28998c2ecf20Sopenharmony_ci goto done; 29008c2ecf20Sopenharmony_ci for (reg = reg_start; reg <= reg_end; reg++) { 29018c2ecf20Sopenharmony_ci *dbg_ptr = sdio_readb(card->func, reg, &ret); 29028c2ecf20Sopenharmony_ci if (ret) { 29038c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 29048c2ecf20Sopenharmony_ci "SDIO read err\n"); 29058c2ecf20Sopenharmony_ci goto done; 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci dbg_ptr++; 29088c2ecf20Sopenharmony_ci if (dbg_ptr >= end_ptr) { 29098c2ecf20Sopenharmony_ci u8 *tmp_ptr; 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1); 29128c2ecf20Sopenharmony_ci if (!tmp_ptr) 29138c2ecf20Sopenharmony_ci goto done; 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci memcpy(tmp_ptr, entry->mem_ptr, 29168c2ecf20Sopenharmony_ci entry->mem_size); 29178c2ecf20Sopenharmony_ci vfree(entry->mem_ptr); 29188c2ecf20Sopenharmony_ci entry->mem_ptr = tmp_ptr; 29198c2ecf20Sopenharmony_ci tmp_ptr = NULL; 29208c2ecf20Sopenharmony_ci dbg_ptr = entry->mem_ptr + entry->mem_size; 29218c2ecf20Sopenharmony_ci entry->mem_size += 0x4000; 29228c2ecf20Sopenharmony_ci end_ptr = entry->mem_ptr + entry->mem_size; 29238c2ecf20Sopenharmony_ci } 29248c2ecf20Sopenharmony_ci } 29258c2ecf20Sopenharmony_ci if (stat == RDWR_STATUS_DONE) { 29268c2ecf20Sopenharmony_ci entry->mem_size = dbg_ptr - entry->mem_ptr; 29278c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n", 29288c2ecf20Sopenharmony_ci entry->mem_name, entry->mem_size); 29298c2ecf20Sopenharmony_ci ret = 0; 29308c2ecf20Sopenharmony_ci break; 29318c2ecf20Sopenharmony_ci } 29328c2ecf20Sopenharmony_ci } 29338c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); 29348c2ecf20Sopenharmony_ci 29358c2ecf20Sopenharmony_cidone: 29368c2ecf20Sopenharmony_ci if (ret) { 29378c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, "firmware dump failed\n"); 29388c2ecf20Sopenharmony_ci if (entry->mem_ptr) { 29398c2ecf20Sopenharmony_ci vfree(entry->mem_ptr); 29408c2ecf20Sopenharmony_ci entry->mem_ptr = NULL; 29418c2ecf20Sopenharmony_ci } 29428c2ecf20Sopenharmony_ci entry->mem_size = 0; 29438c2ecf20Sopenharmony_ci } 29448c2ecf20Sopenharmony_ci sdio_release_host(card->func); 29458c2ecf20Sopenharmony_ci} 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_cistatic void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter) 29488c2ecf20Sopenharmony_ci{ 29498c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci adapter->devdump_data = vzalloc(MWIFIEX_FW_DUMP_SIZE); 29528c2ecf20Sopenharmony_ci if (!adapter->devdump_data) { 29538c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, ERROR, 29548c2ecf20Sopenharmony_ci "vzalloc devdump data failure!\n"); 29558c2ecf20Sopenharmony_ci return; 29568c2ecf20Sopenharmony_ci } 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_ci mwifiex_drv_info_dump(adapter); 29598c2ecf20Sopenharmony_ci if (card->fw_dump_enh) 29608c2ecf20Sopenharmony_ci mwifiex_sdio_generic_fw_dump(adapter); 29618c2ecf20Sopenharmony_ci else 29628c2ecf20Sopenharmony_ci mwifiex_sdio_fw_dump(adapter); 29638c2ecf20Sopenharmony_ci mwifiex_prepare_fw_dump_info(adapter); 29648c2ecf20Sopenharmony_ci mwifiex_upload_device_dump(adapter); 29658c2ecf20Sopenharmony_ci} 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_cistatic void mwifiex_sdio_work(struct work_struct *work) 29688c2ecf20Sopenharmony_ci{ 29698c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = 29708c2ecf20Sopenharmony_ci container_of(work, struct sdio_mmc_card, work); 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_ci if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 29738c2ecf20Sopenharmony_ci &card->work_flags)) 29748c2ecf20Sopenharmony_ci mwifiex_sdio_device_dump_work(card->adapter); 29758c2ecf20Sopenharmony_ci if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, 29768c2ecf20Sopenharmony_ci &card->work_flags)) 29778c2ecf20Sopenharmony_ci mwifiex_sdio_card_reset_work(card->adapter); 29788c2ecf20Sopenharmony_ci} 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_ci/* This function resets the card */ 29818c2ecf20Sopenharmony_cistatic void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) 29828c2ecf20Sopenharmony_ci{ 29838c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 29848c2ecf20Sopenharmony_ci 29858c2ecf20Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags)) 29868c2ecf20Sopenharmony_ci schedule_work(&card->work); 29878c2ecf20Sopenharmony_ci} 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci/* This function dumps FW information */ 29908c2ecf20Sopenharmony_cistatic void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter) 29918c2ecf20Sopenharmony_ci{ 29928c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci if (!test_and_set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, 29958c2ecf20Sopenharmony_ci &card->work_flags)) 29968c2ecf20Sopenharmony_ci schedule_work(&card->work); 29978c2ecf20Sopenharmony_ci} 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci/* Function to dump SDIO function registers and SDIO scratch registers in case 30008c2ecf20Sopenharmony_ci * of FW crash 30018c2ecf20Sopenharmony_ci */ 30028c2ecf20Sopenharmony_cistatic int 30038c2ecf20Sopenharmony_cimwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) 30048c2ecf20Sopenharmony_ci{ 30058c2ecf20Sopenharmony_ci char *p = drv_buf; 30068c2ecf20Sopenharmony_ci struct sdio_mmc_card *cardp = adapter->card; 30078c2ecf20Sopenharmony_ci int ret = 0; 30088c2ecf20Sopenharmony_ci u8 count, func, data, index = 0, size = 0; 30098c2ecf20Sopenharmony_ci u8 reg, reg_start, reg_end; 30108c2ecf20Sopenharmony_ci char buf[256], *ptr; 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci if (!p) 30138c2ecf20Sopenharmony_ci return 0; 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "SDIO register dump start\n"); 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci mwifiex_pm_wakeup_card(adapter); 30188c2ecf20Sopenharmony_ci 30198c2ecf20Sopenharmony_ci sdio_claim_host(cardp->func); 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci for (count = 0; count < 5; count++) { 30228c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 30238c2ecf20Sopenharmony_ci ptr = buf; 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci switch (count) { 30268c2ecf20Sopenharmony_ci case 0: 30278c2ecf20Sopenharmony_ci /* Read the registers of SDIO function0 */ 30288c2ecf20Sopenharmony_ci func = count; 30298c2ecf20Sopenharmony_ci reg_start = 0; 30308c2ecf20Sopenharmony_ci reg_end = 9; 30318c2ecf20Sopenharmony_ci break; 30328c2ecf20Sopenharmony_ci case 1: 30338c2ecf20Sopenharmony_ci /* Read the registers of SDIO function1 */ 30348c2ecf20Sopenharmony_ci func = count; 30358c2ecf20Sopenharmony_ci reg_start = cardp->reg->func1_dump_reg_start; 30368c2ecf20Sopenharmony_ci reg_end = cardp->reg->func1_dump_reg_end; 30378c2ecf20Sopenharmony_ci break; 30388c2ecf20Sopenharmony_ci case 2: 30398c2ecf20Sopenharmony_ci index = 0; 30408c2ecf20Sopenharmony_ci func = 1; 30418c2ecf20Sopenharmony_ci reg_start = cardp->reg->func1_spec_reg_table[index++]; 30428c2ecf20Sopenharmony_ci size = cardp->reg->func1_spec_reg_num; 30438c2ecf20Sopenharmony_ci reg_end = cardp->reg->func1_spec_reg_table[size-1]; 30448c2ecf20Sopenharmony_ci break; 30458c2ecf20Sopenharmony_ci default: 30468c2ecf20Sopenharmony_ci /* Read the scratch registers of SDIO function1 */ 30478c2ecf20Sopenharmony_ci if (count == 4) 30488c2ecf20Sopenharmony_ci mdelay(100); 30498c2ecf20Sopenharmony_ci func = 1; 30508c2ecf20Sopenharmony_ci reg_start = cardp->reg->func1_scratch_reg; 30518c2ecf20Sopenharmony_ci reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; 30528c2ecf20Sopenharmony_ci } 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci if (count != 2) 30558c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", 30568c2ecf20Sopenharmony_ci func, reg_start, reg_end); 30578c2ecf20Sopenharmony_ci else 30588c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "SDIO Func%d: ", func); 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_ci for (reg = reg_start; reg <= reg_end;) { 30618c2ecf20Sopenharmony_ci if (func == 0) 30628c2ecf20Sopenharmony_ci data = sdio_f0_readb(cardp->func, reg, &ret); 30638c2ecf20Sopenharmony_ci else 30648c2ecf20Sopenharmony_ci data = sdio_readb(cardp->func, reg, &ret); 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci if (count == 2) 30678c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "(%#x) ", reg); 30688c2ecf20Sopenharmony_ci if (!ret) { 30698c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "%02x ", data); 30708c2ecf20Sopenharmony_ci } else { 30718c2ecf20Sopenharmony_ci ptr += sprintf(ptr, "ERR"); 30728c2ecf20Sopenharmony_ci break; 30738c2ecf20Sopenharmony_ci } 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci if (count == 2 && reg < reg_end) 30768c2ecf20Sopenharmony_ci reg = cardp->reg->func1_spec_reg_table[index++]; 30778c2ecf20Sopenharmony_ci else 30788c2ecf20Sopenharmony_ci reg++; 30798c2ecf20Sopenharmony_ci } 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "%s\n", buf); 30828c2ecf20Sopenharmony_ci p += sprintf(p, "%s\n", buf); 30838c2ecf20Sopenharmony_ci } 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci sdio_release_host(cardp->func); 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci mwifiex_dbg(adapter, MSG, "SDIO register dump end\n"); 30888c2ecf20Sopenharmony_ci 30898c2ecf20Sopenharmony_ci return p - drv_buf; 30908c2ecf20Sopenharmony_ci} 30918c2ecf20Sopenharmony_ci 30928c2ecf20Sopenharmony_ci/* sdio device/function initialization, code is extracted 30938c2ecf20Sopenharmony_ci * from init_if handler and register_dev handler. 30948c2ecf20Sopenharmony_ci */ 30958c2ecf20Sopenharmony_cistatic void mwifiex_sdio_up_dev(struct mwifiex_adapter *adapter) 30968c2ecf20Sopenharmony_ci{ 30978c2ecf20Sopenharmony_ci struct sdio_mmc_card *card = adapter->card; 30988c2ecf20Sopenharmony_ci u8 sdio_ireg; 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci sdio_claim_host(card->func); 31018c2ecf20Sopenharmony_ci sdio_enable_func(card->func); 31028c2ecf20Sopenharmony_ci sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); 31038c2ecf20Sopenharmony_ci sdio_release_host(card->func); 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci /* tx_buf_size might be changed to 3584 by firmware during 31068c2ecf20Sopenharmony_ci * data transfer, we will reset to default size. 31078c2ecf20Sopenharmony_ci */ 31088c2ecf20Sopenharmony_ci adapter->tx_buf_size = card->tx_buf_size; 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci /* Read the host_int_status_reg for ACK the first interrupt got 31118c2ecf20Sopenharmony_ci * from the bootloader. If we don't do this we get a interrupt 31128c2ecf20Sopenharmony_ci * as soon as we register the irq. 31138c2ecf20Sopenharmony_ci */ 31148c2ecf20Sopenharmony_ci mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); 31158c2ecf20Sopenharmony_ci 31168c2ecf20Sopenharmony_ci mwifiex_init_sdio_ioport(adapter); 31178c2ecf20Sopenharmony_ci} 31188c2ecf20Sopenharmony_ci 31198c2ecf20Sopenharmony_cistatic struct mwifiex_if_ops sdio_ops = { 31208c2ecf20Sopenharmony_ci .init_if = mwifiex_init_sdio, 31218c2ecf20Sopenharmony_ci .cleanup_if = mwifiex_cleanup_sdio, 31228c2ecf20Sopenharmony_ci .check_fw_status = mwifiex_check_fw_status, 31238c2ecf20Sopenharmony_ci .check_winner_status = mwifiex_check_winner_status, 31248c2ecf20Sopenharmony_ci .prog_fw = mwifiex_prog_fw_w_helper, 31258c2ecf20Sopenharmony_ci .register_dev = mwifiex_register_dev, 31268c2ecf20Sopenharmony_ci .unregister_dev = mwifiex_unregister_dev, 31278c2ecf20Sopenharmony_ci .enable_int = mwifiex_sdio_enable_host_int, 31288c2ecf20Sopenharmony_ci .disable_int = mwifiex_sdio_disable_host_int, 31298c2ecf20Sopenharmony_ci .process_int_status = mwifiex_process_int_status, 31308c2ecf20Sopenharmony_ci .host_to_card = mwifiex_sdio_host_to_card, 31318c2ecf20Sopenharmony_ci .wakeup = mwifiex_pm_wakeup_card, 31328c2ecf20Sopenharmony_ci .wakeup_complete = mwifiex_pm_wakeup_card_complete, 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci /* SDIO specific */ 31358c2ecf20Sopenharmony_ci .update_mp_end_port = mwifiex_update_mp_end_port, 31368c2ecf20Sopenharmony_ci .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, 31378c2ecf20Sopenharmony_ci .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, 31388c2ecf20Sopenharmony_ci .event_complete = mwifiex_sdio_event_complete, 31398c2ecf20Sopenharmony_ci .dnld_fw = mwifiex_sdio_dnld_fw, 31408c2ecf20Sopenharmony_ci .card_reset = mwifiex_sdio_card_reset, 31418c2ecf20Sopenharmony_ci .reg_dump = mwifiex_sdio_reg_dump, 31428c2ecf20Sopenharmony_ci .device_dump = mwifiex_sdio_device_dump, 31438c2ecf20Sopenharmony_ci .deaggr_pkt = mwifiex_deaggr_sdio_pkt, 31448c2ecf20Sopenharmony_ci .up_dev = mwifiex_sdio_up_dev, 31458c2ecf20Sopenharmony_ci}; 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_cimodule_driver(mwifiex_sdio, sdio_register_driver, sdio_unregister_driver); 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 31508c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); 31518c2ecf20Sopenharmony_ciMODULE_VERSION(SDIO_VERSION); 31528c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 31538c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); 31548c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); 31558c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); 31568c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); 31578c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME); 31588c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8977_DEFAULT_FW_NAME); 31598c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8987_DEFAULT_FW_NAME); 31608c2ecf20Sopenharmony_ciMODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME); 3161