diff options
Diffstat (limited to 'drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_sdio.c')
-rw-r--r-- | drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_sdio.c | 3013 |
1 files changed, 3013 insertions, 0 deletions
diff --git a/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_sdio.c b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_sdio.c new file mode 100644 index 000000000000..b88c4fdb3ad8 --- /dev/null +++ b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_sdio.c @@ -0,0 +1,3013 @@ +/** @file mlan_sdio.c + * + * @brief This file contains SDIO specific code + * + * + * Copyright 2014-2020 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 +********************************************************/ + +/******************************************************** + 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(); + + pmadapter->pcard_sd->ioport = MEM_PORT; + PRINTM(MINFO, "SDIO FUNC1 IO port: 0x%x\n", + pmadapter->pcard_sd->ioport); + + /* 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 = MAX_PORT; + + ENTER(); + + PRINTM(MIF_D, "wlan_get_rd_port: mp_rd_bitmap=0x%08x\n", rd_bitmap); + + if (!(rd_bitmap & reg->data_port_mask)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + 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; + + 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; + } + + 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 < MAX_POLL_TRIES; 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 + /* 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; + + 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) { + 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_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 + */ +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 + */ +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; + + /* 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; + } + + 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; + 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; + + ENTER(); + + 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 (pmadapter->pcard_sd->mp_rd_bitmap & reg->data_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 */ + MP_RX_AGGR_SETUP(pmadapter, pmbuf, port, rx_len); + + if (MP_RX_AGGR_PKT_LIMIT_REACHED(pmadapter) || + MP_RX_AGGR_PORT_LIMIT_REACHED(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 (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); + } + if (f_post_aggr_cur) { + PRINTM(MINFO, "Current packet aggregation.\n"); + /* Curr pkt can be aggregated */ + MP_RX_AGGR_SETUP(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 = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + 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; + } + + 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; + + 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 = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + 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) { + MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); + aggr_sg = MTRUE; + } else { + MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); + } + if (MP_TX_AGGR_PKT_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) { + MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); + aggr_sg = MTRUE; + } else { + MP_TX_AGGR_BUF_PUT(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 + * + */ +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(); + + 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 + */ +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 + */ +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 + */ +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; + } +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; + } + + switch (card_type) { +#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 + default: + PRINTM(MERROR, "can't get right card type \n"); + ret = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function gets interrupt status. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_sdio_interrupt(t_u16 msg_id, pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer mbuf; + t_u32 sdio_ireg = 0; + t_u8 offset = 0; + t_u8 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); + + if (MLAN_STATUS_SUCCESS != + pcb->moal_read_data_sync(pmadapter->pmoal_handle, &mbuf, + (REG_PORT + offset) | + MLAN_SDIO_BYTE_MODE_MASK, + 0)) { + PRINTM(MERROR, + "moal_read_data_sync: read registers failed\n"); + pmadapter->dbg.num_int_read_failure++; + goto done; + } + offset += mbuf.data_len; + max_mp_regs -= mbuf.data_len; + } + + 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 (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 + */ +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; + + 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; + + /* 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; + 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))) { + PRINTM(MINFO, " <--- Tx DONE Interrupt --->\n"); + pmadapter->data_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; + 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; + PRINTM(MINTR, "UPLD: rd_bitmap=0x%08x\n", + pmadapter->pcard_sd->mp_rd_bitmap); + + 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 (rx_len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) + 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)) { + 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; + + 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 (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; + cmd53_port = (pmadapter->pcard_sd->ioport) | CMD_PORT_SLCT; + 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)) +/** + * @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 + */ +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 + */ +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)) + 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, pkt_type = 0; + 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)); + pkt_type = wlan_le16_to_cpu( + *(t_u16 *)(data + OFFSET_OF_SDIO_HEADER + 2)); + 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 = SDIO_MP_AGGR_DEF_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 = SDIO_MP_AGGR_DEF_PKT_LIMIT; + t_u32 mpa_rx_buf_size = SDIO_MP_AGGR_BUF_SIZE_MAX; + + 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 + */ +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 + */ +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) + 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 + ) { + 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 + */ +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 + */ +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, + + .intf_header_len = SDIO_INTF_HEADER_LEN, +}; |