Commit | Line | Data |
---|---|---|
8fc8598e JC |
1 | /************************************************************************************************** |
2 | * Procedure: Init boot code/firmware code/data session | |
3 | * | |
9b0131cb | 4 | * Description: This routine will initialize firmware. If any error occurs during the initialization |
35997ff0 | 5 | * process, the routine shall terminate immediately and return fail. |
8fc8598e JC |
6 | * NIC driver should call NdisOpenFile only from MiniportInitialize. |
7 | * | |
8 | * Arguments: The pointer of the adapter | |
9 | ||
10 | * Returns: | |
11 | * NDIS_STATUS_FAILURE - the following initialization process should be terminated | |
12 | * NDIS_STATUS_SUCCESS - if firmware initialization process success | |
13 | **************************************************************************************************/ | |
2addf798 | 14 | |
8fc8598e JC |
15 | #include "r8192U.h" |
16 | #include "r8192U_hw.h" | |
17 | #include "r819xU_firmware_img.h" | |
18 | #include "r819xU_firmware.h" | |
8fc8598e | 19 | #include <linux/firmware.h> |
2cc817c0 AR |
20 | |
21 | static void firmware_init_param(struct net_device *dev) | |
8fc8598e | 22 | { |
35997ff0 | 23 | struct r8192_priv *priv = ieee80211_priv(dev); |
8fc8598e JC |
24 | rt_firmware *pfirmware = priv->pFirmware; |
25 | ||
26 | pfirmware->cmdpacket_frag_thresold = GET_COMMAND_PACKET_FRAG_THRESHOLD(MAX_TRANSMIT_BUFFER_SIZE); | |
27 | } | |
28 | ||
29 | /* | |
30 | * segment the img and use the ptr and length to remember info on each segment | |
31 | * | |
32 | */ | |
2cc817c0 AR |
33 | static bool fw_download_code(struct net_device *dev, u8 *code_virtual_address, |
34 | u32 buffer_len) | |
8fc8598e JC |
35 | { |
36 | struct r8192_priv *priv = ieee80211_priv(dev); | |
35997ff0 | 37 | bool rt_status = true; |
8fc8598e JC |
38 | u16 frag_threshold; |
39 | u16 frag_length, frag_offset = 0; | |
8fc8598e JC |
40 | int i; |
41 | ||
42 | rt_firmware *pfirmware = priv->pFirmware; | |
43 | struct sk_buff *skb; | |
44 | unsigned char *seg_ptr; | |
45 | cb_desc *tcb_desc; | |
46 | u8 bLastIniPkt; | |
095ff453 | 47 | u8 index; |
8fc8598e JC |
48 | |
49 | firmware_init_param(dev); | |
6f438042 | 50 | /* Fragmentation might be required */ |
8fc8598e JC |
51 | frag_threshold = pfirmware->cmdpacket_frag_thresold; |
52 | do { | |
14bc0d4f | 53 | if ((buffer_len - frag_offset) > frag_threshold) { |
bbfd888d | 54 | frag_length = frag_threshold; |
8fc8598e JC |
55 | bLastIniPkt = 0; |
56 | ||
57 | } else { | |
58 | frag_length = buffer_len - frag_offset; | |
59 | bLastIniPkt = 1; | |
60 | ||
61 | } | |
62 | ||
63 | /* Allocate skb buffer to contain firmware info and tx descriptor info | |
64 | * add 4 to avoid packet appending overflow. | |
65 | * */ | |
8fc8598e | 66 | skb = dev_alloc_skb(USB_HWDESC_HEADER_LEN + frag_length + 4); |
f8518efa XR |
67 | if (!skb) |
68 | return false; | |
e60b6538 | 69 | memcpy((unsigned char *)(skb->cb), &dev, sizeof(dev)); |
12fbccbe | 70 | tcb_desc = (cb_desc *)(skb->cb + MAX_DEV_ADDR_SIZE); |
8fc8598e JC |
71 | tcb_desc->queue_index = TXCMD_QUEUE; |
72 | tcb_desc->bCmdOrInit = DESC_PACKET_TYPE_INIT; | |
73 | tcb_desc->bLastIniPkt = bLastIniPkt; | |
74 | ||
8fc8598e | 75 | skb_reserve(skb, USB_HWDESC_HEADER_LEN); |
8fc8598e JC |
76 | seg_ptr = skb->data; |
77 | /* | |
78 | * Transform from little endian to big endian | |
e406322b | 79 | * and pending zero |
8fc8598e | 80 | */ |
0bfacefd RB |
81 | for (i = 0; i < frag_length; i += 4) { |
82 | *seg_ptr++ = ((i+0) < frag_length)?code_virtual_address[i+3] : 0; | |
83 | *seg_ptr++ = ((i+1) < frag_length)?code_virtual_address[i+2] : 0; | |
84 | *seg_ptr++ = ((i+2) < frag_length)?code_virtual_address[i+1] : 0; | |
85 | *seg_ptr++ = ((i+3) < frag_length)?code_virtual_address[i+0] : 0; | |
8fc8598e | 86 | } |
0bfacefd | 87 | tcb_desc->txbuf_size = (u16)i; |
8fc8598e JC |
88 | skb_put(skb, i); |
89 | ||
095ff453 RB |
90 | index = tcb_desc->queue_index; |
91 | if (!priv->ieee80211->check_nic_enough_desc(dev, index) || | |
92 | (!skb_queue_empty(&priv->ieee80211->skb_waitQ[index])) || | |
93 | (priv->ieee80211->queue_stop)) { | |
bd1ccd33 | 94 | RT_TRACE(COMP_FIRMWARE, "=====================================================> tx full!\n"); |
8fc8598e JC |
95 | skb_queue_tail(&priv->ieee80211->skb_waitQ[tcb_desc->queue_index], skb); |
96 | } else { | |
0b4ef0a6 | 97 | priv->ieee80211->softmac_hard_start_xmit(skb, dev); |
8fc8598e JC |
98 | } |
99 | ||
100 | code_virtual_address += frag_length; | |
101 | frag_offset += frag_length; | |
102 | ||
32b116ed | 103 | } while (frag_offset < buffer_len); |
8fc8598e JC |
104 | |
105 | return rt_status; | |
106 | ||
8fc8598e JC |
107 | } |
108 | ||
6f438042 TC |
109 | /* |
110 | * Procedure: Check whether main code is download OK. If OK, turn on CPU | |
111 | * | |
112 | * Description: CPU register locates in different page against general register. | |
113 | * Switch to CPU register in the begin and switch back before return | |
114 | * | |
115 | * | |
116 | * Arguments: The pointer of the adapter | |
117 | * | |
118 | * Returns: | |
119 | * NDIS_STATUS_FAILURE - the following initialization process should | |
120 | * be terminated | |
121 | * NDIS_STATUS_SUCCESS - if firmware initialization process success | |
122 | */ | |
2cc817c0 | 123 | static bool CPUcheck_maincodeok_turnonCPU(struct net_device *dev) |
8fc8598e JC |
124 | { |
125 | bool rt_status = true; | |
126 | int check_putcodeOK_time = 200000, check_bootOk_time = 200000; | |
e406322b | 127 | u32 CPU_status = 0; |
8fc8598e JC |
128 | |
129 | /* Check whether put code OK */ | |
130 | do { | |
b3d42bf1 | 131 | read_nic_dword(dev, CPU_GEN, &CPU_status); |
8fc8598e | 132 | |
14bc0d4f | 133 | if (CPU_status&CPU_GEN_PUT_CODE_OK) |
8fc8598e JC |
134 | break; |
135 | ||
32b116ed | 136 | } while (check_putcodeOK_time--); |
8fc8598e | 137 | |
14bc0d4f | 138 | if (!(CPU_status&CPU_GEN_PUT_CODE_OK)) { |
8fc8598e JC |
139 | RT_TRACE(COMP_ERR, "Download Firmware: Put code fail!\n"); |
140 | goto CPUCheckMainCodeOKAndTurnOnCPU_Fail; | |
141 | } else { | |
142 | RT_TRACE(COMP_FIRMWARE, "Download Firmware: Put code ok!\n"); | |
143 | } | |
144 | ||
145 | /* Turn On CPU */ | |
b3d42bf1 | 146 | read_nic_dword(dev, CPU_GEN, &CPU_status); |
5bb546f7 KG |
147 | write_nic_byte(dev, CPU_GEN, |
148 | (u8)((CPU_status | CPU_GEN_PWR_STB_CPU) & 0xff)); | |
8fc8598e JC |
149 | mdelay(1000); |
150 | ||
151 | /* Check whether CPU boot OK */ | |
152 | do { | |
b3d42bf1 | 153 | read_nic_dword(dev, CPU_GEN, &CPU_status); |
8fc8598e | 154 | |
14bc0d4f | 155 | if (CPU_status&CPU_GEN_BOOT_RDY) |
8fc8598e | 156 | break; |
32b116ed | 157 | } while (check_bootOk_time--); |
8fc8598e | 158 | |
0710bf3d | 159 | if (!(CPU_status&CPU_GEN_BOOT_RDY)) |
8fc8598e | 160 | goto CPUCheckMainCodeOKAndTurnOnCPU_Fail; |
0710bf3d | 161 | else |
8fc8598e | 162 | RT_TRACE(COMP_FIRMWARE, "Download Firmware: Boot ready!\n"); |
8fc8598e JC |
163 | |
164 | return rt_status; | |
165 | ||
166 | CPUCheckMainCodeOKAndTurnOnCPU_Fail: | |
f8628a47 | 167 | RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__); |
4b2faf80 | 168 | rt_status = false; |
8fc8598e JC |
169 | return rt_status; |
170 | } | |
171 | ||
2cc817c0 | 172 | static bool CPUcheck_firmware_ready(struct net_device *dev) |
8fc8598e JC |
173 | { |
174 | ||
175 | bool rt_status = true; | |
176 | int check_time = 200000; | |
177 | u32 CPU_status = 0; | |
178 | ||
179 | /* Check Firmware Ready */ | |
180 | do { | |
b3d42bf1 | 181 | read_nic_dword(dev, CPU_GEN, &CPU_status); |
8fc8598e | 182 | |
14bc0d4f | 183 | if (CPU_status&CPU_GEN_FIRM_RDY) |
8fc8598e JC |
184 | break; |
185 | ||
32b116ed | 186 | } while (check_time--); |
8fc8598e | 187 | |
14bc0d4f | 188 | if (!(CPU_status&CPU_GEN_FIRM_RDY)) |
8fc8598e JC |
189 | goto CPUCheckFirmwareReady_Fail; |
190 | else | |
191 | RT_TRACE(COMP_FIRMWARE, "Download Firmware: Firmware ready!\n"); | |
192 | ||
193 | return rt_status; | |
194 | ||
195 | CPUCheckFirmwareReady_Fail: | |
f8628a47 | 196 | RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__); |
8fc8598e JC |
197 | rt_status = false; |
198 | return rt_status; | |
199 | ||
200 | } | |
201 | ||
202 | bool init_firmware(struct net_device *dev) | |
203 | { | |
35997ff0 | 204 | struct r8192_priv *priv = ieee80211_priv(dev); |
4b2faf80 | 205 | bool rt_status = true; |
8fc8598e | 206 | |
8fc8598e JC |
207 | u32 file_length = 0; |
208 | u8 *mapped_file = NULL; | |
209 | u32 init_step = 0; | |
210 | opt_rst_type_e rst_opt = OPT_SYSTEM_RESET; | |
35997ff0 | 211 | firmware_init_step_e starting_state = FW_INIT_STEP0_BOOT; |
8fc8598e JC |
212 | |
213 | rt_firmware *pfirmware = priv->pFirmware; | |
35997ff0 | 214 | const struct firmware *fw_entry; |
8fc8598e | 215 | const char *fw_name[3] = { "RTL8192U/boot.img", |
e406322b | 216 | "RTL8192U/main.img", |
8fc8598e JC |
217 | "RTL8192U/data.img"}; |
218 | int rc; | |
219 | ||
220 | RT_TRACE(COMP_FIRMWARE, " PlatformInitFirmware()==>\n"); | |
221 | ||
020af9a5 | 222 | if (pfirmware->firmware_status == FW_STATUS_0_INIT) { |
8fc8598e JC |
223 | /* it is called by reset */ |
224 | rst_opt = OPT_SYSTEM_RESET; | |
225 | starting_state = FW_INIT_STEP0_BOOT; | |
6f438042 | 226 | /* TODO: system reset */ |
8fc8598e | 227 | |
32b116ed | 228 | } else if (pfirmware->firmware_status == FW_STATUS_5_READY) { |
8fc8598e JC |
229 | /* it is called by Initialize */ |
230 | rst_opt = OPT_FIRMWARE_RESET; | |
231 | starting_state = FW_INIT_STEP2_DATA; | |
32b116ed | 232 | } else { |
8fc8598e JC |
233 | RT_TRACE(COMP_FIRMWARE, "PlatformInitFirmware: undefined firmware state\n"); |
234 | } | |
235 | ||
236 | /* | |
237 | * Download boot, main, and data image for System reset. | |
589b3d06 | 238 | * Download data image for firmware reset |
8fc8598e | 239 | */ |
5ec1aeb3 | 240 | for (init_step = starting_state; init_step <= FW_INIT_STEP2_DATA; init_step++) { |
8fc8598e | 241 | /* |
8ef3a7ed | 242 | * Open image file, and map file to continuous memory if open file success. |
8fc8598e JC |
243 | * or read image file from array. Default load from IMG file |
244 | */ | |
14bc0d4f | 245 | if (rst_opt == OPT_SYSTEM_RESET) { |
bd1ccd33 | 246 | rc = request_firmware(&fw_entry, fw_name[init_step], &priv->udev->dev); |
020af9a5 | 247 | if (rc < 0) { |
0a8692b5 BH |
248 | RT_TRACE(COMP_ERR, "request firmware fail!\n"); |
249 | goto download_firmware_fail; | |
8fc8598e JC |
250 | } |
251 | ||
14bc0d4f | 252 | if (fw_entry->size > sizeof(pfirmware->firmware_buf)) { |
0a8692b5 BH |
253 | RT_TRACE(COMP_ERR, "img file size exceed the container buffer fail!\n"); |
254 | goto download_firmware_fail; | |
255 | } | |
8fc8598e | 256 | |
14bc0d4f | 257 | if (init_step != FW_INIT_STEP1_MAIN) { |
bd1ccd33 | 258 | memcpy(pfirmware->firmware_buf, fw_entry->data, fw_entry->size); |
0a8692b5 BH |
259 | mapped_file = pfirmware->firmware_buf; |
260 | file_length = fw_entry->size; | |
261 | } else { | |
0b4ef0a6 | 262 | memset(pfirmware->firmware_buf, 0, 128); |
bd1ccd33 | 263 | memcpy(&pfirmware->firmware_buf[128], fw_entry->data, fw_entry->size); |
0a8692b5 BH |
264 | mapped_file = pfirmware->firmware_buf; |
265 | file_length = fw_entry->size + 128; | |
0a8692b5 BH |
266 | } |
267 | pfirmware->firmware_buf_size = file_length; | |
020af9a5 | 268 | } else if (rst_opt == OPT_FIRMWARE_RESET) { |
8fc8598e JC |
269 | /* we only need to download data.img here */ |
270 | mapped_file = pfirmware->firmware_buf; | |
271 | file_length = pfirmware->firmware_buf_size; | |
272 | } | |
273 | ||
274 | /* Download image file */ | |
275 | /* The firmware download process is just as following, | |
276 | * 1. that is each packet will be segmented and inserted to the wait queue. | |
277 | * 2. each packet segment will be put in the skb_buff packet. | |
278 | * 3. each skb_buff packet data content will already include the firmware info | |
279 | * and Tx descriptor info | |
280 | * */ | |
0b4ef0a6 | 281 | rt_status = fw_download_code(dev, mapped_file, file_length); |
2930d0b9 | 282 | if (rst_opt == OPT_SYSTEM_RESET) |
8fc8598e | 283 | release_firmware(fw_entry); |
8fc8598e | 284 | |
a0886f73 | 285 | if (!rt_status) |
8fc8598e | 286 | goto download_firmware_fail; |
8fc8598e | 287 | |
ad638459 | 288 | switch (init_step) { |
24fbe875 SH |
289 | case FW_INIT_STEP0_BOOT: |
290 | /* Download boot | |
291 | * initialize command descriptor. | |
292 | * will set polling bit when firmware code is also configured | |
293 | */ | |
294 | pfirmware->firmware_status = FW_STATUS_1_MOVE_BOOT_CODE; | |
6f438042 | 295 | /* mdelay(1000); */ |
24fbe875 SH |
296 | /* |
297 | * To initialize IMEM, CPU move code from 0x80000080, | |
298 | * hence, we send 0x80 byte packet | |
299 | */ | |
300 | break; | |
301 | ||
302 | case FW_INIT_STEP1_MAIN: | |
303 | /* Download firmware code. Wait until Boot Ready and Turn on CPU */ | |
304 | pfirmware->firmware_status = FW_STATUS_2_MOVE_MAIN_CODE; | |
305 | ||
306 | /* Check Put Code OK and Turn On CPU */ | |
307 | rt_status = CPUcheck_maincodeok_turnonCPU(dev); | |
a0886f73 | 308 | if (!rt_status) { |
24fbe875 SH |
309 | RT_TRACE(COMP_ERR, "CPUcheck_maincodeok_turnonCPU fail!\n"); |
310 | goto download_firmware_fail; | |
311 | } | |
312 | ||
313 | pfirmware->firmware_status = FW_STATUS_3_TURNON_CPU; | |
314 | break; | |
315 | ||
316 | case FW_INIT_STEP2_DATA: | |
317 | /* download initial data code */ | |
318 | pfirmware->firmware_status = FW_STATUS_4_MOVE_DATA_CODE; | |
319 | mdelay(1); | |
320 | ||
321 | rt_status = CPUcheck_firmware_ready(dev); | |
a0886f73 | 322 | if (!rt_status) { |
bd1ccd33 | 323 | RT_TRACE(COMP_ERR, "CPUcheck_firmware_ready fail(%d)!\n", rt_status); |
24fbe875 SH |
324 | goto download_firmware_fail; |
325 | } | |
326 | ||
327 | /* wait until data code is initialized ready.*/ | |
328 | pfirmware->firmware_status = FW_STATUS_5_READY; | |
329 | break; | |
8fc8598e JC |
330 | } |
331 | } | |
332 | ||
333 | RT_TRACE(COMP_FIRMWARE, "Firmware Download Success\n"); | |
8fc8598e JC |
334 | return rt_status; |
335 | ||
336 | download_firmware_fail: | |
f8628a47 | 337 | RT_TRACE(COMP_ERR, "ERR in %s()\n", __func__); |
4b2faf80 | 338 | rt_status = false; |
8fc8598e JC |
339 | return rt_status; |
340 | ||
341 | } | |
342 | ||
589c3ca0 SLH |
343 | MODULE_FIRMWARE("RTL8192U/boot.img"); |
344 | MODULE_FIRMWARE("RTL8192U/main.img"); | |
345 | MODULE_FIRMWARE("RTL8192U/data.img"); |