MCUXpresso_MIMXRT1021xxxxx/middleware/wifi_nxp/sdio_nxp_abs/fwdnld_sdio.c
Yilin Sun 763d32be90
Updated SDK to v2.15.000
Signed-off-by: Yilin Sun <imi415@imi.moe>
2024-03-15 22:23:36 +08:00

381 lines
9.8 KiB
C

/** @file fwdnld_sdio.c
*
* @brief This file provides interface abstraction APIs for SDIO
*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <string.h>
#include <fsl_os_abstraction.h>
#include "fwdnld_intf_abs.h"
#include "fwdnld_sdio.h"
#include "mlan_sdio_defs.h"
#include <mlan_sdio_api.h>
#include "sdio.h"
fwdnld_intf_t sdio_intf_g;
fwdnld_sdio_intf_specific sdio_intf_specific_g;
/*
* FW dnld blocksize set 0x110 to 0 and 0x111 to 0x01 => 0x100 => 256
* Note this only applies to the blockmode we use 256 bytes
* as block because MLAN_SDIO_BLOCK_SIZE = 256
*/
static fwdnld_intf_ret_t wlan_set_fw_dnld_size(void)
{
uint32_t resp;
bool rv = sdio_drv_creg_write(FN1_BLOCK_SIZE_0, 0, 0, &resp);
if (rv == false)
{
return FWDNLD_INTF_FAIL;
}
rv = sdio_drv_creg_write(FN1_BLOCK_SIZE_1, 0, 1, &resp);
if (rv == false)
{
return FWDNLD_INTF_FAIL;
}
return FWDNLD_INTF_SUCCESS;
}
static void wlan_card_fw_status(t_u16 *dat)
{
uint32_t resp = 0;
(void)sdio_drv_creg_read(CARD_FW_STATUS0_REG, 1, &resp);
*dat = (t_u16)(resp & 0xffU);
(void)sdio_drv_creg_read(CARD_FW_STATUS1_REG, 1, &resp);
*dat |= (t_u16)((resp & 0xffU) << 8);
}
static bool wlan_sdio_check_fw_status(t_u32 card_poll)
{
t_u16 dat = 0U;
t_u32 i = 0U;
for (i = 0; i < card_poll; i++)
{
(void)wlan_card_fw_status(&dat);
if (dat == FIRMWARE_READY)
{
sdio_io_d("Firmware Ready");
return true;
}
OSA_TimeDelay(5U);
}
return false;
}
#ifdef CONFIG_WIFI_IND_DNLD
/** @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 int32_t wlan_sdio_disable_host_int_mask()
{
uint32_t host_int_mask = 0;
uint32_t resp;
(void)sdio_drv_creg_read(HOST_INT_MASK_REG, 1, &host_int_mask);
/* Update with the mask and write back to the register */
host_int_mask &= ~HIM_DISABLE;
(void)sdio_drv_creg_write(HOST_INT_MASK_REG, 1, host_int_mask, &resp);
return FWDNLD_INTF_SUCCESS;
}
/**
* @brief This function probes the driver
*
* @param pmadapter A pointer to mlan_adapter structure
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static int32_t wlan_sdio_probe()
{
int32_t ret = FWDNLD_INTF_SUCCESS;
uint32_t sdio_ireg = 0;
uint32_t host_int_mask = 0;
uint32_t resp;
/*
* 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.
*/
(void)sdio_drv_creg_read(HOST_INT_STATUS_REG, 1, &sdio_ireg);
(void)sdio_drv_creg_read(HOST_INT_MASK_REG, 1, &host_int_mask);
/* Update with the mask and write back to the register */
host_int_mask &= ~HIM_DISABLE;
(void)sdio_drv_creg_write(HOST_INT_MASK_REG, 1, host_int_mask, &resp);
/* Get SDIO ioport */
ret = sdio_ioport_init();
return ret;
}
int32_t wlan_reset_fw()
{
t_u32 tries = 0;
int32_t ret = FWDNLD_INTF_SUCCESS;
bool rv;
uint32_t resp;
// wlan_pm_sdio_wakeup_card(pmadapter, MFALSE);
rv = sdio_drv_creg_write(HOST_TO_CARD_EVENT_REG, 1, HOST_POWER_UP, &resp);
if (rv == false)
{
return FWDNLD_INTF_FAIL;
}
/** wait SOC fully wake up */
for (tries = 0; tries < MAX_POLL_TRIES; ++tries)
{
rv = sdio_drv_creg_write(CARD_FW_RESET_REG, 1, 0xba, &resp);
if (rv == true)
{
(void)sdio_drv_creg_read(CARD_FW_RESET_REG, 1, &resp);
if (resp == 0xba)
{
sdio_io_d("FW wake up");
break;
}
}
OSA_TimeDelay(5U);
}
/* Write register to notify FW */
rv = sdio_drv_creg_write(CARD_FW_RESET_REG, 1, CARD_FW_RESET_VAL, &resp);
if (rv == false)
{
sdio_io_d("Failed to write register.");
ret = FWDNLD_INTF_FAIL;
goto done;
}
#if defined(SD8978) || defined(SD8987) || defined(SD9177)
(void)sdio_drv_creg_read(HOST_TO_CARD_EVENT_REG, 1, &resp);
rv = sdio_drv_creg_write(HOST_TO_CARD_EVENT_REG, 1, resp | HOST_POWER_UP, &resp);
if (rv == false)
{
sdio_io_e("Failed to write register.");
ret = FWDNLD_INTF_FAIL;
goto done;
}
#endif
/* Poll register around 100 ms */
for (tries = 0; tries < MAX_POLL_TRIES; ++tries)
{
(void)sdio_drv_creg_read(CARD_FW_RESET_REG, 1, &resp);
if (resp == 0)
{
/* FW is ready */
sdio_io_d("FW is ready");
break;
}
OSA_TimeDelay(5U);
}
if (resp)
{
sdio_io_e("Failed to poll FW reset register %X=0x%x", CARD_FW_RESET_REG, resp);
ret = FWDNLD_INTF_FAIL;
goto done;
}
sdio_io_d("FW Reset success");
ret = wlan_sdio_probe();
done:
return ret;
}
#endif
static fwdnld_intf_ret_t sdio_prep_for_fwdnld(fwdnld_intf_t *intf, void *settings)
{
/* set fw download block size */
return wlan_set_fw_dnld_size();
}
static fwdnld_intf_ret_t sdio_post_fwdnld_check_conn_ready(fwdnld_intf_t *intf, void *settings)
{
if (wlan_sdio_check_fw_status(1000) != true)
{
sdio_io_e("SDIO - FW Ready Registers not set");
return FWDNLD_INTF_FAIL;
}
else
{
sdio_io_d("WLAN FW download Successful");
return FWDNLD_INTF_SUCCESS;
}
}
#if defined(CONFIG_WIFI_IND_DNLD)
static fwdnld_intf_ret_t sdio_fwdnld_check_reload(fwdnld_intf_t *intf, uint8_t fw_reload)
{
t_u32 poll_num = 10;
int32_t ret;
/* Check if firmware is already running */
ret = wlan_sdio_check_fw_status(poll_num);
if (ret == true)
{
if (fw_reload == FW_RELOAD_SDIO_INBAND_RESET)
{
sdio_io_d("Try reset fw in mlan");
ret = wlan_reset_fw();
if (ret == FWDNLD_INTF_FAIL)
{
sdio_io_e("FW reset failure!");
return FWDNLD_INTF_FAIL;
}
}
else
{
PRINTF("WLAN FW already running! Skip FW download\r\n");
PRINTF("FW download skipped because fly wire is not connected from MCU to Wi-Fi SoC\r\n");
return FWDNLD_INTF_SKIP;
}
}
else if (fw_reload == FW_RELOAD_NO_EMULATION)
{
ret = wlan_sdio_disable_host_int_mask();
}
return FWDNLD_INTF_SUCCESS;
}
#endif
static fwdnld_intf_ret_t sdio_interface_send(fwdnld_intf_t *intf,
const uint8_t *buffer,
uint32_t transfer_len,
uint32_t *len)
{
uint32_t tx_blocks = 0, txlen = 0, buflen = 0, offset = 0;
uint32_t outbuf_len;
uint8_t *loutbuf = NULL;
uint32_t resp;
uint32_t tries = 0, ioport;
fwdnld_intf_ret_t ret = FWDNLD_INTF_SUCCESS;
loutbuf = GET_INTF_OUTBUF(intf);
outbuf_len = GET_INTF_OUTBUFLEN(intf);
(void)memset(loutbuf, 0, outbuf_len);
*len = 0;
do
{
/* Read CARD_STATUS_REG (0X30) FN =1 */
for (tries = 0; tries < MAX_POLL_TRIES; tries++)
{
if (wlan_card_status(DN_LD_CARD_RDY | CARD_IO_READY) == true)
{
*len = wlan_card_read_f1_base_regs();
}
else
{
sdio_io_e("Error in wlan_card_status()");
break;
}
// (void)PRINTF("len %d =>", len);
if (*len != 0U)
{
break;
}
else if (offset > 0)
{
return FWDNLD_INTF_SUCCESS;
}
else
{ /* Do Nothing */
}
}
if (*len == 0U)
{
if (offset > 0)
{
return FWDNLD_INTF_SUCCESS;
}
sdio_io_e("Card timeout %s:%d", __func__, __LINE__);
return FWDNLD_INTF_FAIL;
}
else if (*len > outbuf_len)
{
sdio_io_e("FW Download Failure. Invalid len");
return FWDNLD_INTF_FAIL;
}
else
{ /* Do Nothing */
}
txlen = *len;
/* Set blocksize to transfer - checking for last block */
if (transfer_len < txlen)
{
txlen = transfer_len;
*len = txlen;
}
ioport = GET_INTF_SDIO_IOPORT(intf);
(void)memcpy((void *)loutbuf, (const void *)(buffer + offset), txlen);
calculate_sdio_write_params(txlen, (unsigned int *)&tx_blocks, (unsigned int *)&buflen);
(void)sdio_drv_write(ioport, 1, tx_blocks, buflen, (uint8_t *)loutbuf, &resp);
if (*len <= transfer_len)
{
transfer_len -= *len;
offset += *len;
}
else
{
ret = FWDNLD_INTF_FAIL;
break;
}
*len = 0;
} while (transfer_len > 0);
return ret;
}
fwdnld_intf_t *sdio_init_interface(void *settings)
{
int32_t ret;
ret = sdio_init();
if (ret != 0)
{
return NULL;
}
sdio_intf_g.intf_s.fwdnld_intf_send = sdio_interface_send;
sdio_intf_g.intf_s.fwdnld_intf_prepare = sdio_prep_for_fwdnld;
sdio_intf_g.intf_s.fwdnld_intf_check_ready = sdio_post_fwdnld_check_conn_ready;
#if defined(CONFIG_WIFI_IND_DNLD)
sdio_intf_g.intf_s.fwdnld_intf_check_reload = sdio_fwdnld_check_reload;
#endif
sdio_intf_g.intf_s.outbuf = wifi_get_sdio_outbuf(&sdio_intf_g.intf_s.outbuf_len);
sdio_intf_g.intf_s.intf_specific = &sdio_intf_specific_g;
ret = sdio_ioport_init();
if (ret != 0)
{
return NULL;
}
sdio_intf_specific_g.ioport = wifi_get_sdio_ioport();
return &sdio_intf_g;
}