summaryrefslogtreecommitdiff
path: root/mwl_fwdl.c
blob: 442c21cfddb743d34dea6dcd2de4a43e2239d393 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
* Copyright (c) 2006-2015 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 firmware download 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);

		/* this is arbitrary per your platform; we use 0xffff */
		curr_iteration = FW_MAX_NUM_CHECKS;

		/* 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 for 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 for fw signature");
		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);
}