summaryrefslogtreecommitdiff
path: root/mwl_fwdl.c
diff options
context:
space:
mode:
Diffstat (limited to 'mwl_fwdl.c')
-rw-r--r--mwl_fwdl.c219
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);
+}