summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlan/mlan_sdio.c
diff options
context:
space:
mode:
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.c3013
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,
+ &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, &reg)) {
+ 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, &reg)) {
+ 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, &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,
+ &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,
+};