diff options
Diffstat (limited to 'mwl_fwdl.c')
-rw-r--r-- | mwl_fwdl.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/mwl_fwdl.c b/mwl_fwdl.c new file mode 100644 index 0000000..5f9ef91 --- /dev/null +++ b/mwl_fwdl.c @@ -0,0 +1,219 @@ +/* +* Copyright (c) 2006-2014 Marvell International Ltd. +* +* Permission to use, copy, modify, and/or distribute this software for any +* purpose with or without fee is hereby granted, provided that the above +* copyright notice and this permission notice appear in all copies. +* +* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* +* +* Description: This file implements frimware downloa related functions. +* +*/ + +#include <linux/io.h> + +#include "mwl_sysadpt.h" +#include "mwl_dev.h" +#include "mwl_debug.h" +#include "mwl_fwcmd.h" +#include "mwl_fwdl.h" + +/* CONSTANTS AND MACROS +*/ + +#define FW_DOWNLOAD_BLOCK_SIZE 256 +#define FW_CHECK_MSECS 1 + +#define FW_MAX_NUM_CHECKS 0xffff + +/* PRIVATE FUNCTION DECLARATION +*/ + +static void mwl_fwdl_trig_pcicmd(struct mwl_priv *priv); +static void mwl_fwdl_trig_pcicmd_bootcode(struct mwl_priv *priv); + +/* PUBLIC FUNCTION DEFINITION +*/ + +int mwl_fwdl_download_firmware(struct ieee80211_hw *hw) +{ + struct mwl_priv *priv; + const struct firmware *fw; + u32 curr_iteration = 0; + u32 size_fw_downloaded = 0; + u32 int_code = 0; + u32 len = 0; + + WLDBG_ENTER(DBG_LEVEL_1); + + BUG_ON(!hw); + priv = hw->priv; + BUG_ON(!priv); + fw = priv->fw_ucode; + BUG_ON(!fw); + + mwl_fwcmd_reset(hw); + + /* FW before jumping to boot rom, it will enable PCIe transaction retry, wait for boot code to stop it. + */ + WL_MSEC_SLEEP(FW_CHECK_MSECS); + + writel(MACREG_A2HRIC_BIT_MASK, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CLEAR_SEL); + writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_CAUSE); + writel(0x00, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_MASK); + writel(MACREG_A2HRIC_BIT_MASK, priv->iobase1 + MACREG_REG_A2H_INTERRUPT_STATUS_MASK); + + /* this routine interacts with SC2 bootrom to download firmware binary + * to the device. After DMA'd to SC2, the firmware could be deflated to reside + * on its respective blocks such as ITCM, DTCM, SQRAM, + * (or even DDR, AFTER DDR is init'd before fw download + */ + WLDBG_PRINT("fw download start 88"); + + /* Disable PFU before FWDL + */ + writel(0x100, priv->iobase1 + 0xE0E4); + + /* make sure SCRATCH2 C40 is clear, in case we are too quick + */ + while (readl(priv->iobase1 + 0xc40) == 0); + + while (size_fw_downloaded < fw->size) { + + len = readl(priv->iobase1 + 0xc40); + + if (!len) + break; + + /* this copies the next chunk of fw binary to be delivered + */ + memcpy((char *)&priv->pcmd_buf[0], (fw->data + size_fw_downloaded), len); + + /* this function writes pdata to c10, then write 2 to c18 + */ + mwl_fwdl_trig_pcicmd_bootcode(priv); + + curr_iteration = FW_MAX_NUM_CHECKS; /* this is arbitrary per your platform; we use 0xffff */ + + /* NOTE: the following back to back checks on C1C is time sensitive, hence + * may need to be tweaked dependent on host processor. Time for SC2 to go from + * the write of event 2 to C1C == 2 is ~1300 nSec. Hence the checkings on host + * has to consider how efficient your code can be to meet this timing, or you + * can alternatively tweak this routines to fit your platform + */ + do { + + int_code = readl(priv->iobase1 + 0xc1c); + if (int_code != 0) + break; + curr_iteration--; + + } while (curr_iteration); + + do { + + int_code = readl(priv->iobase1 + 0xc1c); + if ((int_code & MACREG_H2ARIC_BIT_DOOR_BELL) != MACREG_H2ARIC_BIT_DOOR_BELL) + break; + curr_iteration--; + + } while (curr_iteration); + + if (curr_iteration == 0) { + + /* This limited loop check allows you to exit gracefully without locking up + * your entire system just because fw download failed + */ + WLDBG_PRINT("Exhausted curr_iteration during fw download"); + goto err_download; + } + + size_fw_downloaded += len; + } + + WLDBG_PRINT("FwSize = %d downloaded Size = %d curr_iteration %d", + (int)fw->size, size_fw_downloaded, curr_iteration); + + /* Now firware is downloaded successfully, so this part is to check + * whether fw can properly execute to an extent that write back signature + * to indicate its readiness to the host. NOTE: if your downloaded fw crashes, + * this signature checking will fail. This part is similar as SC1 + */ + writew(0x00, &priv->pcmd_buf[1]); + mwl_fwdl_trig_pcicmd(priv); + curr_iteration = FW_MAX_NUM_CHECKS; + do { + + curr_iteration--; + writel(HOSTCMD_SOFTAP_MODE, priv->iobase1 + MACREG_REG_GEN_PTR); + WL_MSEC_SLEEP(FW_CHECK_MSECS); + int_code = readl(priv->iobase1 + MACREG_REG_INT_CODE); + if (!(curr_iteration % 0xff)) + WLDBG_PRINT("%x;", int_code); + + } while ((curr_iteration) && (int_code != HOSTCMD_SOFTAP_FWRDY_SIGNATURE)); + + if (curr_iteration == 0) { + + WLDBG_PRINT("Exhausted curr_iteration waiting for fw signature; firmware seems failed to operate"); + goto err_download; + } + + WLDBG_PRINT("complete"); + writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE); + + WLDBG_EXIT(DBG_LEVEL_1); + + return 0; + +err_download: + + mwl_fwcmd_reset(hw); + + return -EIO; +} + +/* PRIVATE FUNCTION DEFINITION +*/ + +static void mwl_fwdl_trig_pcicmd(struct mwl_priv *priv) +{ + WLDBG_ENTER(DBG_LEVEL_1); + + BUG_ON(!priv); + + writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR); + + writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE); + + writel(MACREG_H2ARIC_BIT_DOOR_BELL, + priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); + + WLDBG_EXIT(DBG_LEVEL_1); +} + +static void mwl_fwdl_trig_pcicmd_bootcode(struct mwl_priv *priv) +{ + WLDBG_ENTER(DBG_LEVEL_1); + + BUG_ON(!priv); + + writel(priv->pphys_cmd_buf, priv->iobase1 + MACREG_REG_GEN_PTR); + + writel(0x00, priv->iobase1 + MACREG_REG_INT_CODE); + + writel(MACREG_H2ARIC_BIT_DOOR_BELL, + priv->iobase1 + MACREG_REG_H2A_INTERRUPT_EVENTS); + + WLDBG_EXIT(DBG_LEVEL_1); +} |