/** @file mlan_sdio.c * * @brief This file contains SDIO specific code * * * Copyright 2008-2021 NXP * * This software file (the File) is distributed by NXP * under the terms of the GNU General Public License Version 2, June 1991 * (the License). You may use, redistribute and/or modify the File in * accordance with the terms and conditions of the License, a copy of which * is available by writing to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE * ARE EXPRESSLY DISCLAIMED. The License provides additional details about * this warranty disclaimer. * */ /******************************************************** Change log: 10/27/2008: initial version ********************************************************/ #include "mlan.h" #ifdef STA_SUPPORT #include "mlan_join.h" #endif #include "mlan_util.h" #include "mlan_fw.h" #include "mlan_main.h" #include "mlan_init.h" #include "mlan_wmm.h" #include "mlan_11n.h" #include "mlan_sdio.h" /******************************************************** Local Variables ********************************************************/ #ifdef SD8887 static const struct _mlan_sdio_card_reg mlan_reg_sd8887 = { .start_rd_port = 0, .start_wr_port = 0, .base_0_reg = 0x6C, .base_1_reg = 0x6D, .poll_reg = 0x5C, .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, .host_int_status = DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS | DN_LD_CMD_PORT_HOST_INT_STATUS | UP_LD_CMD_PORT_HOST_INT_STATUS, .status_reg_0 = 0x90, .status_reg_1 = 0x91, .sdio_int_mask = 0xff, .data_port_mask = 0xffffffff, .max_mp_regs = 196, .rd_bitmap_l = 0x10, .rd_bitmap_u = 0x11, .rd_bitmap_1l = 0x12, .rd_bitmap_1u = 0x13, .wr_bitmap_l = 0x14, .wr_bitmap_u = 0x15, .wr_bitmap_1l = 0x16, .wr_bitmap_1u = 0x17, .rd_len_p0_l = 0x18, .rd_len_p0_u = 0x19, .card_config_2_1_reg = 0xD9, .cmd_config_0 = 0xC4, .cmd_config_1 = 0xC5, .cmd_config_2 = 0xC6, .cmd_config_3 = 0xC7, .cmd_rd_len_0 = 0xC0, .cmd_rd_len_1 = 0xC1, .cmd_rd_len_2 = 0xC2, .cmd_rd_len_3 = 0xC3, .io_port_0_reg = 0xE4, .io_port_1_reg = 0xE5, .io_port_2_reg = 0xE6, .host_int_rsr_reg = 0x04, .host_int_mask_reg = 0x08, .host_int_status_reg = 0x0C, .host_restart_reg = 0x58, .card_to_host_event_reg = 0x5C, .host_interrupt_mask_reg = 0x60, .card_interrupt_status_reg = 0x64, .card_interrupt_rsr_reg = 0x68, .card_revision_reg = 0xC8, .card_ocr_0_reg = 0xD4, .card_ocr_1_reg = 0xD5, .card_ocr_3_reg = 0xD6, .card_config_reg = 0xD7, .card_misc_cfg_reg = 0xD8, .debug_0_reg = 0xDC, .debug_1_reg = 0xDD, .debug_2_reg = 0xDE, .debug_3_reg = 0xDF, .fw_reset_reg = 0x0B6, .fw_reset_val = 1, .winner_check_reg = 0x90, }; static const struct _mlan_card_info mlan_card_info_sd8887 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, .v16_fw_api = 0, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, }; #endif #ifdef SD8801 static const struct _mlan_sdio_card_reg mlan_reg_sd8801 = { .start_rd_port = 1, .start_wr_port = 1, .base_0_reg = 0x40, .base_1_reg = 0x41, .poll_reg = 0x30, .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, .host_int_status = DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS, .status_reg_0 = 0x60, .status_reg_1 = 0x61, .sdio_int_mask = 0x3f, .data_port_mask = 0x0000fffe, .max_mp_regs = 64, .rd_bitmap_l = 0x4, .rd_bitmap_u = 0x5, .wr_bitmap_l = 0x6, .wr_bitmap_u = 0x7, .rd_len_p0_l = 0x8, .rd_len_p0_u = 0x9, .io_port_0_reg = 0x78, .io_port_1_reg = 0x79, .io_port_2_reg = 0x7A, .host_int_rsr_reg = 0x01, .host_int_mask_reg = 0x02, .host_int_status_reg = 0x03, .card_misc_cfg_reg = 0x6c, .fw_reset_reg = 0x64, .fw_reset_val = 0, }; static const struct _mlan_card_info mlan_card_info_sd8801 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, .v14_fw_api = 1, .v16_fw_api = 0, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, }; #endif #ifdef SD8897 static const struct _mlan_sdio_card_reg mlan_reg_sd8897 = { .start_rd_port = 0, .start_wr_port = 0, .base_0_reg = 0x60, .base_1_reg = 0x61, .poll_reg = 0x50, .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, .host_int_status = DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS | DN_LD_CMD_PORT_HOST_INT_STATUS | UP_LD_CMD_PORT_HOST_INT_STATUS, .status_reg_0 = 0xC0, .status_reg_1 = 0xC1, .sdio_int_mask = 0xff, .data_port_mask = 0xffffffff, .max_mp_regs = 184, .rd_bitmap_l = 0x04, .rd_bitmap_u = 0x05, .rd_bitmap_1l = 0x06, .rd_bitmap_1u = 0x07, .wr_bitmap_l = 0x08, .wr_bitmap_u = 0x09, .wr_bitmap_1l = 0x0A, .wr_bitmap_1u = 0x0B, .rd_len_p0_l = 0x0C, .rd_len_p0_u = 0x0D, .card_config_2_1_reg = 0xCD, .cmd_config_0 = 0xB8, .cmd_config_1 = 0xB9, .cmd_config_2 = 0xBA, .cmd_config_3 = 0xBB, .cmd_rd_len_0 = 0xB4, .cmd_rd_len_1 = 0xB5, .cmd_rd_len_2 = 0xB6, .cmd_rd_len_3 = 0xB7, .io_port_0_reg = 0xD8, .io_port_1_reg = 0xD9, .io_port_2_reg = 0xDA, .host_int_rsr_reg = 0x01, .host_int_mask_reg = 0x02, .host_int_status_reg = 0x03, .host_restart_reg = 0x4C, .card_to_host_event_reg = 0x50, .host_interrupt_mask_reg = 0x54, .card_interrupt_status_reg = 0x58, .card_interrupt_rsr_reg = 0x5C, .card_revision_reg = 0xBC, .card_ocr_0_reg = 0xC8, .card_ocr_1_reg = 0xC9, .card_ocr_3_reg = 0xCA, .card_config_reg = 0xCB, .card_misc_cfg_reg = 0xCC, .debug_0_reg = 0xD0, .debug_1_reg = 0xD1, .debug_2_reg = 0xD2, .debug_3_reg = 0xD3, .fw_reset_reg = 0x0E8, .fw_reset_val = 1, .winner_check_reg = 0xC0, }; static const struct _mlan_card_info mlan_card_info_sd8897 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, .v16_fw_api = 0, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, }; #endif #if defined(SD8977) || defined(SD8997) || defined(SD8987) || \ defined(SD9098) || defined(SD9097) || defined(SD8978) || \ defined(SD9177) static const struct _mlan_sdio_card_reg mlan_reg_sd8977_sd8997 = { .start_rd_port = 0, .start_wr_port = 0, .base_0_reg = 0xf8, .base_1_reg = 0xf9, .poll_reg = 0x5C, .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, .host_int_status = DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS | DN_LD_CMD_PORT_HOST_INT_STATUS | UP_LD_CMD_PORT_HOST_INT_STATUS, .status_reg_0 = 0xe8, .status_reg_1 = 0xe9, .sdio_int_mask = 0xff, .data_port_mask = 0xffffffff, .max_mp_regs = 196, .rd_bitmap_l = 0x10, .rd_bitmap_u = 0x11, .rd_bitmap_1l = 0x12, .rd_bitmap_1u = 0x13, .wr_bitmap_l = 0x14, .wr_bitmap_u = 0x15, .wr_bitmap_1l = 0x16, .wr_bitmap_1u = 0x17, .rd_len_p0_l = 0x18, .rd_len_p0_u = 0x19, .card_config_2_1_reg = 0xD9, .cmd_config_0 = 0xC4, .cmd_config_1 = 0xC5, .cmd_config_2 = 0xC6, .cmd_config_3 = 0xC7, .cmd_rd_len_0 = 0xC0, .cmd_rd_len_1 = 0xC1, .cmd_rd_len_2 = 0xC2, .cmd_rd_len_3 = 0xC3, .io_port_0_reg = 0xE4, .io_port_1_reg = 0xE5, .io_port_2_reg = 0xE6, .host_int_rsr_reg = 0x04, .host_int_mask_reg = 0x08, .host_int_status_reg = 0x0C, .host_restart_reg = 0x58, .card_to_host_event_reg = 0x5C, .host_interrupt_mask_reg = 0x60, .card_interrupt_status_reg = 0x64, .card_interrupt_rsr_reg = 0x68, .card_revision_reg = 0xC8, .card_ocr_0_reg = 0xD4, .card_ocr_1_reg = 0xD5, .card_ocr_3_reg = 0xD6, .card_config_reg = 0xD7, .card_misc_cfg_reg = 0xD8, .debug_0_reg = 0xDC, .debug_1_reg = 0xDD, .debug_2_reg = 0xDE, .debug_3_reg = 0xDF, .fw_reset_reg = 0x0EE, .fw_reset_val = 0x99, .fw_dnld_offset_0_reg = 0xEC, .fw_dnld_offset_1_reg = 0xED, .fw_dnld_offset_2_reg = 0xEE, .fw_dnld_offset_3_reg = 0xEF, .fw_dnld_status_0_reg = 0xE8, .fw_dnld_status_1_reg = 0xE9, .winner_check_reg = 0xFC, }; #endif #ifdef SD8997 static const struct _mlan_card_info mlan_card_info_sd8997 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, .v16_fw_api = 1, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, }; #endif #ifdef SD9097 static const struct _mlan_card_info mlan_card_info_sd9097 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, .v16_fw_api = 1, .v17_fw_api = 1, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, }; #endif #ifdef SD9098 static const struct _mlan_card_info mlan_card_info_sd9098 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, .v16_fw_api = 1, .v17_fw_api = 1, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, }; #endif #ifdef SD9177 static const struct _mlan_card_info mlan_card_info_sd9177 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, .v16_fw_api = 1, .v17_fw_api = 1, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, }; #endif #if defined(SD8977) || defined(SD8978) static const struct _mlan_card_info mlan_card_info_sd8977 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, .v16_fw_api = 1, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, }; #endif #ifdef SD8987 static const struct _mlan_card_info mlan_card_info_sd8987 = { .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, .v16_fw_api = 1, .supp_ps_handshake = 0, .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, }; #endif /******************************************************** Global Variables ********************************************************/ /******************************************************** Local Functions ********************************************************/ /** * @brief This function initialize the SDIO port * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_init_ioport(mlan_adapter *pmadapter) { t_u32 reg; pmlan_callbacks pcb = &pmadapter->callbacks; t_u8 host_int_rsr_reg = pmadapter->pcard_sd->reg->host_int_rsr_reg; t_u8 host_int_rsr_mask = pmadapter->pcard_sd->reg->sdio_int_mask; t_u8 card_misc_cfg_reg = pmadapter->pcard_sd->reg->card_misc_cfg_reg; t_u8 card_config_2_1_reg = pmadapter->pcard_sd->reg->card_config_2_1_reg; t_u8 cmd_config_0 = pmadapter->pcard_sd->reg->cmd_config_0; t_u8 cmd_config_1 = pmadapter->pcard_sd->reg->cmd_config_1; ENTER(); if (pmadapter->pcard_sd->supports_sdio_new_mode) { pmadapter->pcard_sd->ioport = MEM_PORT; } else { if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->io_port_0_reg, ®)) pmadapter->pcard_sd->ioport |= (reg & 0xff); else { LEAVE(); return MLAN_STATUS_FAILURE; } if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->io_port_1_reg, ®)) pmadapter->pcard_sd->ioport |= ((reg & 0xff) << 8); else { LEAVE(); return MLAN_STATUS_FAILURE; } if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->io_port_2_reg, ®)) pmadapter->pcard_sd->ioport |= ((reg & 0xff) << 16); else { LEAVE(); return MLAN_STATUS_FAILURE; } } PRINTM(MINFO, "SDIO FUNC1 IO port: 0x%x\n", pmadapter->pcard_sd->ioport); if (pmadapter->pcard_sd->supports_sdio_new_mode) { /* enable sdio cmd53 new mode */ if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, card_config_2_1_reg, ®)) { pcb->moal_write_reg(pmadapter->pmoal_handle, card_config_2_1_reg, reg | CMD53_NEW_MODE); } else { LEAVE(); return MLAN_STATUS_FAILURE; } /* configure cmd port */ /* enable reading rx length from the register */ if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, cmd_config_0, ®)) { pcb->moal_write_reg(pmadapter->pmoal_handle, cmd_config_0, reg | CMD_PORT_RD_LEN_EN); } else { LEAVE(); return MLAN_STATUS_FAILURE; } /* enable Dnld/Upld ready auto reset for cmd port * after cmd53 is completed */ if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, cmd_config_1, ®)) { pcb->moal_write_reg(pmadapter->pmoal_handle, cmd_config_1, reg | CMD_PORT_AUTO_EN); } else { LEAVE(); return MLAN_STATUS_FAILURE; } } #if defined(SD8977) || defined(SD8978) if (IS_SD8977(pmadapter->card_type) || IS_SD8978(pmadapter->card_type)) { if ((pmadapter->init_para.int_mode == INT_MODE_GPIO) && (pmadapter->init_para.gpio_pin == GPIO_INT_NEW_MODE)) { PRINTM(MMSG, "Enable GPIO-1 int mode\n"); pcb->moal_write_reg(pmadapter->pmoal_handle, SCRATCH_REG_32, ENABLE_GPIO_1_INT_MODE); } } #endif /* Set Host interrupt reset to read to clear */ if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, host_int_rsr_reg, ®)) { pcb->moal_write_reg(pmadapter->pmoal_handle, host_int_rsr_reg, reg | host_int_rsr_mask); } else { LEAVE(); return MLAN_STATUS_FAILURE; } /* Dnld/Upld ready set to auto reset */ if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, card_misc_cfg_reg, ®)) { pcb->moal_write_reg(pmadapter->pmoal_handle, card_misc_cfg_reg, reg | AUTO_RE_ENABLE_INT); } else { LEAVE(); return MLAN_STATUS_FAILURE; } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function sends data to the card. * * @param pmadapter A pointer to mlan_adapter structure * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include * SDIO header) * @param port Port * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_write_data_sync(mlan_adapter *pmadapter, mlan_buffer *pmbuf, t_u32 port) { t_u32 i = 0; pmlan_callbacks pcb = &pmadapter->callbacks; mlan_status ret = MLAN_STATUS_SUCCESS; ENTER(); do { ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, pmbuf, port, 0); if (ret != MLAN_STATUS_SUCCESS) { i++; PRINTM(MERROR, "host_to_card, write iomem (%d) failed: %d\n", i, ret); if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, HOST_TERM_CMD53)) { PRINTM(MERROR, "write CFG reg failed\n"); } ret = MLAN_STATUS_FAILURE; if (i > MAX_WRITE_IOMEM_RETRY) { pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL; goto exit; } } } while (ret == MLAN_STATUS_FAILURE); exit: LEAVE(); return ret; } /** * @brief This function gets available SDIO port for reading cmd/data * * @param pmadapter A pointer to mlan_adapter structure * @param pport A pointer to port number * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_get_rd_port(mlan_adapter *pmadapter, t_u8 *pport) { t_u32 rd_bitmap = pmadapter->pcard_sd->mp_rd_bitmap; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; t_u8 max_ports = pmadapter->pcard_sd->max_ports; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; ENTER(); PRINTM(MIF_D, "wlan_get_rd_port: mp_rd_bitmap=0x%08x\n", rd_bitmap); if (new_mode) { if (!(rd_bitmap & reg->data_port_mask)) { LEAVE(); return MLAN_STATUS_FAILURE; } } else { if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) { LEAVE(); return MLAN_STATUS_FAILURE; } } if (!new_mode && (pmadapter->pcard_sd->mp_rd_bitmap & CTRL_PORT_MASK)) { pmadapter->pcard_sd->mp_rd_bitmap &= (t_u32)(~CTRL_PORT_MASK); *pport = CTRL_PORT; PRINTM(MIF_D, "wlan_get_rd_port: port=%d mp_rd_bitmap=0x%08x\n", *pport, pmadapter->pcard_sd->mp_rd_bitmap); } else { if (pmadapter->pcard_sd->mp_rd_bitmap & (1 << pmadapter->pcard_sd->curr_rd_port)) { pmadapter->pcard_sd->mp_rd_bitmap &= (t_u32)( ~(1 << pmadapter->pcard_sd->curr_rd_port)); *pport = pmadapter->pcard_sd->curr_rd_port; /* hw rx wraps round only after port (MAX_PORT-1) */ if (++pmadapter->pcard_sd->curr_rd_port == max_ports) pmadapter->pcard_sd->curr_rd_port = reg->start_rd_port; } else { LEAVE(); return MLAN_STATUS_FAILURE; } PRINTM(MIF_D, "port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", *pport, rd_bitmap, pmadapter->pcard_sd->mp_rd_bitmap); } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function gets available SDIO port for writing data * * @param pmadapter A pointer to mlan_adapter structure * @param pport A pointer to port number * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_get_wr_port_data(mlan_adapter *pmadapter, t_u8 *pport) { t_u32 wr_bitmap = pmadapter->pcard_sd->mp_wr_bitmap; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; ENTER(); PRINTM(MIF_D, "wlan_get_wr_port_data: mp_wr_bitmap=0x%08x\n", wr_bitmap); if (!(wr_bitmap & pmadapter->pcard_sd->mp_data_port_mask)) { pmadapter->data_sent = MTRUE; LEAVE(); return MLAN_STATUS_RESOURCE; } if (pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port)) { pmadapter->pcard_sd->mp_wr_bitmap &= (t_u32)(~(1 << pmadapter->pcard_sd->curr_wr_port)); *pport = pmadapter->pcard_sd->curr_wr_port; if (++pmadapter->pcard_sd->curr_wr_port == pmadapter->pcard_sd->mp_end_port) pmadapter->pcard_sd->curr_wr_port = reg->start_wr_port; } else { pmadapter->data_sent = MTRUE; LEAVE(); return MLAN_STATUS_RESOURCE; } if ((!new_mode) && (*pport == CTRL_PORT)) { PRINTM(MERROR, "Invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *pport, pmadapter->pcard_sd->curr_wr_port, wr_bitmap, pmadapter->pcard_sd->mp_wr_bitmap); LEAVE(); return MLAN_STATUS_FAILURE; } PRINTM(MIF_D, "port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *pport, wr_bitmap, pmadapter->pcard_sd->mp_wr_bitmap); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function polls the card status register. * * @param pmadapter A pointer to mlan_adapter structure * @param bits the bit mask * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_poll_card_status(mlan_adapter *pmadapter, t_u8 bits) { pmlan_callbacks pcb = &pmadapter->callbacks; t_u32 tries; t_u32 cs = 0; ENTER(); for (tries = 0; tries < 10000; tries++) { if (pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->poll_reg, &cs) != MLAN_STATUS_SUCCESS) break; else if ((cs & bits) == bits) { LEAVE(); return MLAN_STATUS_SUCCESS; } wlan_udelay(pmadapter, 10); } PRINTM(MERROR, "wlan_sdio_poll_card_status failed, tries = %d, cs = 0x%x\n", tries, cs); LEAVE(); return MLAN_STATUS_FAILURE; } /** * @brief This function reads firmware status registers * * @param pmadapter A pointer to mlan_adapter structure * @param dat A pointer to keep returned data * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_read_fw_status(mlan_adapter *pmadapter, t_u16 *dat) { pmlan_callbacks pcb = &pmadapter->callbacks; t_u32 fws0 = 0, fws1 = 0; ENTER(); if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->status_reg_0, &fws0)) { LEAVE(); return MLAN_STATUS_FAILURE; } if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->status_reg_1, &fws1)) { LEAVE(); return MLAN_STATUS_FAILURE; } *dat = (t_u16)((fws1 << 8) | fws0); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function reads firmware dnld offset registers * * @param pmadapter A pointer to mlan_adapter structure * @param dat A pointer to keep returned data * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_read_fw_dnld_offset(mlan_adapter *pmadapter, t_u32 *dat) { pmlan_callbacks pcb = &pmadapter->callbacks; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 fw_dnld_offset_0 = 0; t_u32 fw_dnld_offset_1 = 0; t_u32 fw_dnld_offset_2 = 0; t_u32 fw_dnld_offset_3 = 0; ENTER(); ret = pcb->moal_read_reg(pmadapter->pmoal_handle, reg->fw_dnld_offset_0_reg, &fw_dnld_offset_0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev fw_dnld_offset_0 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", reg->fw_dnld_offset_0_reg, fw_dnld_offset_0); ret = MLAN_STATUS_FAILURE; goto done; } ret = pcb->moal_read_reg(pmadapter->pmoal_handle, reg->fw_dnld_offset_1_reg, &fw_dnld_offset_1); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev fw_dnld_offset_1 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", reg->fw_dnld_offset_1_reg, fw_dnld_offset_1); ret = MLAN_STATUS_FAILURE; goto done; } ret = pcb->moal_read_reg(pmadapter->pmoal_handle, reg->fw_dnld_offset_2_reg, &fw_dnld_offset_2); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev fw_dnld_offset_2 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", reg->fw_dnld_offset_2_reg, fw_dnld_offset_2); ret = MLAN_STATUS_FAILURE; goto done; } ret = pcb->moal_read_reg(pmadapter->pmoal_handle, reg->fw_dnld_offset_3_reg, &fw_dnld_offset_3); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev fw_dnld_offset_3 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", reg->fw_dnld_offset_3_reg, fw_dnld_offset_3); ret = MLAN_STATUS_FAILURE; goto done; } *dat = (t_u32)(((fw_dnld_offset_3 & 0xff) << 24) | ((fw_dnld_offset_2 & 0xff) << 16) | ((fw_dnld_offset_1 & 0xff) << 8) | (fw_dnld_offset_0 & 0xff)); done: LEAVE(); return ret; } /** * @brief This function reads firmware dnld status registers * * @param pmadapter A pointer to mlan_adapter structure * @param dat A pointer to keep returned data * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_read_fw_dnld_status(mlan_adapter *pmadapter, t_u16 *dat) { pmlan_callbacks pcb = &pmadapter->callbacks; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 fw_dnld_status_0 = 0; t_u32 fw_dnld_status_1 = 0; ENTER(); ret = pcb->moal_read_reg(pmadapter->pmoal_handle, reg->fw_dnld_status_0_reg, &fw_dnld_status_0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev fw_dnld_status_0 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", reg->fw_dnld_status_0_reg, fw_dnld_status_0); ret = MLAN_STATUS_FAILURE; goto done; } ret = pcb->moal_read_reg(pmadapter->pmoal_handle, reg->fw_dnld_status_1_reg, &fw_dnld_status_1); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev fw_dnld_status_1 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", reg->fw_dnld_status_1_reg, fw_dnld_status_1); ret = MLAN_STATUS_FAILURE; goto done; } *dat = (t_u16)(((fw_dnld_status_1 & 0xff) << 8) | (fw_dnld_status_0 & 0xff)); done: LEAVE(); return ret; } /** @brief This function disables the host interrupts mask. * * @param pmadapter A pointer to mlan_adapter structure * @param mask the interrupt mask * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_disable_host_int_mask(pmlan_adapter pmadapter, t_u8 mask) { t_u32 host_int_mask = 0; pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); /* Read back the host_int_mask register */ if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->host_int_mask_reg, &host_int_mask)) { LEAVE(); return MLAN_STATUS_FAILURE; } /* Update with the mask and write back to the register */ host_int_mask &= ~mask; if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->host_int_mask_reg, host_int_mask)) { PRINTM(MWARN, "Disable host interrupt failed\n"); LEAVE(); return MLAN_STATUS_FAILURE; } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function enables the host interrupts mask * * @param pmadapter A pointer to mlan_adapter structure * @param mask the interrupt mask * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_enable_host_int_mask(pmlan_adapter pmadapter, t_u8 mask) { pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); /* Simply write the mask to the register */ if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->host_int_mask_reg, mask)) { PRINTM(MWARN, "Enable host interrupt failed\n"); LEAVE(); return MLAN_STATUS_FAILURE; } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function reads data from the card. * * @param pmadapter A pointer to mlan_adapter structure * @param type A pointer to keep type as data or command * @param nb A pointer to keep the data/cmd length returned in buffer * @param pmbuf A pointer to the SDIO data/cmd buffer * @param npayload the length of data/cmd buffer * @param ioport the SDIO ioport * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_card_to_host(mlan_adapter *pmadapter, t_u32 *type, t_u32 *nb, pmlan_buffer pmbuf, t_u32 npayload, t_u32 ioport) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; t_u32 i = 0; ENTER(); if (!pmbuf) { PRINTM(MWARN, "pmbuf is NULL!\n"); ret = MLAN_STATUS_FAILURE; goto exit; } do { ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, pmbuf, ioport, 0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "wlan: cmd53 read failed: %d ioport=0x%x retry=%d\n", ret, ioport, i); i++; if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, HOST_TERM_CMD53)) { PRINTM(MERROR, "Set Term cmd53 failed\n"); } if (i > MAX_WRITE_IOMEM_RETRY) { pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; ret = MLAN_STATUS_FAILURE; goto exit; } } } while (ret == MLAN_STATUS_FAILURE); *nb = wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + pmbuf->data_offset)); if (*nb > npayload) { PRINTM(MERROR, "invalid packet, *nb=%d, npayload=%d\n", *nb, npayload); pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; ret = MLAN_STATUS_FAILURE; goto exit; } DBG_HEXDUMP(MIF_D, "SDIO Blk Rd", pmbuf->pbuf + pmbuf->data_offset, MIN(*nb, MAX_DATA_DUMP_LEN)); *type = wlan_le16_to_cpu( *(t_u16 *)(pmbuf->pbuf + pmbuf->data_offset + 2)); exit: LEAVE(); return ret; } /** * @brief This function downloads FW blocks to device * * @param pmadapter A pointer to mlan_adapter * @param firmware A pointer to firmware image * @param firmwarelen firmware len * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_prog_fw_w_helper(pmlan_adapter pmadapter, t_u8 *fw, t_u32 fw_len) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; t_u8 *firmware = fw; t_u32 firmwarelen = fw_len; t_u32 offset = 0; t_u32 base0, base1; t_void *tmpfwbuf = MNULL; t_u32 tmpfwbufsz; t_u8 *fwbuf; mlan_buffer mbuf; t_u16 len = 0; t_u32 txlen = 0, tx_blocks = 0, tries = 0; t_u32 i = 0; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; t_u32 read_base_0_reg = reg->base_0_reg; t_u32 read_base_1_reg = reg->base_1_reg; #if defined(SD9098) t_u32 rev_id_reg = 0; t_u32 revision_id = 0; #endif t_u8 check_fw_status = MFALSE; t_u16 fw_dnld_status = 0; t_u32 fw_dnld_offset = 0; t_u8 mic_retry = 0; ENTER(); if (!firmware && !pcb->moal_get_fw_data) { PRINTM(MMSG, "No firmware image found! Terminating download\n"); LEAVE(); return MLAN_STATUS_FAILURE; } PRINTM(MINFO, "WLAN: Downloading FW image (%d bytes)\n", firmwarelen); tmpfwbufsz = ALIGN_SZ(WLAN_UPLD_SIZE, DMA_ALIGNMENT); ret = pcb->moal_malloc(pmadapter->pmoal_handle, tmpfwbufsz, MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&tmpfwbuf); if ((ret != MLAN_STATUS_SUCCESS) || !tmpfwbuf) { PRINTM(MERROR, "Unable to allocate buffer for firmware. Terminating download\n"); ret = MLAN_STATUS_FAILURE; goto done; } memset(pmadapter, tmpfwbuf, 0, tmpfwbufsz); /* Ensure 8-byte aligned firmware buffer */ fwbuf = (t_u8 *)ALIGN_ADDR(tmpfwbuf, DMA_ALIGNMENT); #if defined(SD9098) if (IS_SD9098(pmadapter->card_type)) { rev_id_reg = pmadapter->pcard_sd->reg->card_revision_reg; ret = pcb->moal_read_reg(pmadapter->pmoal_handle, rev_id_reg, &revision_id); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Card Revision register read failed:" "card_revision_reg=0x%x\n", rev_id_reg); goto done; } /* Skyhawk A0, need to check both CRC and MIC error */ if (revision_id >= CHIP_9098_REV_A0) check_fw_status = MTRUE; } #endif #if defined(SD9097) if (IS_SD9097(pmadapter->card_type)) check_fw_status = MTRUE; #endif #if defined(SD9177) if (IS_SD9177(pmadapter->card_type)) check_fw_status = MTRUE; #endif /* Perform firmware data transfer */ do { /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits */ ret = wlan_sdio_poll_card_status( pmadapter, CARD_IO_READY | DN_LD_CARD_RDY); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MFATAL, "WLAN: FW download with helper poll status timeout @ %d\n", offset); goto done; } /* More data */ if (firmwarelen && offset >= firmwarelen) break; for (tries = 0; tries < MAX_POLL_TRIES; tries++) { ret = pcb->moal_read_reg(pmadapter->pmoal_handle, read_base_0_reg, &base0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev BASE0 register read failed:" " base0=0x%04X(%d). Terminating download\n", base0, base0); goto done; } ret = pcb->moal_read_reg(pmadapter->pmoal_handle, read_base_1_reg, &base1); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "Dev BASE1 register read failed:" " base1=0x%04X(%d). Terminating download\n", base1, base1); goto done; } len = (t_u16)(((base1 & 0xff) << 8) | (base0 & 0xff)); if (len) break; wlan_udelay(pmadapter, 10); } if (!len) break; else if (len > WLAN_UPLD_SIZE) { PRINTM(MFATAL, "WLAN: FW download failure @ %d, invalid length %d\n", offset, len); ret = MLAN_STATUS_FAILURE; goto done; } /* Ignore CRC check before download the 1st packet */ if (offset == 0 && (len & MBIT(0))) len &= ~MBIT(0); txlen = len; if (len & MBIT(0)) { /* New fw download process, check CRC and MIC error */ if (check_fw_status) { /* Get offset from fw dnld offset Register */ ret = wlan_sdio_read_fw_dnld_offset( pmadapter, &fw_dnld_offset); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MFATAL, "WLAN: FW download with helper read fw dnld offset failed @ %d\n", offset); goto done; } /* Get CRC MIC error from fw dnld status * Register */ ret = wlan_sdio_read_fw_dnld_status( pmadapter, &fw_dnld_status); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MFATAL, "WLAN: FW download with helper read fw dnld status failed @ %d\n", offset); goto done; } PRINTM(MERROR, "WLAN: FW download error: status=0x%x offset = 0x%x fw offset = 0x%x\n", fw_dnld_status, offset, fw_dnld_offset); } i++; if (i > MAX_WRITE_IOMEM_RETRY) { PRINTM(MFATAL, "WLAN: FW download failure @ %d, over max retry count\n", offset); ret = MLAN_STATUS_FAILURE; goto done; } PRINTM(MERROR, "WLAN: FW CRC error indicated by the helper:" " len = 0x%04X, txlen = %d\n", len, txlen); len &= ~MBIT(0); if (fw_dnld_status & (MBIT(6) | MBIT(7))) { offset = 0; mic_retry++; if (mic_retry > MAX_FW_RETRY) { PRINTM(MFATAL, "WLAN: FW download failure @ %d, over max mic retry count\n", offset); ret = MLAN_STATUS_FAILURE; goto done; } } PRINTM(MERROR, "WLAN: retry: %d, offset %d\n", i, offset); DBG_HEXDUMP(MERROR, "WLAN: FW block:", fwbuf, len); /* Setting this to 0 to resend from same offset */ txlen = 0; } else { i = 0; /* Set blocksize to transfer - checking * for last block */ if (firmwarelen && firmwarelen - offset < txlen) txlen = firmwarelen - offset; PRINTM(MINFO, "."); tx_blocks = (txlen + MLAN_SDIO_BLOCK_SIZE_FW_DNLD - 1) / MLAN_SDIO_BLOCK_SIZE_FW_DNLD; /* Copy payload to buffer */ if (firmware) memmove(pmadapter, fwbuf, &firmware[offset], txlen); else pcb->moal_get_fw_data(pmadapter->pmoal_handle, offset, txlen, fwbuf); } /* Send data */ memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); mbuf.pbuf = (t_u8 *)fwbuf; mbuf.data_len = tx_blocks * MLAN_SDIO_BLOCK_SIZE_FW_DNLD; ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, &mbuf, pmadapter->pcard_sd->ioport, 0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "WLAN: FW download, write iomem (%d) failed @ %d\n", i, offset); if (pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, HOST_TERM_CMD53) != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "write CFG reg failed\n"); } ret = MLAN_STATUS_FAILURE; goto done; } offset += txlen; } while (MTRUE); PRINTM(MMSG, "Wlan: FW download over, firmwarelen=%d downloaded %d\n", firmwarelen, offset); ret = MLAN_STATUS_SUCCESS; done: if (tmpfwbuf) pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)tmpfwbuf); LEAVE(); return ret; } /** * @brief This function disables the host interrupts. * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_disable_sdio_host_int(pmlan_adapter pmadapter) { mlan_status ret; ENTER(); ret = wlan_sdio_disable_host_int_mask(pmadapter, HIM_DISABLE); LEAVE(); return ret; } /** * @brief This function decodes the rx packet & * calls corresponding handlers according to the packet type * * @param pmadapter A pointer to mlan_adapter structure * @param pmbuf A pointer to the SDIO data/cmd buffer * @param upld_typ Type of rx packet * @param lock_flag flag for spin_lock. * @return MLAN_STATUS_SUCCESS */ static mlan_status wlan_decode_rx_packet(mlan_adapter *pmadapter, mlan_buffer *pmbuf, t_u32 upld_typ, t_u8 lock_flag) { t_u8 *cmd_buf; t_u32 event; t_u32 in_ts_sec, in_ts_usec; pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); switch (upld_typ) { case MLAN_TYPE_SPA_DATA: PRINTM(MINFO, "--- Rx: SPA Data packet ---\n"); pmbuf->data_len = pmadapter->upld_len; if (pmadapter->rx_work_flag) { pmbuf->buf_type = MLAN_BUF_TYPE_SPA_DATA; if (lock_flag) pmadapter->callbacks.moal_spin_lock( pmadapter->pmoal_handle, pmadapter->rx_data_queue.plock); util_enqueue_list_tail(pmadapter->pmoal_handle, &pmadapter->rx_data_queue, (pmlan_linked_list)pmbuf, MNULL, MNULL); pmadapter->rx_pkts_queued++; if (lock_flag) pmadapter->callbacks.moal_spin_unlock( pmadapter->pmoal_handle, pmadapter->rx_data_queue.plock); } else { wlan_decode_spa_buffer(pmadapter, pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len); wlan_free_mlan_buffer(pmadapter, pmbuf); } pmadapter->data_received = MTRUE; break; case MLAN_TYPE_DATA: PRINTM(MINFO, "--- Rx: Data packet ---\n"); if (pmadapter->upld_len > pmbuf->data_len) { PRINTM(MERROR, "SDIO: Drop packet upld_len=%d data_len=%d \n", pmadapter->upld_len, pmbuf->data_len); wlan_free_mlan_buffer(pmadapter, pmbuf); break; } pmbuf->data_len = (pmadapter->upld_len - SDIO_INTF_HEADER_LEN); pmbuf->data_offset += SDIO_INTF_HEADER_LEN; if (pmadapter->rx_work_flag) { // rx_trace 5 if (pmadapter->tp_state_on) { pmadapter->callbacks.moal_tp_accounting( pmadapter->pmoal_handle, pmbuf, 5 /*RX_DROP_P1*/); pcb->moal_get_system_time( pmadapter->pmoal_handle, &in_ts_sec, &in_ts_usec); pmbuf->in_ts_sec = in_ts_sec; pmbuf->in_ts_usec = in_ts_usec; } if (pmadapter->tp_state_drop_point == 5 /*RX_DROP_P1*/) { pmadapter->ops.data_complete( pmadapter, pmbuf, MLAN_STATUS_SUCCESS); } else { if (lock_flag) pmadapter->callbacks.moal_spin_lock( pmadapter->pmoal_handle, pmadapter->rx_data_queue.plock); util_enqueue_list_tail( pmadapter->pmoal_handle, &pmadapter->rx_data_queue, (pmlan_linked_list)pmbuf, MNULL, MNULL); pmadapter->rx_pkts_queued++; if (pmadapter->tp_state_on) pmadapter->callbacks .moal_tp_accounting_rx_param( pmadapter->pmoal_handle, 1, pmadapter ->rx_pkts_queued); if (lock_flag) pmadapter->callbacks.moal_spin_unlock( pmadapter->pmoal_handle, pmadapter->rx_data_queue.plock); } } else { wlan_handle_rx_packet(pmadapter, pmbuf); } pmadapter->data_received = MTRUE; break; case MLAN_TYPE_CMD: PRINTM(MINFO, "--- Rx: Cmd Response ---\n"); /* take care of curr_cmd = NULL case */ if (!pmadapter->curr_cmd) { cmd_buf = pmadapter->upld_buf; if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) { wlan_process_sleep_confirm_resp( pmadapter, pmbuf->pbuf + pmbuf->data_offset + SDIO_INTF_HEADER_LEN, pmadapter->upld_len - SDIO_INTF_HEADER_LEN); } pmadapter->upld_len -= SDIO_INTF_HEADER_LEN; memcpy_ext(pmadapter, cmd_buf, pmbuf->pbuf + pmbuf->data_offset + SDIO_INTF_HEADER_LEN, pmadapter->upld_len - SDIO_INTF_HEADER_LEN, MRVDRV_SIZE_OF_CMD_BUFFER); wlan_free_mlan_buffer(pmadapter, pmbuf); } else { pmadapter->cmd_resp_received = MTRUE; pmadapter->upld_len -= SDIO_INTF_HEADER_LEN; pmbuf->data_len = pmadapter->upld_len; pmbuf->data_offset += SDIO_INTF_HEADER_LEN; pmadapter->curr_cmd->respbuf = pmbuf; if (pmadapter->upld_len >= MRVDRV_SIZE_OF_CMD_BUFFER) { PRINTM(MMSG, "Invalid CmdResp len=%d\n", pmadapter->upld_len); DBG_HEXDUMP(MERROR, "Invalid CmdResp", pmbuf->pbuf + pmbuf->data_offset, MAX_DATA_DUMP_LEN); } } break; case MLAN_TYPE_EVENT: PRINTM(MINFO, "--- Rx: Event ---\n"); event = *(t_u32 *)&pmbuf->pbuf[pmbuf->data_offset + SDIO_INTF_HEADER_LEN]; pmadapter->event_cause = wlan_le32_to_cpu(event); if ((pmadapter->upld_len > MLAN_EVENT_HEADER_LEN) && ((pmadapter->upld_len - MLAN_EVENT_HEADER_LEN) < MAX_EVENT_SIZE)) { memcpy_ext(pmadapter, pmadapter->event_body, pmbuf->pbuf + pmbuf->data_offset + MLAN_EVENT_HEADER_LEN, pmadapter->upld_len - MLAN_EVENT_HEADER_LEN, MAX_EVENT_SIZE); } /* event cause has been saved to adapter->event_cause */ pmadapter->event_received = MTRUE; pmbuf->data_len = pmadapter->upld_len; pmadapter->pmlan_buffer_event = pmbuf; /* remove SDIO header */ pmbuf->data_offset += SDIO_INTF_HEADER_LEN; pmbuf->data_len -= SDIO_INTF_HEADER_LEN; break; default: PRINTM(MERROR, "SDIO unknown upload type = 0x%x\n", upld_typ); wlan_free_mlan_buffer(pmadapter, pmbuf); break; } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function receives single packet * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_receive_single_packet(mlan_adapter *pmadapter) { mlan_buffer *pmbuf; t_u8 port; t_u16 rx_len; t_u32 pkt_type = 0; mlan_status ret = MLAN_STATUS_SUCCESS; ENTER(); pmbuf = pmadapter->pcard_sd->mpa_rx.mbuf_arr[0]; port = pmadapter->pcard_sd->mpa_rx.start_port; rx_len = pmadapter->pcard_sd->mpa_rx.len_arr[0]; if (MLAN_STATUS_SUCCESS != wlan_sdio_card_to_host(pmadapter, &pkt_type, (t_u32 *)&pmadapter->upld_len, pmbuf, rx_len, pmadapter->pcard_sd->ioport + port)) { ret = MLAN_STATUS_FAILURE; goto done; } if (pkt_type != MLAN_TYPE_DATA && pkt_type != MLAN_TYPE_SPA_DATA) { PRINTM(MERROR, "receive a wrong pkt from DATA PORT: type=%d, len=%dd\n", pkt_type, pmbuf->data_len); pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; ret = MLAN_STATUS_FAILURE; goto done; } pmadapter->pcard_sd->mpa_rx_count[0]++; wlan_decode_rx_packet(pmadapter, pmbuf, pkt_type, MTRUE); done: if (ret != MLAN_STATUS_SUCCESS) wlan_free_mlan_buffer(pmadapter, pmbuf); MP_RX_AGGR_BUF_RESET(pmadapter); LEAVE(); return ret; } /** * @brief This function receives data from the card in aggregate mode. * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_receive_mp_aggr_buf(mlan_adapter *pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; mlan_buffer mbuf_aggr; mlan_buffer *mbuf_deaggr; t_u32 pind = 0; t_u32 pkt_len, pkt_type = 0; t_u8 *curr_ptr; t_u32 cmd53_port = 0; t_u32 i = 0; t_u32 port_count = 0; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; /* do aggr RX now */ PRINTM(MINFO, "do_rx_aggr: num of packets: %d\n", pmadapter->pcard_sd->mpa_rx.pkt_cnt); memset(pmadapter, &mbuf_aggr, 0, sizeof(mlan_buffer)); if (pmadapter->pcard_sd->mpa_rx.pkt_cnt == 1) return wlan_receive_single_packet(pmadapter); if (!pmadapter->pcard_sd->mpa_rx.buf) { mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_rx.buf_len; mbuf_aggr.pnext = mbuf_aggr.pprev = &mbuf_aggr; mbuf_aggr.use_count = 0; for (pind = 0; pind < pmadapter->pcard_sd->mpa_rx.pkt_cnt; pind++) { pmadapter->pcard_sd->mpa_rx.mbuf_arr[pind]->data_len = pmadapter->pcard_sd->mpa_rx.len_arr[pind]; wlan_link_buf_to_aggr( &mbuf_aggr, pmadapter->pcard_sd->mpa_rx.mbuf_arr[pind]); } } else { mbuf_aggr.pbuf = (t_u8 *)pmadapter->pcard_sd->mpa_rx.buf; mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_rx.buf_len; } if (new_mode) { port_count = bitcount(pmadapter->pcard_sd->mpa_rx.ports) - 1; /* port_count = pmadapter->mpa_rx.pkt_cnt - 1; */ cmd53_port = (pmadapter->pcard_sd->ioport | SDIO_MPA_ADDR_BASE | (port_count << 8)) + pmadapter->pcard_sd->mpa_rx.start_port; } else { cmd53_port = (pmadapter->pcard_sd->ioport | SDIO_MPA_ADDR_BASE | (pmadapter->pcard_sd->mpa_rx.ports << 4)) + pmadapter->pcard_sd->mpa_rx.start_port; } do { ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, &mbuf_aggr, cmd53_port, 0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "wlan: sdio mp cmd53 read failed: %d ioport=0x%x retry=%d\n", ret, cmd53_port, i); i++; if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, HOST_TERM_CMD53)) { PRINTM(MERROR, "Set Term cmd53 failed\n"); } if (i > MAX_WRITE_IOMEM_RETRY) { ret = MLAN_STATUS_FAILURE; goto done; } } } while (ret == MLAN_STATUS_FAILURE); if (pmadapter->rx_work_flag) pmadapter->callbacks.moal_spin_lock( pmadapter->pmoal_handle, pmadapter->rx_data_queue.plock); if (!pmadapter->pcard_sd->mpa_rx.buf && pmadapter->pcard_sd->mpa_rx.pkt_cnt > 1) { for (pind = 0; pind < pmadapter->pcard_sd->mpa_rx.pkt_cnt; pind++) { mbuf_deaggr = pmadapter->pcard_sd->mpa_rx.mbuf_arr[pind]; pkt_len = wlan_le16_to_cpu( *(t_u16 *)(mbuf_deaggr->pbuf + mbuf_deaggr->data_offset)); pkt_type = wlan_le16_to_cpu( *(t_u16 *)(mbuf_deaggr->pbuf + mbuf_deaggr->data_offset + 2)); pmadapter->upld_len = pkt_len; wlan_decode_rx_packet(pmadapter, mbuf_deaggr, pkt_type, MFALSE); } } else { DBG_HEXDUMP(MIF_D, "SDIO MP-A Blk Rd", pmadapter->pcard_sd->mpa_rx.buf, MIN(pmadapter->pcard_sd->mpa_rx.buf_len, MAX_DATA_DUMP_LEN)); curr_ptr = pmadapter->pcard_sd->mpa_rx.buf; for (pind = 0; pind < pmadapter->pcard_sd->mpa_rx.pkt_cnt; pind++) { /* get curr PKT len & type */ pkt_len = wlan_le16_to_cpu(*(t_u16 *)&curr_ptr[0]); pkt_type = wlan_le16_to_cpu(*(t_u16 *)&curr_ptr[2]); PRINTM(MINFO, "RX: [%d] pktlen: %d pkt_type: 0x%x\n", pind, pkt_len, pkt_type); /* copy pkt to deaggr buf */ mbuf_deaggr = pmadapter->pcard_sd->mpa_rx.mbuf_arr[pind]; if ((pkt_type == MLAN_TYPE_DATA || pkt_type == MLAN_TYPE_SPA_DATA) && (pkt_len <= pmadapter->pcard_sd->mpa_rx.len_arr[pind])) { memcpy_ext(pmadapter, mbuf_deaggr->pbuf + mbuf_deaggr->data_offset, curr_ptr, pkt_len, pkt_len); pmadapter->upld_len = pkt_len; /* Process de-aggr packet */ wlan_decode_rx_packet(pmadapter, mbuf_deaggr, pkt_type, MFALSE); } else { PRINTM(MERROR, "Wrong aggr packet: type=%d, len=%d, max_len=%d\n", pkt_type, pkt_len, pmadapter->pcard_sd->mpa_rx .len_arr[pind]); wlan_free_mlan_buffer(pmadapter, mbuf_deaggr); } curr_ptr += pmadapter->pcard_sd->mpa_rx.len_arr[pind]; } } if (pmadapter->rx_work_flag) pmadapter->callbacks.moal_spin_unlock( pmadapter->pmoal_handle, pmadapter->rx_data_queue.plock); pmadapter->pcard_sd ->mpa_rx_count[pmadapter->pcard_sd->mpa_rx.pkt_cnt - 1]++; MP_RX_AGGR_BUF_RESET(pmadapter); done: return ret; } /** * @brief This function receives data from the card in aggregate mode. * * @param pmadapter A pointer to mlan_adapter structure * @param pmbuf A pointer to the SDIO data/cmd buffer * @param port Current port on which packet needs to be rxed * @param rx_len Length of received packet * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_card_to_host_mp_aggr(mlan_adapter *pmadapter, mlan_buffer *pmbuf, t_u8 port, t_u16 rx_len) { mlan_status ret = MLAN_STATUS_SUCCESS; t_s32 f_do_rx_aggr = 0; t_s32 f_do_rx_cur = 0; t_s32 f_aggr_cur = 0; t_s32 f_post_aggr_cur = 0; t_u32 pind = 0; t_u32 pkt_type = 0; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; ENTER(); if (!new_mode && (port == CTRL_PORT)) { /* Read the command response or event without aggr */ PRINTM(MINFO, "card_2_host_mp_aggr: No aggr for control port\n"); f_do_rx_cur = 1; goto rx_curr_single; } if (!pmadapter->pcard_sd->mpa_rx.enabled) { PRINTM(MINFO, "card_2_host_mp_aggr: rx aggregation disabled !\n"); f_do_rx_cur = 1; goto rx_curr_single; } if ((new_mode && (pmadapter->pcard_sd->mp_rd_bitmap & reg->data_port_mask)) || (!new_mode && (pmadapter->pcard_sd->mp_rd_bitmap & (~((t_u32)CTRL_PORT_MASK))))) { /* Some more data RX pending */ PRINTM(MINFO, "card_2_host_mp_aggr: Not last packet\n"); if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { if (MP_RX_AGGR_BUF_HAS_ROOM(pmadapter, rx_len)) { f_aggr_cur = 1; } else { /* No room in Aggr buf, do rx aggr now */ f_do_rx_aggr = 1; f_post_aggr_cur = 1; } } else { /* Rx aggr not in progress */ f_aggr_cur = 1; } } else { /* No more data RX pending */ PRINTM(MINFO, "card_2_host_mp_aggr: Last packet\n"); if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { f_do_rx_aggr = 1; if (MP_RX_AGGR_BUF_HAS_ROOM(pmadapter, rx_len)) { f_aggr_cur = 1; } else { /* No room in Aggr buf, do rx aggr now */ f_do_rx_cur = 1; } } else { f_do_rx_cur = 1; } } if (f_aggr_cur) { PRINTM(MINFO, "Current packet aggregation.\n"); /* Curr pkt can be aggregated */ if (new_mode) MP_RX_AGGR_SETUP(pmadapter, pmbuf, port, rx_len); else MP_RX_AGGR_SETUP_NONEWMODE(pmadapter, pmbuf, port, rx_len); if (MP_RX_AGGR_PKT_LIMIT_REACHED(pmadapter) || ((new_mode && MP_RX_AGGR_PORT_LIMIT_REACHED(pmadapter)) || (!new_mode && MP_RX_AGGR_PORT_LIMIT_REACHED_NONEWMODE(pmadapter)))) { PRINTM(MINFO, "card_2_host_mp_aggr: Aggregation Packet limit reached\n"); /* No more pkts allowed in Aggr buf, rx it */ f_do_rx_aggr = 1; } } if (f_do_rx_aggr) { /* do aggr RX now */ if (MLAN_STATUS_SUCCESS != wlan_receive_mp_aggr_buf(pmadapter)) { ret = MLAN_STATUS_FAILURE; goto done; } } rx_curr_single: if (f_do_rx_cur) { PRINTM(MINFO, "RX: f_do_rx_cur: port: %d rx_len: %d\n", port, rx_len); if (MLAN_STATUS_SUCCESS != wlan_sdio_card_to_host( pmadapter, &pkt_type, (t_u32 *)&pmadapter->upld_len, pmbuf, rx_len, pmadapter->pcard_sd->ioport + port)) { ret = MLAN_STATUS_FAILURE; goto done; } if (!new_mode && ((port == CTRL_PORT) && ((pkt_type != MLAN_TYPE_EVENT) && (pkt_type != MLAN_TYPE_CMD)))) { PRINTM(MERROR, "Wrong pkt from CTRL PORT: type=%d, len=%dd\n", pkt_type, pmbuf->data_len); pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; ret = MLAN_STATUS_FAILURE; goto done; } if (new_mode || (port != CTRL_PORT)) { if (pkt_type != MLAN_TYPE_DATA && pkt_type != MLAN_TYPE_SPA_DATA) { PRINTM(MERROR, "receive a wrong pkt from DATA PORT: type=%d, len=%dd\n", pkt_type, pmbuf->data_len); pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; ret = MLAN_STATUS_FAILURE; goto done; } } if (new_mode || (port != CTRL_PORT)) pmadapter->pcard_sd->mpa_rx_count[0]++; wlan_decode_rx_packet(pmadapter, pmbuf, pkt_type, MTRUE); } if (f_post_aggr_cur) { PRINTM(MINFO, "Current packet aggregation.\n"); /* Curr pkt can be aggregated */ if (new_mode) MP_RX_AGGR_SETUP(pmadapter, pmbuf, port, rx_len); else MP_RX_AGGR_SETUP_NONEWMODE(pmadapter, pmbuf, port, rx_len); } done: if (ret == MLAN_STATUS_FAILURE) { if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { /* MP-A transfer failed - cleanup */ for (pind = 0; pind < pmadapter->pcard_sd->mpa_rx.pkt_cnt; pind++) { wlan_free_mlan_buffer( pmadapter, pmadapter->pcard_sd->mpa_rx .mbuf_arr[pind]); } MP_RX_AGGR_BUF_RESET(pmadapter); } if (f_do_rx_cur) { /* Single Transfer pending */ /* Free curr buff also */ wlan_free_mlan_buffer(pmadapter, pmbuf); } } LEAVE(); return ret; } /** * @brief This function sends aggr buf * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_send_mp_aggr_buf(mlan_adapter *pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 cmd53_port = 0; t_u32 port_count = 0; mlan_buffer mbuf_aggr; t_u8 i = 0; t_u8 mp_aggr_pkt_limit = pmadapter->pcard_sd->mp_aggr_pkt_limit; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; ENTER(); if (!pmadapter->pcard_sd->mpa_tx.pkt_cnt) { LEAVE(); return ret; } PRINTM(MINFO, "host_2_card_mp_aggr: Send aggregation buffer." "%d %d\n", pmadapter->pcard_sd->mpa_tx.start_port, pmadapter->pcard_sd->mpa_tx.ports); memset(pmadapter, &mbuf_aggr, 0, sizeof(mlan_buffer)); if (!pmadapter->pcard_sd->mpa_tx.buf && pmadapter->pcard_sd->mpa_tx.pkt_cnt > 1) { mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_tx.buf_len; mbuf_aggr.pnext = mbuf_aggr.pprev = &mbuf_aggr; mbuf_aggr.use_count = 0; for (i = 0; i < pmadapter->pcard_sd->mpa_tx.pkt_cnt; i++) wlan_link_buf_to_aggr( &mbuf_aggr, pmadapter->pcard_sd->mpa_tx.mbuf_arr[i]); } else { mbuf_aggr.pbuf = (t_u8 *)pmadapter->pcard_sd->mpa_tx.buf; mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_tx.buf_len; } if (new_mode) { port_count = bitcount(pmadapter->pcard_sd->mpa_tx.ports) - 1; cmd53_port = (pmadapter->pcard_sd->ioport | SDIO_MPA_ADDR_BASE | (port_count << 8)) + pmadapter->pcard_sd->mpa_tx.start_port; } else { cmd53_port = (pmadapter->pcard_sd->ioport | SDIO_MPA_ADDR_BASE | (pmadapter->pcard_sd->mpa_tx.ports << 4)) + pmadapter->pcard_sd->mpa_tx.start_port; } if (pmadapter->pcard_sd->mpa_tx.pkt_cnt == 1) cmd53_port = pmadapter->pcard_sd->ioport + pmadapter->pcard_sd->mpa_tx.start_port; /** only one packet */ if (!pmadapter->pcard_sd->mpa_tx.buf && pmadapter->pcard_sd->mpa_tx.pkt_cnt == 1) ret = wlan_write_data_sync( pmadapter, pmadapter->pcard_sd->mpa_tx.mbuf_arr[0], cmd53_port); else ret = wlan_write_data_sync(pmadapter, &mbuf_aggr, cmd53_port); if (!pmadapter->pcard_sd->mpa_tx.buf) { /** free mlan buffer */ for (i = 0; i < pmadapter->pcard_sd->mpa_tx.pkt_cnt; i++) { wlan_write_data_complete( pmadapter, pmadapter->pcard_sd->mpa_tx.mbuf_arr[i], MLAN_STATUS_SUCCESS); } } if (!(pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port)) && (pmadapter->pcard_sd->mpa_tx.pkt_cnt < mp_aggr_pkt_limit)) pmadapter->pcard_sd->mpa_sent_no_ports++; pmadapter->pcard_sd ->mpa_tx_count[pmadapter->pcard_sd->mpa_tx.pkt_cnt - 1]++; pmadapter->pcard_sd ->last_mp_wr_bitmap[pmadapter->pcard_sd->last_mp_index] = pmadapter->pcard_sd->mp_wr_bitmap; pmadapter->pcard_sd ->last_mp_wr_ports[pmadapter->pcard_sd->last_mp_index] = cmd53_port; pmadapter->pcard_sd->last_mp_wr_len[pmadapter->pcard_sd->last_mp_index] = pmadapter->pcard_sd->mpa_tx.buf_len; pmadapter->pcard_sd ->last_curr_wr_port[pmadapter->pcard_sd->last_mp_index] = pmadapter->pcard_sd->curr_wr_port; memcpy_ext( pmadapter, (t_u8 *)&pmadapter->pcard_sd ->last_mp_wr_info[pmadapter->pcard_sd->last_mp_index * mp_aggr_pkt_limit], (t_u8 *)pmadapter->pcard_sd->mpa_tx.mp_wr_info, mp_aggr_pkt_limit * sizeof(t_u16), mp_aggr_pkt_limit * sizeof(t_u16)); pmadapter->pcard_sd->last_mp_index++; if (pmadapter->pcard_sd->last_mp_index >= SDIO_MP_DBG_NUM) pmadapter->pcard_sd->last_mp_index = 0; MP_TX_AGGR_BUF_RESET(pmadapter); LEAVE(); return ret; } /** * @brief This function sends data to the card in SDIO aggregated mode. * * @param pmadapter A pointer to mlan_adapter structure * @param mbuf A pointer to the SDIO data/cmd buffer * @param port current port for aggregation * @param next_pkt_len Length of next packet used for multiport aggregation * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_host_to_card_mp_aggr(mlan_adapter *pmadapter, mlan_buffer *mbuf, t_u8 port, t_u32 next_pkt_len) { mlan_status ret = MLAN_STATUS_SUCCESS; t_s32 f_send_aggr_buf = 0; t_s32 f_send_cur_buf = 0; t_s32 f_precopy_cur_buf = 0; t_s32 f_postcopy_cur_buf = 0; t_u8 aggr_sg = 0; t_u8 mp_aggr_pkt_limit = pmadapter->pcard_sd->mp_aggr_pkt_limit; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; ENTER(); PRINTM(MIF_D, "host_2_card_mp_aggr: next_pkt_len: %d curr_port:%d\n", next_pkt_len, port); if (!pmadapter->pcard_sd->mpa_tx.enabled) { PRINTM(MINFO, "host_2_card_mp_aggr: tx aggregation disabled !\n"); f_send_cur_buf = 1; goto tx_curr_single; } if (next_pkt_len) { /* More pkt in TX queue */ PRINTM(MINFO, "host_2_card_mp_aggr: More packets in Queue.\n"); if (MP_TX_AGGR_IN_PROGRESS(pmadapter)) { if (MP_TX_AGGR_BUF_HAS_ROOM(pmadapter, mbuf, mbuf->data_len)) { f_precopy_cur_buf = 1; if (!(pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port)) || !MP_TX_AGGR_BUF_HAS_ROOM( pmadapter, mbuf, mbuf->data_len + next_pkt_len)) { f_send_aggr_buf = 1; } } else { /* No room in Aggr buf, send it */ f_send_aggr_buf = 1; if (!(pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port))) { f_send_cur_buf = 1; } else { f_postcopy_cur_buf = 1; } } } else { if (MP_TX_AGGR_BUF_HAS_ROOM(pmadapter, mbuf, mbuf->data_len) && (pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port))) f_precopy_cur_buf = 1; else f_send_cur_buf = 1; } } else { /* Last pkt in TX queue */ PRINTM(MINFO, "host_2_card_mp_aggr: Last packet in Tx Queue.\n"); if (MP_TX_AGGR_IN_PROGRESS(pmadapter)) { /* some packs in Aggr buf already */ f_send_aggr_buf = 1; if (MP_TX_AGGR_BUF_HAS_ROOM(pmadapter, mbuf, mbuf->data_len)) { f_precopy_cur_buf = 1; } else { /* No room in Aggr buf, send it */ f_send_cur_buf = 1; } } else { f_send_cur_buf = 1; } pmadapter->pcard_sd->mpa_sent_last_pkt++; } if (f_precopy_cur_buf) { PRINTM(MINFO, "host_2_card_mp_aggr: Precopy current buffer\n"); if (pmadapter->pcard_sd->mpa_buf) memcpy_ext( pmadapter, pmadapter->pcard_sd->mpa_buf + (pmadapter->pcard_sd->last_mp_index * mp_aggr_pkt_limit + pmadapter->pcard_sd->mpa_tx.pkt_cnt) * MLAN_SDIO_BLOCK_SIZE, mbuf->pbuf + mbuf->data_offset, MLAN_SDIO_BLOCK_SIZE, MLAN_SDIO_BLOCK_SIZE); if (!pmadapter->pcard_sd->mpa_tx.buf) { if (new_mode) MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); else MP_TX_AGGR_BUF_PUT_SG_NONEWMODE(pmadapter, mbuf, port); aggr_sg = MTRUE; } else { if (new_mode) MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); else MP_TX_AGGR_BUF_PUT_NONEWMODE(pmadapter, mbuf, port); } if (MP_TX_AGGR_PKT_LIMIT_REACHED(pmadapter) || (!new_mode && MP_TX_AGGR_PORT_LIMIT_REACHED(pmadapter))) { PRINTM(MIF_D, "host_2_card_mp_aggr: Aggregation Pkt limit reached\n"); /* No more pkts allowed in Aggr buf, send it */ f_send_aggr_buf = 1; } } if (f_send_aggr_buf) ret = wlan_send_mp_aggr_buf(pmadapter); tx_curr_single: if (f_send_cur_buf) { PRINTM(MINFO, "host_2_card_mp_aggr: writing to port #%d\n", port); ret = wlan_write_data_sync(pmadapter, mbuf, pmadapter->pcard_sd->ioport + port); if (!(pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port))) pmadapter->pcard_sd->mpa_sent_no_ports++; pmadapter->pcard_sd ->last_mp_wr_bitmap[pmadapter->pcard_sd->last_mp_index] = pmadapter->pcard_sd->mp_wr_bitmap; pmadapter->pcard_sd ->last_mp_wr_ports[pmadapter->pcard_sd->last_mp_index] = pmadapter->pcard_sd->ioport + port; pmadapter->pcard_sd ->last_mp_wr_len[pmadapter->pcard_sd->last_mp_index] = mbuf->data_len; memset(pmadapter, (t_u8 *)&pmadapter->pcard_sd->last_mp_wr_info [pmadapter->pcard_sd->last_mp_index * mp_aggr_pkt_limit], 0, sizeof(t_u16) * mp_aggr_pkt_limit); pmadapter->pcard_sd ->last_mp_wr_info[pmadapter->pcard_sd->last_mp_index * mp_aggr_pkt_limit] = *(t_u16 *)(mbuf->pbuf + mbuf->data_offset); pmadapter->pcard_sd ->last_curr_wr_port[pmadapter->pcard_sd->last_mp_index] = pmadapter->pcard_sd->curr_wr_port; if (pmadapter->pcard_sd->mpa_buf) memcpy_ext(pmadapter, pmadapter->pcard_sd->mpa_buf + (pmadapter->pcard_sd->last_mp_index * mp_aggr_pkt_limit * MLAN_SDIO_BLOCK_SIZE), mbuf->pbuf + mbuf->data_offset, MLAN_SDIO_BLOCK_SIZE, MLAN_SDIO_BLOCK_SIZE); pmadapter->pcard_sd->last_mp_index++; if (pmadapter->pcard_sd->last_mp_index >= SDIO_MP_DBG_NUM) pmadapter->pcard_sd->last_mp_index = 0; pmadapter->pcard_sd->mpa_tx_count[0]++; } if (f_postcopy_cur_buf) { PRINTM(MINFO, "host_2_card_mp_aggr: Postcopy current buffer\n"); if (pmadapter->pcard_sd->mpa_buf) memcpy_ext( pmadapter, pmadapter->pcard_sd->mpa_buf + (pmadapter->pcard_sd->last_mp_index * mp_aggr_pkt_limit + pmadapter->pcard_sd->mpa_tx.pkt_cnt) * MLAN_SDIO_BLOCK_SIZE, mbuf->pbuf + mbuf->data_offset, MLAN_SDIO_BLOCK_SIZE, MLAN_SDIO_BLOCK_SIZE); if (!pmadapter->pcard_sd->mpa_tx.buf) { if (new_mode) MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); else MP_TX_AGGR_BUF_PUT_SG_NONEWMODE(pmadapter, mbuf, port); aggr_sg = MTRUE; } else { if (new_mode) MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); else MP_TX_AGGR_BUF_PUT_NONEWMODE(pmadapter, mbuf, port); } } /* Always return PENDING in SG mode */ if (aggr_sg) ret = MLAN_STATUS_PENDING; LEAVE(); return ret; } /******************************************************** Global functions ********************************************************/ /** * @brief This function checks if the interface is ready to download * or not while other download interface is present * * @param pmadapter A pointer to mlan_adapter structure * @param val Winner status (0: winner) * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE * */ static mlan_status wlan_sdio_check_winner_status(mlan_adapter *pmadapter, t_u32 *val) { t_u32 winner = 0; pmlan_callbacks pcb; t_u8 card_winner_check_reg = pmadapter->pcard_sd->reg->winner_check_reg; ENTER(); #ifdef SD8801 if (IS_SD8801(pmadapter->card_type)) { *val = 0; return MLAN_STATUS_SUCCESS; } #endif pcb = &pmadapter->callbacks; if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, card_winner_check_reg, &winner)) { LEAVE(); return MLAN_STATUS_FAILURE; } *val = winner; LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function checks if the firmware is ready to accept * command or not. * * @param pmadapter A pointer to mlan_adapter structure * @param pollnum Maximum polling number * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_check_fw_status(mlan_adapter *pmadapter, t_u32 pollnum) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u16 firmwarestat = 0; t_u32 tries; ENTER(); /* Wait for firmware initialization event */ for (tries = 0; tries < pollnum; tries++) { ret = wlan_sdio_read_fw_status(pmadapter, &firmwarestat); if (MLAN_STATUS_SUCCESS != ret) continue; if (firmwarestat == SDIO_FIRMWARE_READY) { ret = MLAN_STATUS_SUCCESS; break; } else { wlan_mdelay(pmadapter, 100); ret = MLAN_STATUS_FAILURE; } } if (ret != MLAN_STATUS_SUCCESS) { if (pollnum > 1) PRINTM(MERROR, "Fail to poll firmware status: firmwarestat=0x%x\n", firmwarestat); goto done; } done: LEAVE(); return ret; } /** * @brief This function enables the host interrupts. * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_enable_sdio_host_int(pmlan_adapter pmadapter) { mlan_status ret; t_u8 mask = pmadapter->pcard_sd->reg->host_int_enable; ENTER(); ret = wlan_sdio_enable_host_int_mask(pmadapter, mask); LEAVE(); return ret; } /** * @brief This function downloads firmware to card * * @param pmadapter A pointer to mlan_adapter * @param pmfw A pointer to firmware image * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_dnld_fw(pmlan_adapter pmadapter, pmlan_fw_image pmfw) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 poll_num = 1; t_u32 winner = 0; ENTER(); /*when using GPIO wakeup, don't run the below code. *if using GPIO wakeup, host will do handshake with FW *to check if FW wake up and pull up SDIO line, then reload driver. *So when using GPIO wakeup, don't need driver to do check wakeup status *again. when using SDIO interface wakeup, run the below code; if using *SDIO interface wakeup, driver need to do check wakeup status with FW. */ /* Card specific probing */ ret = wlan_sdio_probe(pmadapter); if (ret == MLAN_STATUS_FAILURE) { PRINTM(MERROR, "WLAN SDIO probe failed\n", ret); LEAVE(); return ret; } /* Check if firmware is already running */ ret = wlan_sdio_check_fw_status(pmadapter, poll_num); if (ret == MLAN_STATUS_SUCCESS) { #if defined(SDIO) if (pmfw->fw_reload == FW_RELOAD_SDIO_INBAND_RESET) { PRINTM(MMSG, "Try reset fw in mlan\n"); ret = wlan_reset_fw(pmadapter); if (ret == MLAN_STATUS_FAILURE) { PRINTM(MERROR, "FW reset failure!"); LEAVE(); return ret; } } else { #endif PRINTM(MMSG, "WLAN FW already running! Skip FW download\n"); #if defined(SDIO) pmadapter->ops.wakeup_card(pmadapter, MFALSE); #endif goto done; #if defined(SDIO) } #endif } poll_num = MAX_FIRMWARE_POLL_TRIES; /* Check if other interface is downloading */ ret = wlan_sdio_check_winner_status(pmadapter, &winner); if (ret == MLAN_STATUS_FAILURE) { PRINTM(MFATAL, "WLAN read winner status failed!\n"); goto done; } if (winner) { PRINTM(MMSG, "WLAN is not the winner (0x%x). Skip FW download\n", winner); poll_num = MAX_MULTI_INTERFACE_POLL_TRIES; goto poll_fw; } /* Download the firmware image via helper */ ret = wlan_sdio_prog_fw_w_helper(pmadapter, pmfw->pfw_buf, pmfw->fw_len); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "wlan_dnld_fw fail ret=0x%x\n", ret); LEAVE(); return ret; } poll_fw: /* Check if the firmware is downloaded successfully or not */ ret = wlan_sdio_check_fw_status(pmadapter, poll_num); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MFATAL, "FW failed to be active in time!\n"); ret = MLAN_STATUS_FAILURE; LEAVE(); return ret; } #ifdef SD9177 if (IS_SD9177(pmadapter->card_type)) wlan_mdelay(pmadapter, 1000); #endif done: /* re-enable host interrupt for mlan after fw dnld is successful */ wlan_enable_sdio_host_int(pmadapter); LEAVE(); return ret; } /** * @brief This function probes the driver * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_sdio_probe(pmlan_adapter pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 sdio_ireg = 0; pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); /* * Read the HOST_INT_STATUS_REG for ACK the first interrupt got * from the bootloader. If we don't do this we get a interrupt * as soon as we register the irq. */ pcb->moal_read_reg(pmadapter->pmoal_handle, pmadapter->pcard_sd->reg->host_int_status_reg, &sdio_ireg); /* Disable host interrupt mask register for SDIO */ ret = wlan_disable_sdio_host_int(pmadapter); if (ret != MLAN_STATUS_SUCCESS) { LEAVE(); return MLAN_STATUS_FAILURE; } /* Get SDIO ioport */ ret = wlan_sdio_init_ioport(pmadapter); LEAVE(); return ret; } /** * @brief This function get sdio device from card type * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_get_sdio_device(pmlan_adapter pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u16 card_type = pmadapter->card_type; ENTER(); ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, sizeof(mlan_sdio_card), MLAN_MEM_DEF, (t_u8 **)&pmadapter->pcard_sd); if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_sd) { PRINTM(MERROR, "Failed to allocate pcard_sd\n"); LEAVE(); return MLAN_STATUS_FAILURE; } pmadapter->pcard_sd->max_ports = MAX_PORT; pmadapter->pcard_sd->mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; pmadapter->pcard_sd->supports_sdio_new_mode = MTRUE; pmadapter->pcard_sd->mp_tx_aggr_buf_size = SDIO_MP_AGGR_BUF_SIZE_MAX; pmadapter->pcard_sd->mp_rx_aggr_buf_size = SDIO_MP_AGGR_BUF_SIZE_MAX; switch (card_type) { #ifdef SD8801 case CARD_TYPE_SD8801: pmadapter->pcard_sd->reg = &mlan_reg_sd8801; pmadapter->pcard_info = &mlan_card_info_sd8801; pmadapter->pcard_sd->max_ports = MAX_PORT_16; pmadapter->pcard_sd->mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT_8; pmadapter->pcard_sd->supports_sdio_new_mode = MFALSE; pmadapter->pcard_sd->mp_tx_aggr_buf_size = SDIO_MP_AGGR_BUF_SIZE_32K; pmadapter->pcard_sd->mp_rx_aggr_buf_size = SDIO_MP_AGGR_BUF_SIZE_32K; break; #endif #ifdef SD8887 case CARD_TYPE_SD8887: pmadapter->pcard_sd->reg = &mlan_reg_sd8887; pmadapter->pcard_info = &mlan_card_info_sd8887; break; #endif #ifdef SD8897 case CARD_TYPE_SD8897: pmadapter->pcard_sd->reg = &mlan_reg_sd8897; pmadapter->pcard_info = &mlan_card_info_sd8897; break; #endif #if defined(SD8977) || defined(SD8978) case CARD_TYPE_SD8977: case CARD_TYPE_SD8978: pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; pmadapter->pcard_info = &mlan_card_info_sd8977; break; #endif #ifdef SD8997 case CARD_TYPE_SD8997: pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; pmadapter->pcard_info = &mlan_card_info_sd8997; break; #endif #ifdef SD8987 case CARD_TYPE_SD8987: pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; pmadapter->pcard_info = &mlan_card_info_sd8987; break; #endif #ifdef SD9098 case CARD_TYPE_SD9098: pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; pmadapter->pcard_info = &mlan_card_info_sd9098; break; #endif #ifdef SD9097 case CARD_TYPE_SD9097: pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; pmadapter->pcard_info = &mlan_card_info_sd9097; break; #endif #ifdef SD9177 case CARD_TYPE_SD9177: pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; pmadapter->pcard_info = &mlan_card_info_sd9177; break; #endif default: PRINTM(MERROR, "can't get right card type \n"); ret = MLAN_STATUS_FAILURE; break; } LEAVE(); return ret; } /** * @brief This function dump the mp registers when issue happened * * @param pmadapter A pointer to mlan_adapter structure * @return N/A */ void wlan_dump_mp_registers(pmlan_adapter pmadapter) { t_u32 mp_wr_bitmap; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; t_u32 mp_rd_bitmap; t_u16 rx_len = 0; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; t_u8 cmd_rd_len_0 = reg->cmd_rd_len_0; t_u8 cmd_rd_len_1 = reg->cmd_rd_len_1; t_u8 host_int_status_reg = reg->host_int_status_reg; t_u32 sdio_ireg = 0; mp_wr_bitmap = (t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_l]; mp_wr_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_u]) << 8; if (new_mode) { mp_wr_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_1l]) << 16; mp_wr_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_1u]) << 24; } PRINTM(MMSG, "wlan: mp_data_port_mask = 0x%x\n", pmadapter->pcard_sd->mp_data_port_mask); PRINTM(MMSG, "wlan: HW wr_bitmap=0x%08x Host: wr_bitmap=0x%08x\n", mp_wr_bitmap, pmadapter->pcard_sd->mp_wr_bitmap); mp_rd_bitmap = (t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_l]; mp_rd_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_u]) << 8; if (new_mode) { mp_rd_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_1l]) << 16; mp_rd_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_1u]) << 24; } PRINTM(MMSG, "wlan: HW rd_bitmap=0x%08x Host: rd_bitmap=0x%08x\n", mp_rd_bitmap, pmadapter->pcard_sd->mp_rd_bitmap); if (new_mode) { rx_len = ((t_u16)pmadapter->pcard_sd->mp_regs[cmd_rd_len_1]) << 8; rx_len |= (t_u16)pmadapter->pcard_sd->mp_regs[cmd_rd_len_0]; PRINTM(MMSG, "wlan: cmd rx buffer rx_len = %d\n", rx_len); } PRINTM(MMSG, "wlan: HW sdio_ireg = 0x%x\n", pmadapter->pcard_sd->mp_regs[host_int_status_reg]); sdio_ireg = pmadapter->pcard_sd->mp_regs[host_int_status_reg]; if (new_mode && rx_len) sdio_ireg |= UP_LD_CMD_PORT_HOST_INT_STATUS; if (!(pmadapter->pcard_sd->mp_wr_bitmap & pmadapter->pcard_sd->mp_data_port_mask)) { if (mp_wr_bitmap & pmadapter->pcard_sd->mp_data_port_mask) sdio_ireg |= DN_LD_HOST_INT_STATUS; } if ((!pmadapter->pcard_sd->mp_rd_bitmap) && mp_rd_bitmap) sdio_ireg |= UP_LD_HOST_INT_STATUS; pmadapter->pcard_sd->mp_regs[host_int_status_reg] = sdio_ireg; PRINTM(MMSG, "wlan: recovered sdio_ireg=0x%x\n", sdio_ireg); return; } /** * @brief This function gets interrupt status. * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS */ static mlan_status wlan_sdio_interrupt(t_u16 msg_id, pmlan_adapter pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; mlan_buffer mbuf; t_u32 sdio_ireg = 0; t_u8 offset = 0; t_u8 i = 0; int max_mp_regs = pmadapter->pcard_sd->reg->max_mp_regs; t_u8 host_int_status_reg = pmadapter->pcard_sd->reg->host_int_status_reg; ENTER(); while (max_mp_regs) { memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); mbuf.pbuf = pmadapter->pcard_sd->mp_regs + offset; mbuf.data_len = MIN(max_mp_regs, MLAN_SDIO_BLOCK_SIZE); do { ret = pcb->moal_read_data_sync( pmadapter->pmoal_handle, &mbuf, (REG_PORT + offset) | MLAN_SDIO_BYTE_MODE_MASK, 0); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "wlan: cmd53 read regs failed: %d port=%x retry=%d\n", ret, REG_PORT + offset, i); i++; pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, HOST_TERM_CMD53); if (i > MAX_WRITE_IOMEM_RETRY) { PRINTM(MERROR, "wlan: Fail to read mp_regs\n"); pmadapter->dbg.num_int_read_failure++; goto done; } } } while (ret == MLAN_STATUS_FAILURE); offset += mbuf.data_len; max_mp_regs -= mbuf.data_len; } if (i > 0) wlan_dump_mp_registers(pmadapter); DBG_HEXDUMP(MIF_D, "SDIO MP Registers", pmadapter->pcard_sd->mp_regs, max_mp_regs); sdio_ireg = pmadapter->pcard_sd->mp_regs[host_int_status_reg]; pmadapter->dbg.last_int_status = pmadapter->ireg | sdio_ireg; if (sdio_ireg) { /* * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS * DN_LD_CMD_PORT_HOST_INT_STATUS and/or * UP_LD_CMD_PORT_HOST_INT_STATUS * Clear the interrupt status register */ PRINTM(MINTR, "wlan_interrupt: sdio_ireg = 0x%x\n", sdio_ireg); pmadapter->pcard_sd->num_of_irq++; pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pint_lock); pmadapter->ireg |= sdio_ireg; pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmadapter->pint_lock); if (!pmadapter->pps_uapsd_mode && pmadapter->ps_state == PS_STATE_SLEEP) { pmadapter->pm_wakeup_fw_try = MFALSE; pmadapter->ps_state = PS_STATE_AWAKE; pmadapter->pm_wakeup_card_req = MFALSE; } } else { PRINTM(MMSG, "wlan_interrupt: sdio_ireg = 0x%x\n", sdio_ireg); } done: LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function try to read the packet when fail to alloc rx buffer * * @param pmadapter A pointer to mlan_adapter structure * @param port Current port on which packet needs to be rxed * @param rx_len Length of received packet * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_card_to_host_recovery(mlan_adapter *pmadapter, t_u8 port, t_u16 rx_len) { mlan_buffer mbuf; t_u32 pkt_type = 0; mlan_status ret = MLAN_STATUS_FAILURE; ENTER(); if (!pmadapter->pcard_sd->supports_sdio_new_mode) goto done; if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { PRINTM(MDATA, "Recovery:do Rx Aggr\n"); /* do aggr RX now */ wlan_receive_mp_aggr_buf(pmadapter); } memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); mbuf.pbuf = pmadapter->pcard_sd->rx_buf; mbuf.data_len = rx_len; PRINTM(MDATA, "Recovery: Try read port=%d rx_len=%d\n", port, rx_len); if (MLAN_STATUS_SUCCESS != wlan_sdio_card_to_host(pmadapter, &pkt_type, (t_u32 *)&pmadapter->upld_len, &mbuf, rx_len, pmadapter->pcard_sd->ioport + port)) { PRINTM(MERROR, "Recovery: Fail to do cmd53\n"); } if (pkt_type != MLAN_TYPE_DATA && pkt_type != MLAN_TYPE_SPA_DATA) { PRINTM(MERROR, "Recovery: Receive a wrong pkt: type=%d, len=%d\n", pkt_type, pmadapter->upld_len); goto done; } if (pkt_type == MLAN_TYPE_DATA) { // TODO fill the hole in Rx reorder table PRINTM(MDATA, "Recovery: Drop Data packet\n"); pmadapter->dbg.num_pkt_dropped++; } else if (pkt_type == MLAN_TYPE_SPA_DATA) { PRINTM(MDATA, "Recovery: SPA Data packet len=%d\n", pmadapter->upld_len); wlan_decode_spa_buffer(pmadapter, pmadapter->pcard_sd->rx_buf, pmadapter->upld_len); pmadapter->data_received = MTRUE; } PRINTM(MMSG, "wlan: Success handle rx port=%d, rx_len=%d \n", port, rx_len); ret = MLAN_STATUS_SUCCESS; done: LEAVE(); return ret; } /** * @brief This function checks the interrupt status and handle it accordingly. * * @param pmadapter A pointer to mlan_adapter structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_process_sdio_int_status(mlan_adapter *pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; t_u8 sdio_ireg; mlan_buffer *pmbuf = MNULL; t_u8 port = 0; t_u32 len_reg_l, len_reg_u; t_u32 rx_blocks; t_u8 bit_count = 0; t_u32 ps_state = pmadapter->ps_state; t_u16 rx_len; t_u32 upld_typ = 0; t_u32 cr = 0; const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; t_u8 rd_len_p0_l = reg->rd_len_p0_l; t_u8 rd_len_p0_u = reg->rd_len_p0_u; t_u8 cmd_rd_len_0 = reg->cmd_rd_len_0; t_u8 cmd_rd_len_1 = reg->cmd_rd_len_1; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; ENTER(); pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pint_lock); sdio_ireg = (t_u8)pmadapter->ireg; pmadapter->ireg = 0; pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmadapter->pint_lock); if (!sdio_ireg) goto done; if (new_mode) { /* check the command port */ if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS) { if (pmadapter->cmd_sent) pmadapter->cmd_sent = MFALSE; PRINTM(MINFO, "cmd_sent=%d\n", pmadapter->cmd_sent); } if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { /* read the len of control packet */ rx_len = ((t_u16)pmadapter->pcard_sd ->mp_regs[cmd_rd_len_1]) << 8; rx_len |= (t_u16)pmadapter->pcard_sd ->mp_regs[cmd_rd_len_0]; PRINTM(MINFO, "RX: cmd port rx_len=%u\n", rx_len); rx_blocks = (rx_len + MLAN_SDIO_BLOCK_SIZE - 1) / MLAN_SDIO_BLOCK_SIZE; if (rx_len <= SDIO_INTF_HEADER_LEN || (rx_blocks * MLAN_SDIO_BLOCK_SIZE) > ALLOC_BUF_SIZE) { PRINTM(MERROR, "invalid rx_len=%d\n", rx_len); ret = MLAN_STATUS_FAILURE; goto done; } rx_len = (t_u16)(rx_blocks * MLAN_SDIO_BLOCK_SIZE); pmbuf = wlan_alloc_mlan_buffer(pmadapter, rx_len, 0, MOAL_MALLOC_BUFFER); if (pmbuf == MNULL) { PRINTM(MERROR, "Failed to allocate 'mlan_buffer'\n"); ret = MLAN_STATUS_FAILURE; goto done; } PRINTM(MINFO, "cmd rx buffer rx_len = %d\n", rx_len); /* Transfer data from card */ if (MLAN_STATUS_SUCCESS != wlan_sdio_card_to_host(pmadapter, &upld_typ, (t_u32 *)&pmadapter->upld_len, pmbuf, rx_len, pmadapter->pcard_sd->ioport | CMD_PORT_SLCT)) { pmadapter->dbg.num_cmdevt_card_to_host_failure++; PRINTM(MERROR, "Card-to-host cmd failed: int status=0x%x\n", sdio_ireg); wlan_free_mlan_buffer(pmadapter, pmbuf); ret = MLAN_STATUS_FAILURE; goto term_cmd53; } if ((upld_typ != MLAN_TYPE_CMD) && (upld_typ != MLAN_TYPE_EVENT)) PRINTM(MERROR, "receive a wrong packet from CMD PORT. type =0x%x\n", upld_typ); wlan_decode_rx_packet(pmadapter, pmbuf, upld_typ, MFALSE); /* We might receive data/sleep_cfm at the same time */ /* reset data_receive flag to avoid ps_state change */ if ((ps_state == PS_STATE_SLEEP_CFM) && (pmadapter->ps_state == PS_STATE_SLEEP)) pmadapter->data_received = MFALSE; } } if (sdio_ireg & DN_LD_HOST_INT_STATUS) { if (pmadapter->pcard_sd->mp_wr_bitmap & pmadapter->pcard_sd->mp_data_port_mask) pmadapter->pcard_sd->mp_invalid_update++; pmadapter->pcard_sd->mp_wr_bitmap = (t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_l]; pmadapter->pcard_sd->mp_wr_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_u]) << 8; if (new_mode) { pmadapter->pcard_sd->mp_wr_bitmap |= ((t_u32)pmadapter->pcard_sd ->mp_regs[reg->wr_bitmap_1l]) << 16; pmadapter->pcard_sd->mp_wr_bitmap |= ((t_u32)pmadapter->pcard_sd ->mp_regs[reg->wr_bitmap_1u]) << 24; } bit_count = bitcount(pmadapter->pcard_sd->mp_wr_bitmap & pmadapter->pcard_sd->mp_data_port_mask); if (bit_count) { pmadapter->pcard_sd->mp_update[bit_count - 1]++; if (pmadapter->pcard_sd->mp_update[bit_count - 1] == 0xffffffff) memset(pmadapter, pmadapter->pcard_sd->mp_update, 0, sizeof(pmadapter->pcard_sd->mp_update)); } pmadapter->pcard_sd->last_recv_wr_bitmap = pmadapter->pcard_sd->mp_wr_bitmap; PRINTM(MINTR, "DNLD: wr_bitmap=0x%08x\n", pmadapter->pcard_sd->mp_wr_bitmap); if (pmadapter->data_sent && (pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port))) { pmadapter->callbacks.moal_tp_accounting_rx_param( pmadapter->pmoal_handle, 3, 0); PRINTM(MINFO, " <--- Tx DONE Interrupt --->\n"); pmadapter->data_sent = MFALSE; } } if ((!new_mode) && (pmadapter->cmd_sent == MTRUE)) { /* Check if firmware has attach buffer at command port and * update just that in wr_bit_map. */ pmadapter->pcard_sd->mp_wr_bitmap |= (t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; if (pmadapter->pcard_sd->mp_wr_bitmap & CTRL_PORT_MASK) pmadapter->cmd_sent = MFALSE; } if (sdio_ireg & UP_LD_HOST_INT_STATUS) { pmadapter->pcard_sd->mp_rd_bitmap = (t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_l]; pmadapter->pcard_sd->mp_rd_bitmap |= ((t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_u]) << 8; if (new_mode) { pmadapter->pcard_sd->mp_rd_bitmap |= ((t_u32)pmadapter->pcard_sd ->mp_regs[reg->rd_bitmap_1l]) << 16; pmadapter->pcard_sd->mp_rd_bitmap |= ((t_u32)pmadapter->pcard_sd ->mp_regs[reg->rd_bitmap_1u]) << 24; } pmadapter->pcard_sd->last_recv_rd_bitmap = pmadapter->pcard_sd->mp_rd_bitmap; PRINTM(MINTR, "UPLD: rd_bitmap=0x%08x\n", pmadapter->pcard_sd->mp_rd_bitmap); pmadapter->callbacks.moal_tp_accounting_rx_param( pmadapter->pmoal_handle, 0, 0); while (MTRUE) { ret = wlan_get_rd_port(pmadapter, &port); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MINFO, "no more rd_port to be handled\n"); break; } len_reg_l = rd_len_p0_l + (port << 1); len_reg_u = rd_len_p0_u + (port << 1); rx_len = ((t_u16)pmadapter->pcard_sd->mp_regs[len_reg_u]) << 8; rx_len |= (t_u16)pmadapter->pcard_sd->mp_regs[len_reg_l]; PRINTM(MINFO, "RX: port=%d rx_len=%u\n", port, rx_len); rx_blocks = (rx_len + MLAN_SDIO_BLOCK_SIZE - 1) / MLAN_SDIO_BLOCK_SIZE; if (rx_len <= SDIO_INTF_HEADER_LEN || (rx_blocks * MLAN_SDIO_BLOCK_SIZE) > pmadapter->pcard_sd->mpa_rx.buf_size) { PRINTM(MERROR, "invalid rx_len=%d\n", rx_len); ret = MLAN_STATUS_FAILURE; goto done; } rx_len = (t_u16)(rx_blocks * MLAN_SDIO_BLOCK_SIZE); if (!new_mode && (port == CTRL_PORT)) pmbuf = wlan_alloc_mlan_buffer( pmadapter, rx_len, 0, MOAL_MALLOC_BUFFER); else pmbuf = wlan_alloc_mlan_buffer( pmadapter, rx_len, MLAN_RX_HEADER_LEN, MOAL_ALLOC_MLAN_BUFFER); if (pmbuf == MNULL) { PRINTM(MERROR, "Failed to allocate 'mlan_buffer'\n"); pmadapter->dbg.num_alloc_buffer_failure++; if (MLAN_STATUS_SUCCESS == wlan_sdio_card_to_host_recovery( pmadapter, port, rx_len)) continue; ret = MLAN_STATUS_FAILURE; goto done; } PRINTM(MINFO, "rx_len = %d\n", rx_len); if (MLAN_STATUS_SUCCESS != wlan_sdio_card_to_host_mp_aggr(pmadapter, pmbuf, port, rx_len)) { if ((!new_mode) && (port == CTRL_PORT)) pmadapter->dbg .num_cmdevt_card_to_host_failure++; else pmadapter->dbg .num_rx_card_to_host_failure++; PRINTM(MERROR, "Card to host failed: int status=0x%x\n", sdio_ireg); ret = MLAN_STATUS_FAILURE; goto term_cmd53; } } /* We might receive data/sleep_cfm at the same time */ /* reset data_receive flag to avoid ps_state change */ if ((ps_state == PS_STATE_SLEEP_CFM) && (pmadapter->ps_state == PS_STATE_SLEEP)) pmadapter->data_received = MFALSE; } ret = MLAN_STATUS_SUCCESS; goto done; term_cmd53: /* terminate cmd53 */ if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, &cr)) PRINTM(MERROR, "read CFG reg failed\n"); PRINTM(MINFO, "Config Reg val = %d\n", cr); if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, (cr | HOST_TERM_CMD53))) PRINTM(MERROR, "write CFG reg failed\n"); PRINTM(MINFO, "write success\n"); if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, &cr)) PRINTM(MERROR, "read CFG reg failed\n"); PRINTM(MINFO, "Config reg val =%x\n", cr); done: LEAVE(); return ret; } /** * @brief This function sends data to the card. * * @param pmadapter A pointer to mlan_adapter structure * @param type data or command * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include * SDIO header) * @param tx_param A pointer to mlan_tx_param * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_sdio_host_to_card(mlan_adapter *pmadapter, t_u8 type, mlan_buffer *pmbuf, mlan_tx_param *tx_param) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 buf_block_len; t_u32 blksz; t_u8 port = 0; t_u32 cmd53_port = 0; t_u8 *payload = pmbuf->pbuf + pmbuf->data_offset; t_bool new_mode = pmadapter->pcard_sd->supports_sdio_new_mode; ENTER(); /* Allocate buffer and copy payload */ blksz = MLAN_SDIO_BLOCK_SIZE; buf_block_len = (pmbuf->data_len + blksz - 1) / blksz; *(t_u16 *)&payload[0] = wlan_cpu_to_le16((t_u16)pmbuf->data_len); *(t_u16 *)&payload[2] = wlan_cpu_to_le16(type); /* * This is SDIO specific header * t_u16 length, * t_u16 type (MLAN_TYPE_DATA = 0, * MLAN_TYPE_CMD = 1, MLAN_TYPE_EVENT = 3) */ if (type == MLAN_TYPE_DATA) { ret = wlan_get_wr_port_data(pmadapter, &port); if (ret != MLAN_STATUS_SUCCESS) { PRINTM(MERROR, "no wr_port available: wr_bitmap=0x%08x curr_wr_port=%d\n", pmadapter->pcard_sd->mp_wr_bitmap, pmadapter->pcard_sd->curr_wr_port); goto exit; } /* Transfer data to card */ pmbuf->data_len = buf_block_len * blksz; if (tx_param) ret = wlan_host_to_card_mp_aggr(pmadapter, pmbuf, port, tx_param->next_pkt_len); else ret = wlan_host_to_card_mp_aggr(pmadapter, pmbuf, port, 0); } else { /*Type must be MLAN_TYPE_CMD*/ pmadapter->cmd_sent = MTRUE; if (!new_mode) pmadapter->pcard_sd->mp_wr_bitmap &= (t_u32)(~(1 << CTRL_PORT)); if (pmbuf->data_len <= SDIO_INTF_HEADER_LEN || pmbuf->data_len > WLAN_UPLD_SIZE) PRINTM(MWARN, "wlan_sdio_host_to_card(): Error: payload=%p, nb=%d\n", payload, pmbuf->data_len); /* Transfer data to card */ pmbuf->data_len = buf_block_len * blksz; if (new_mode) cmd53_port = (pmadapter->pcard_sd->ioport) | CMD_PORT_SLCT; else cmd53_port = pmadapter->pcard_sd->ioport + CTRL_PORT; ret = wlan_write_data_sync(pmadapter, pmbuf, cmd53_port); } if (ret == MLAN_STATUS_FAILURE) { PRINTM(MERROR, "Error: host_to_card failed: 0x%X\n", ret); if (type == MLAN_TYPE_CMD) pmadapter->cmd_sent = MFALSE; if (type == MLAN_TYPE_DATA) pmadapter->data_sent = MFALSE; } else { if (type == MLAN_TYPE_DATA) { if (!(pmadapter->pcard_sd->mp_wr_bitmap & (1 << pmadapter->pcard_sd->curr_wr_port))) pmadapter->data_sent = MTRUE; else pmadapter->data_sent = MFALSE; } DBG_HEXDUMP(MIF_D, "SDIO Blk Wr", pmbuf->pbuf + pmbuf->data_offset, MIN(pmbuf->data_len, MAX_DATA_DUMP_LEN)); } exit: LEAVE(); return ret; } #if (defined(SD9098) || defined(SD9097) || defined(SD9177)) /** * @brief This function sends vdll data to the card. * * @param pmadapter A pointer to mlan_adapter structure * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include * SDIO header) * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_send_vdll(mlan_adapter *pmadapter, mlan_buffer *pmbuf) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 buf_block_len; t_u32 blksz; t_u8 *payload = pmbuf->pbuf + pmbuf->data_offset; t_u32 cmd53_port = 0; ENTER(); blksz = MLAN_SDIO_BLOCK_SIZE; buf_block_len = (pmbuf->data_len + blksz - 1) / blksz; *(t_u16 *)&payload[0] = wlan_cpu_to_le16((t_u16)pmbuf->data_len); *(t_u16 *)&payload[2] = wlan_cpu_to_le16(MLAN_TYPE_VDLL); pmbuf->data_len = buf_block_len * blksz; if (pmbuf->data_len > MRVDRV_SIZE_OF_CMD_BUFFER) { PRINTM(MERROR, "VDLL block is too big: %d\n", pmbuf->data_len); return MLAN_STATUS_FAILURE; } cmd53_port = (pmadapter->pcard_sd->ioport) | CMD_PORT_SLCT; pmadapter->cmd_sent = MTRUE; ret = wlan_write_data_sync(pmadapter, pmbuf, cmd53_port); if (ret == MLAN_STATUS_FAILURE) PRINTM(MERROR, "Send Vdll: host_to_card failed: 0x%X\n", ret); else DBG_HEXDUMP(MIF_D, "SDIO Blk Wr", pmbuf->pbuf + pmbuf->data_offset, MIN(pmbuf->data_len, MAX_DATA_DUMP_LEN)); LEAVE(); return ret; } #endif /** * @brief This function sends data to the card. * * @param pmpriv A pointer to mlan_private structure * @param type data or command * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include * SDIO header) * @param tx_param A pointer to mlan_tx_param * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_sdio_host_to_card_ext(pmlan_private pmpriv, t_u8 type, mlan_buffer *pmbuf, mlan_tx_param *tx_param) { mlan_status ret = MLAN_STATUS_SUCCESS; mlan_adapter *pmadapter = pmpriv->adapter; #if (defined(SD9098) || defined(SD9097) || defined(SD9177)) if (type == MLAN_TYPE_VDLL) return wlan_sdio_send_vdll(pmadapter, pmbuf); #endif ret = wlan_sdio_host_to_card(pmadapter, type, pmbuf, tx_param); if (type == MLAN_TYPE_DATA && ret == MLAN_STATUS_FAILURE) pmadapter->data_sent = MFALSE; LEAVE(); return ret; } /** * @brief Deaggregate single port aggregation packet * * @param pmadapter A pointer to mlan_adapter structure * @param buf A pointer to aggregated data packet * @param len * * @return N/A */ void wlan_decode_spa_buffer(mlan_adapter *pmadapter, t_u8 *buf, t_u32 len) { int total_pkt_len; t_u8 block_num = 0; t_u16 block_size = 0; t_u8 *data; t_u32 pkt_len; mlan_buffer *mbuf_deaggr = MNULL; ENTER(); data = (t_u8 *)buf; total_pkt_len = len; if (total_pkt_len < pmadapter->pcard_sd->sdio_rx_block_size) { PRINTM(MERROR, "Invalid sp aggr packet size=%d\n", total_pkt_len); goto done; } while (total_pkt_len >= (OFFSET_OF_SDIO_HEADER + SDIO_INTF_HEADER_LEN)) { block_num = *(data + OFFSET_OF_BLOCK_NUMBER); block_size = pmadapter->pcard_sd->sdio_rx_block_size * block_num; if (block_size > total_pkt_len) { PRINTM(MERROR, "Error in pkt, block_num=%d, pkt_len=%d\n", block_num, total_pkt_len); break; } pkt_len = wlan_le16_to_cpu( *(t_u16 *)(data + OFFSET_OF_SDIO_HEADER)); if ((pkt_len + OFFSET_OF_SDIO_HEADER) > block_size) { PRINTM(MERROR, "Error in pkt, pkt_len=%d, block_size=%d\n", pkt_len, block_size); break; } mbuf_deaggr = wlan_alloc_mlan_buffer( pmadapter, pkt_len - SDIO_INTF_HEADER_LEN, MLAN_RX_HEADER_LEN, MOAL_ALLOC_MLAN_BUFFER); if (mbuf_deaggr == MNULL) { PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); break; } memcpy_ext(pmadapter, mbuf_deaggr->pbuf + mbuf_deaggr->data_offset, data + OFFSET_OF_SDIO_HEADER + SDIO_INTF_HEADER_LEN, pkt_len - SDIO_INTF_HEADER_LEN, pkt_len - SDIO_INTF_HEADER_LEN); mbuf_deaggr->data_len = pkt_len - SDIO_INTF_HEADER_LEN; wlan_handle_rx_packet(pmadapter, mbuf_deaggr); data += block_size; total_pkt_len -= block_size; if (total_pkt_len < pmadapter->pcard_sd->sdio_rx_block_size) break; } done: LEAVE(); return; } /** * @brief This function deaggr rx pkt * * @param pmadapter A pointer to mlan_adapter structure * @param pmbuf A pointer to the SDIO mpa data * @return N/A */ t_void wlan_sdio_deaggr_rx_pkt(pmlan_adapter pmadapter, mlan_buffer *pmbuf) { if (pmbuf->buf_type == MLAN_BUF_TYPE_SPA_DATA) { wlan_decode_spa_buffer(pmadapter, pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len); wlan_free_mlan_buffer(pmadapter, pmbuf); } else wlan_handle_rx_packet(pmadapter, pmbuf); } /** * @brief This function allocates buffer for the SDIO aggregation buffer * related members of adapter structure * * @param pmadapter A pointer to mlan_adapter structure * @param mpa_tx_buf_size Tx buffer size to allocate * @param mpa_rx_buf_size Rx buffer size to allocate * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_alloc_sdio_mpa_buffers(mlan_adapter *pmadapter, t_u32 mpa_tx_buf_size, t_u32 mpa_rx_buf_size) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; t_u8 mp_aggr_pkt_limit = pmadapter->pcard_sd->mp_aggr_pkt_limit; ENTER(); if ((pmadapter->pcard_sd->max_segs < mp_aggr_pkt_limit) || (pmadapter->pcard_sd->max_seg_size < pmadapter->pcard_sd->max_sp_tx_size)) { ret = pcb->moal_malloc( pmadapter->pmoal_handle, mpa_tx_buf_size + DMA_ALIGNMENT, MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&pmadapter->pcard_sd->mpa_tx.head_ptr); if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_sd->mpa_tx.head_ptr) { PRINTM(MERROR, "Could not allocate buffer for SDIO MP TX aggr\n"); ret = MLAN_STATUS_FAILURE; goto error; } pmadapter->pcard_sd->mpa_tx.buf = (t_u8 *)ALIGN_ADDR( pmadapter->pcard_sd->mpa_tx.head_ptr, DMA_ALIGNMENT); } else { PRINTM(MMSG, "wlan: Enable TX SG mode\n"); pmadapter->pcard_sd->mpa_tx.head_ptr = MNULL; pmadapter->pcard_sd->mpa_tx.buf = MNULL; } pmadapter->pcard_sd->mpa_tx.buf_size = mpa_tx_buf_size; if ((pmadapter->pcard_sd->max_segs < mp_aggr_pkt_limit) || (pmadapter->pcard_sd->max_seg_size < pmadapter->pcard_sd->max_sp_rx_size)) { ret = pcb->moal_malloc( pmadapter->pmoal_handle, mpa_rx_buf_size + DMA_ALIGNMENT, MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&pmadapter->pcard_sd->mpa_rx.head_ptr); if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_sd->mpa_rx.head_ptr) { PRINTM(MERROR, "Could not allocate buffer for SDIO MP RX aggr\n"); ret = MLAN_STATUS_FAILURE; goto error; } pmadapter->pcard_sd->mpa_rx.buf = (t_u8 *)ALIGN_ADDR( pmadapter->pcard_sd->mpa_rx.head_ptr, DMA_ALIGNMENT); } else { PRINTM(MMSG, "wlan: Enable RX SG mode\n"); pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; pmadapter->pcard_sd->mpa_rx.buf = MNULL; } pmadapter->pcard_sd->mpa_rx.buf_size = mpa_rx_buf_size; error: if (ret != MLAN_STATUS_SUCCESS) wlan_free_sdio_mpa_buffers(pmadapter); LEAVE(); return ret; } /** * @brief This function frees buffers for the SDIO aggregation * * @param pmadapter A pointer to mlan_adapter structure * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_free_sdio_mpa_buffers(mlan_adapter *pmadapter) { pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); if (pmadapter->pcard_sd->mpa_tx.buf) { pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter->pcard_sd->mpa_tx.head_ptr); pmadapter->pcard_sd->mpa_tx.head_ptr = MNULL; pmadapter->pcard_sd->mpa_tx.buf = MNULL; pmadapter->pcard_sd->mpa_tx.buf_size = 0; } if (pmadapter->pcard_sd->mpa_rx.buf) { pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter->pcard_sd->mpa_rx.head_ptr); pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; pmadapter->pcard_sd->mpa_rx.buf = MNULL; pmadapter->pcard_sd->mpa_rx.buf_size = 0; } LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function re-allocate rx mpa buffer * * @param pmadapter A pointer to mlan_adapter structure * * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_re_alloc_sdio_rx_mpa_buffer(mlan_adapter *pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; t_u8 mp_aggr_pkt_limit = pmadapter->pcard_sd->mp_aggr_pkt_limit; t_u32 mpa_rx_buf_size = pmadapter->pcard_sd->mp_tx_aggr_buf_size; if (pmadapter->pcard_sd->mpa_rx.buf) { pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter->pcard_sd->mpa_rx.head_ptr); pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; pmadapter->pcard_sd->mpa_rx.buf = MNULL; pmadapter->pcard_sd->mpa_rx.buf_size = 0; } if (pmadapter->pcard_sd->sdio_rx_aggr_enable) { mpa_rx_buf_size = MAX(mpa_rx_buf_size, SDIO_CMD53_MAX_SIZE); /** reallocate rx buffer for recover when single port rx * aggregation enabled */ if (pmadapter->pcard_sd->rx_buffer) { pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter->pcard_sd->rx_buffer); pmadapter->pcard_sd->rx_buffer = MNULL; pmadapter->pcard_sd->rx_buf = MNULL; } ret = pmadapter->callbacks.moal_malloc( pmadapter->pmoal_handle, SDIO_CMD53_MAX_SIZE + DMA_ALIGNMENT, MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&pmadapter->pcard_sd->rx_buffer); if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_sd->rx_buffer) { PRINTM(MERROR, "Failed to allocate receive buffer\n"); ret = MLAN_STATUS_FAILURE; goto error; } pmadapter->pcard_sd->rx_buf = (t_u8 *)ALIGN_ADDR( pmadapter->pcard_sd->rx_buffer, DMA_ALIGNMENT); } if ((pmadapter->pcard_sd->max_segs < mp_aggr_pkt_limit) || (pmadapter->pcard_sd->max_seg_size < pmadapter->pcard_sd->max_sp_rx_size)) { ret = pcb->moal_malloc( pmadapter->pmoal_handle, mpa_rx_buf_size + DMA_ALIGNMENT, MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&pmadapter->pcard_sd->mpa_rx.head_ptr); if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_sd->mpa_rx.head_ptr) { PRINTM(MERROR, "Could not allocate buffer for SDIO MP RX aggr\n"); ret = MLAN_STATUS_FAILURE; goto error; } pmadapter->pcard_sd->mpa_rx.buf = (t_u8 *)ALIGN_ADDR( pmadapter->pcard_sd->mpa_rx.head_ptr, DMA_ALIGNMENT); } else { PRINTM(MMSG, "wlan: Enable RX SG mode\n"); pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; pmadapter->pcard_sd->mpa_rx.buf = MNULL; } pmadapter->pcard_sd->mpa_rx.buf_size = mpa_rx_buf_size; PRINTM(MMSG, "mpa_rx_buf_size=%d\n", mpa_rx_buf_size); error: return ret; } /** * @brief This function wakes up the card. * * @param pmadapter A pointer to mlan_adapter structure * @param timeout set timeout flag * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_pm_sdio_wakeup_card(pmlan_adapter pmadapter, t_u8 timeout) { mlan_status ret = MLAN_STATUS_SUCCESS; t_u32 age_ts_usec; pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); PRINTM(MEVENT, "Wakeup device...\n"); pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &pmadapter->pm_wakeup_in_secs, &age_ts_usec); if (timeout) { pmadapter->callbacks.moal_start_timer( pmadapter->pmoal_handle, pmadapter->pwakeup_fw_timer, MFALSE, MRVDRV_TIMER_3S); pmadapter->wakeup_fw_timer_is_set = MTRUE; } ret = pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, HOST_POWER_UP); LEAVE(); return ret; } /** * @brief This function resets the PM setting of the card. * * @param pmadapter A pointer to mlan_adapter structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ static mlan_status wlan_pm_sdio_reset_card(pmlan_adapter pmadapter) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_callbacks pcb = &pmadapter->callbacks; ENTER(); ret = pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, 0); LEAVE(); return ret; } /** * @brief This function issues commands to initialize firmware * * @param priv A pointer to mlan_private structure * * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status wlan_set_sdio_gpio_int(pmlan_private priv) { mlan_status ret = MLAN_STATUS_SUCCESS; pmlan_adapter pmadapter = MNULL; HostCmd_DS_SDIO_GPIO_INT_CONFIG sdio_int_cfg; if (!priv) { LEAVE(); return MLAN_STATUS_FAILURE; } pmadapter = priv->adapter; ENTER(); if (pmadapter->pcard_sd->int_mode == INT_MODE_GPIO) { if (pmadapter->pcard_sd->gpio_pin != GPIO_INT_NEW_MODE) { PRINTM(MINFO, "SDIO_GPIO_INT_CONFIG: interrupt mode is GPIO\n"); sdio_int_cfg.action = HostCmd_ACT_GEN_SET; sdio_int_cfg.gpio_pin = pmadapter->pcard_sd->gpio_pin; sdio_int_cfg.gpio_int_edge = INT_FALLING_EDGE; sdio_int_cfg.gpio_pulse_width = DELAY_1_US; ret = wlan_prepare_cmd(priv, HostCmd_CMD_SDIO_GPIO_INT_CONFIG, HostCmd_ACT_GEN_SET, 0, MNULL, &sdio_int_cfg); if (ret) { PRINTM(MERROR, "SDIO_GPIO_INT_CONFIG: send command fail\n"); ret = MLAN_STATUS_FAILURE; } } } else { PRINTM(MINFO, "SDIO_GPIO_INT_CONFIG: interrupt mode is SDIO\n"); } LEAVE(); return ret; } /** * @brief This function prepares command of SDIO GPIO interrupt * * @param pmpriv A pointer to mlan_private structure * @param cmd A pointer to HostCmd_DS_COMMAND structure * @param cmd_action The action: GET or SET * @param pdata_buf A pointer to data buffer * @return MLAN_STATUS_SUCCESS */ mlan_status wlan_cmd_sdio_gpio_int(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, t_void *pdata_buf) { HostCmd_DS_SDIO_GPIO_INT_CONFIG *psdio_gpio_int = &cmd->params.sdio_gpio_int; ENTER(); cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SDIO_GPIO_INT_CONFIG); cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG)) + S_DS_GEN); memset(pmpriv->adapter, psdio_gpio_int, 0, sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG)); if (cmd_action == HostCmd_ACT_GEN_SET) { memcpy_ext(pmpriv->adapter, psdio_gpio_int, pdata_buf, sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG), sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG)); psdio_gpio_int->action = wlan_cpu_to_le16(psdio_gpio_int->action); psdio_gpio_int->gpio_pin = wlan_cpu_to_le16(psdio_gpio_int->gpio_pin); psdio_gpio_int->gpio_int_edge = wlan_cpu_to_le16(psdio_gpio_int->gpio_int_edge); psdio_gpio_int->gpio_pulse_width = wlan_cpu_to_le16(psdio_gpio_int->gpio_pulse_width); } LEAVE(); return MLAN_STATUS_SUCCESS; } mlan_status wlan_reset_fw(pmlan_adapter pmadapter) { t_u32 tries = 0; t_u32 value = 1; t_u32 reset_reg = pmadapter->pcard_sd->reg->fw_reset_reg; t_u8 reset_val = pmadapter->pcard_sd->reg->fw_reset_val; pmlan_callbacks pcb = &pmadapter->callbacks; mlan_status ret = MLAN_STATUS_SUCCESS; ENTER(); wlan_pm_sdio_wakeup_card(pmadapter, MFALSE); /** wait SOC fully wake up */ for (tries = 0; tries < MAX_POLL_TRIES; ++tries) { if (MLAN_STATUS_SUCCESS == pcb->moal_write_reg(pmadapter->pmoal_handle, reset_reg, 0xba)) { pcb->moal_read_reg(pmadapter->pmoal_handle, reset_reg, &value); if (value == 0xba) { PRINTM(MMSG, "FW wake up\n"); break; } } pcb->moal_udelay(pmadapter->pmoal_handle, 1000); } /* Write register to notify FW */ if (MLAN_STATUS_FAILURE == pcb->moal_write_reg(pmadapter->pmoal_handle, reset_reg, reset_val)) { PRINTM(MERROR, "Failed to write register.\n"); ret = MLAN_STATUS_FAILURE; goto done; } #if defined(SD8997) || defined(SD8977) || defined(SD8987) || \ defined(SD9098) || defined(SD9097) || defined(SD8978) || \ defined(SD9177) if (MFALSE #ifdef SD8997 || IS_SD8997(pmadapter->card_type) #endif #ifdef SD8977 || IS_SD8977(pmadapter->card_type) #endif #ifdef SD8978 || IS_SD8978(pmadapter->card_type) #endif #ifdef SD8987 || IS_SD8987(pmadapter->card_type) #endif #ifdef SD9098 || IS_SD9098(pmadapter->card_type) #endif #ifdef SD9097 || IS_SD9097(pmadapter->card_type) #endif #ifdef SD9177 || IS_SD9177(pmadapter->card_type) #endif ) { pcb->moal_read_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, &value); pcb->moal_write_reg(pmadapter->pmoal_handle, HOST_TO_CARD_EVENT_REG, value | HOST_POWER_UP); } #endif /* Poll register around 100 ms */ for (tries = 0; tries < MAX_POLL_TRIES; ++tries) { pcb->moal_read_reg(pmadapter->pmoal_handle, reset_reg, &value); if (value == 0) /* FW is ready */ break; pcb->moal_udelay(pmadapter->pmoal_handle, 1000); } if (value) { PRINTM(MERROR, "Failed to poll FW reset register %X=0x%x\n", reset_reg, value); ret = MLAN_STATUS_FAILURE; goto done; } PRINTM(MMSG, "FW Reset success\n"); ret = wlan_sdio_probe(pmadapter); done: LEAVE(); return ret; } /** * @brief This function handle event/data/cmd complete * * @param pmadapter A pointer to mlan_adapter structure * @param pmbuf A pointer to the mlan_buffer * @return N/A */ static mlan_status wlan_sdio_data_evt_complete(pmlan_adapter pmadapter, mlan_buffer *pmbuf, mlan_status status) { ENTER(); wlan_free_mlan_buffer(pmadapter, pmbuf); LEAVE(); return MLAN_STATUS_SUCCESS; } /** * @brief This function handle receive packet * * @param pmadapter A pointer to mlan_adapter structure * @param pmbuf A pointer to the mlan_buffer * @return */ static mlan_status wlan_sdio_handle_rx_packet(mlan_adapter *pmadapter, pmlan_buffer pmbuf) { ENTER(); wlan_sdio_deaggr_rx_pkt(pmadapter, pmbuf); LEAVE(); return MLAN_STATUS_SUCCESS; } mlan_adapter_operations mlan_sdio_ops = { .dnld_fw = wlan_sdio_dnld_fw, .interrupt = wlan_sdio_interrupt, .process_int_status = wlan_process_sdio_int_status, .host_to_card = wlan_sdio_host_to_card_ext, .wakeup_card = wlan_pm_sdio_wakeup_card, .reset_card = wlan_pm_sdio_reset_card, .event_complete = wlan_sdio_data_evt_complete, .data_complete = wlan_sdio_data_evt_complete, .cmdrsp_complete = wlan_sdio_data_evt_complete, .handle_rx_packet = wlan_sdio_handle_rx_packet, .disable_host_int = wlan_disable_sdio_host_int, .enable_host_int = wlan_enable_sdio_host_int, .intf_header_len = SDIO_INTF_HEADER_LEN, };