Commit | Line | Data |
---|---|---|
635d2b00 GKH |
1 | /***************************************************************************** |
2 | ||
95edd09e | 3 | (c) Cambridge Silicon Radio Limited 2012 |
635d2b00 GKH |
4 | All rights reserved and confidential information of CSR |
5 | ||
6 | Refer to LICENSE.txt included with this source for details | |
7 | on the license terms. | |
8 | ||
9 | *****************************************************************************/ | |
10 | ||
11 | /* | |
12 | * --------------------------------------------------------------------------- | |
13 | * FILE: csr_wifi_hip_download.c | |
14 | * | |
15 | * PURPOSE: | |
16 | * Routines for downloading firmware to UniFi. | |
17 | * | |
18 | * --------------------------------------------------------------------------- | |
19 | */ | |
4fe9db37 | 20 | #include <linux/slab.h> |
635d2b00 GKH |
21 | #include "csr_wifi_hip_unifi.h" |
22 | #include "csr_wifi_hip_unifiversion.h" | |
23 | #include "csr_wifi_hip_card.h" | |
24 | #include "csr_wifi_hip_xbv.h" | |
25 | ||
26 | #undef CSR_WIFI_IGNORE_PATCH_VERSION_MISMATCH | |
27 | ||
28 | static CsrResult do_patch_download(card_t *card, void *dlpriv, | |
26a6b2e1 | 29 | xbv1_t *pfwinfo, u32 boot_ctrl_addr); |
635d2b00 GKH |
30 | |
31 | static CsrResult do_patch_convert_download(card_t *card, | |
32 | void *dlpriv, xbv1_t *pfwinfo); | |
33 | ||
34 | /* | |
35 | * --------------------------------------------------------------------------- | |
36 | * _find_in_slut | |
37 | * | |
38 | * Find the offset of the appropriate object in the SLUT of a card | |
39 | * | |
40 | * Arguments: | |
41 | * card Pointer to card struct | |
42 | * psym Pointer to symbol object. | |
43 | * id set up by caller | |
44 | * obj will be set up by this function | |
45 | * pslut Pointer to SLUT address, if 0xffffffff then it must be | |
46 | * read from the chip. | |
47 | * Returns: | |
48 | * CSR_RESULT_SUCCESS on success | |
49 | * Non-zero on error, | |
50 | * CSR_WIFI_HIP_RESULT_NOT_FOUND if not found | |
51 | * --------------------------------------------------------------------------- | |
52 | */ | |
26a6b2e1 | 53 | static CsrResult _find_in_slut(card_t *card, symbol_t *psym, u32 *pslut) |
635d2b00 | 54 | { |
26a6b2e1 | 55 | u32 slut_address; |
8c87f69a | 56 | u16 finger_print; |
635d2b00 GKH |
57 | CsrResult r; |
58 | CsrResult csrResult; | |
59 | ||
60 | /* Get SLUT address */ | |
61 | if (*pslut == 0xffffffff) | |
62 | { | |
63 | r = card_wait_for_firmware_to_start(card, &slut_address); | |
64 | if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) | |
65 | { | |
66 | return r; | |
67 | } | |
68 | if (r != CSR_RESULT_SUCCESS) | |
69 | { | |
70 | unifi_error(card->ospriv, "Firmware hasn't started\n"); | |
635d2b00 GKH |
71 | return r; |
72 | } | |
73 | *pslut = slut_address; | |
74 | ||
75 | /* | |
76 | * Firmware has started so set the SDIO bus clock to the initial speed, | |
77 | * faster than UNIFI_SDIO_CLOCK_SAFE_HZ, to speed up the f/w download. | |
78 | */ | |
79 | csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_INIT_HZ); | |
80 | if (csrResult != CSR_RESULT_SUCCESS) | |
81 | { | |
82 | r = ConvertCsrSdioToCsrHipResult(card, csrResult); | |
635d2b00 GKH |
83 | return r; |
84 | } | |
85 | card->sdio_clock_speed = UNIFI_SDIO_CLOCK_INIT_HZ; | |
86 | } | |
87 | else | |
88 | { | |
89 | slut_address = *pslut; /* Use previously discovered address */ | |
90 | } | |
91 | unifi_trace(card->ospriv, UDBG4, "SLUT addr: 0x%lX\n", slut_address); | |
92 | ||
93 | /* | |
94 | * Check the SLUT fingerprint. | |
95 | * The slut_address is a generic pointer so we must use unifi_card_read16(). | |
96 | */ | |
97 | unifi_trace(card->ospriv, UDBG4, "Looking for SLUT finger print\n"); | |
98 | finger_print = 0; | |
99 | r = unifi_card_read16(card, slut_address, &finger_print); | |
100 | if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) | |
101 | { | |
102 | return r; | |
103 | } | |
104 | if (r != CSR_RESULT_SUCCESS) | |
105 | { | |
106 | unifi_error(card->ospriv, "Failed to read SLUT finger print\n"); | |
635d2b00 GKH |
107 | return r; |
108 | } | |
109 | ||
110 | if (finger_print != SLUT_FINGERPRINT) | |
111 | { | |
112 | unifi_error(card->ospriv, "Failed to find SLUT fingerprint\n"); | |
635d2b00 GKH |
113 | return CSR_RESULT_FAILURE; |
114 | } | |
115 | ||
116 | /* Symbol table starts imedately after the fingerprint */ | |
117 | slut_address += 2; | |
118 | ||
119 | while (1) | |
120 | { | |
8c87f69a | 121 | u16 id; |
26a6b2e1 | 122 | u32 obj; |
635d2b00 GKH |
123 | |
124 | r = unifi_card_read16(card, slut_address, &id); | |
125 | if (r != CSR_RESULT_SUCCESS) | |
126 | { | |
635d2b00 GKH |
127 | return r; |
128 | } | |
129 | slut_address += 2; | |
130 | ||
131 | if (id == CSR_SLT_END) | |
132 | { | |
133 | /* End of table reached: not found */ | |
134 | r = CSR_WIFI_HIP_RESULT_RANGE; | |
135 | break; | |
136 | } | |
137 | ||
138 | r = unifi_read32(card, slut_address, &obj); | |
139 | if (r != CSR_RESULT_SUCCESS) | |
140 | { | |
635d2b00 GKH |
141 | return r; |
142 | } | |
143 | slut_address += 4; | |
144 | ||
145 | unifi_trace(card->ospriv, UDBG3, " found SLUT id %02d.%08lx\n", id, obj); | |
146 | ||
147 | r = CSR_WIFI_HIP_RESULT_NOT_FOUND; | |
148 | /* Found search term? */ | |
149 | if (id == psym->id) | |
150 | { | |
151 | unifi_trace(card->ospriv, UDBG1, " matched SLUT id %02d.%08lx\n", id, obj); | |
152 | psym->obj = obj; | |
153 | r = CSR_RESULT_SUCCESS; | |
154 | break; | |
155 | } | |
156 | } | |
157 | ||
635d2b00 GKH |
158 | return r; |
159 | } | |
160 | ||
161 | ||
162 | /* | |
163 | * --------------------------------------------------------------------------- | |
164 | * do_patch_convert_download | |
165 | * | |
166 | * Download the given firmware image to the UniFi, converting from FWDL | |
167 | * to PTDL XBV format. | |
168 | * | |
169 | * Arguments: | |
170 | * card Pointer to card struct | |
171 | * dlpriv Pointer to source firmware image | |
172 | * fwinfo Pointer to source firmware info struct | |
173 | * | |
174 | * Returns: | |
175 | * CSR_RESULT_SUCCESS on success, CSR error code on error | |
176 | * | |
177 | * Notes: | |
178 | * --------------------------------------------------------------------------- | |
179 | */ | |
180 | static CsrResult do_patch_convert_download(card_t *card, void *dlpriv, xbv1_t *pfwinfo) | |
181 | { | |
182 | CsrResult r; | |
26a6b2e1 | 183 | u32 slut_base = 0xffffffff; |
635d2b00 | 184 | void *pfw; |
26a6b2e1 | 185 | u32 psize; |
635d2b00 GKH |
186 | symbol_t sym; |
187 | ||
188 | /* Reset the chip to guarantee that the ROM loader is running */ | |
189 | r = unifi_init(card); | |
190 | if (r != CSR_RESULT_SUCCESS) | |
191 | { | |
192 | unifi_error(card->ospriv, | |
193 | "do_patch_convert_download: failed to re-init UniFi\n"); | |
194 | return r; | |
195 | } | |
196 | ||
197 | /* If no unifi_helper is running, the firmware version must be read */ | |
198 | if (card->build_id == 0) | |
199 | { | |
26a6b2e1 | 200 | u32 ver = 0; |
635d2b00 GKH |
201 | sym.id = CSR_SLT_BUILD_ID_NUMBER; |
202 | sym.obj = 0; /* To be updated by _find_in_slut() */ | |
203 | ||
204 | unifi_trace(card->ospriv, UDBG1, "Need f/w version\n"); | |
205 | ||
206 | /* Find chip build id entry in SLUT */ | |
207 | r = _find_in_slut(card, &sym, &slut_base); | |
208 | if (r != CSR_RESULT_SUCCESS) | |
209 | { | |
210 | unifi_error(card->ospriv, "Failed to find CSR_SLT_BUILD_ID_NUMBER\n"); | |
211 | return CSR_RESULT_FAILURE; | |
212 | } | |
213 | ||
214 | /* Read running f/w version */ | |
215 | r = unifi_read32(card, sym.obj, &ver); | |
216 | if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) | |
217 | { | |
218 | return r; | |
219 | } | |
220 | if (r != CSR_RESULT_SUCCESS) | |
221 | { | |
222 | unifi_error(card->ospriv, "Failed to read f/w id\n"); | |
223 | return CSR_RESULT_FAILURE; | |
224 | } | |
225 | card->build_id = ver; | |
226 | } | |
227 | ||
228 | /* Convert the ptest firmware to a patch against the running firmware */ | |
229 | pfw = xbv_to_patch(card, unifi_fw_read, dlpriv, pfwinfo, &psize); | |
230 | if (!pfw) | |
231 | { | |
232 | unifi_error(card->ospriv, "Failed to convert f/w to patch"); | |
233 | return CSR_WIFI_HIP_RESULT_NO_MEMORY; | |
234 | } | |
235 | else | |
236 | { | |
237 | void *desc; | |
238 | sym.id = CSR_SLT_BOOT_LOADER_CONTROL; | |
239 | sym.obj = 0; /* To be updated by _find_in_slut() */ | |
240 | ||
241 | /* Find boot loader control entry in SLUT */ | |
242 | r = _find_in_slut(card, &sym, &slut_base); | |
243 | if (r != CSR_RESULT_SUCCESS) | |
244 | { | |
245 | unifi_error(card->ospriv, "Failed to find BOOT_LOADER_CONTROL\n"); | |
c8be681f | 246 | kfree(pfw); |
635d2b00 GKH |
247 | return CSR_RESULT_FAILURE; |
248 | } | |
249 | ||
250 | r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE); | |
251 | if (r != CSR_RESULT_SUCCESS) | |
252 | { | |
253 | unifi_error(card->ospriv, "Failed to wake UniFi\n"); | |
254 | } | |
255 | ||
256 | /* Get a dlpriv for the patch buffer so that unifi_fw_read() can | |
257 | * access it. | |
258 | */ | |
259 | desc = unifi_fw_open_buffer(card->ospriv, pfw, psize); | |
260 | if (!desc) | |
261 | { | |
c8be681f | 262 | kfree(pfw); |
635d2b00 GKH |
263 | return CSR_WIFI_HIP_RESULT_NO_MEMORY; |
264 | } | |
265 | ||
266 | /* Download the patch */ | |
267 | unifi_info(card->ospriv, "Downloading converted f/w as patch\n"); | |
268 | r = unifi_dl_patch(card, desc, sym.obj); | |
4fe9db37 | 269 | kfree(pfw); |
635d2b00 GKH |
270 | unifi_fw_close_buffer(card->ospriv, desc); |
271 | ||
272 | if (r != CSR_RESULT_SUCCESS) | |
273 | { | |
274 | unifi_error(card->ospriv, "Converted patch download failed\n"); | |
635d2b00 GKH |
275 | return r; |
276 | } | |
277 | else | |
278 | { | |
279 | unifi_trace(card->ospriv, UDBG1, "Converted patch downloaded\n"); | |
280 | } | |
281 | ||
282 | /* This command starts the firmware */ | |
283 | r = unifi_do_loader_op(card, sym.obj + 6, UNIFI_BOOT_LOADER_RESTART); | |
284 | if (r != CSR_RESULT_SUCCESS) | |
285 | { | |
286 | unifi_error(card->ospriv, "Failed to write loader restart cmd\n"); | |
287 | } | |
288 | ||
635d2b00 GKH |
289 | return r; |
290 | } | |
291 | } | |
292 | ||
293 | ||
294 | /* | |
295 | * --------------------------------------------------------------------------- | |
296 | * unifi_dl_firmware | |
297 | * | |
298 | * Download the given firmware image to the UniFi. | |
299 | * | |
300 | * Arguments: | |
301 | * card Pointer to card struct | |
302 | * dlpriv A context pointer from the calling function to be | |
303 | * passed when calling unifi_fw_read(). | |
304 | * | |
305 | * Returns: | |
306 | * CSR_RESULT_SUCCESS on success, | |
307 | * CSR_WIFI_HIP_RESULT_NO_MEMORY memory allocation failed | |
308 | * CSR_WIFI_HIP_RESULT_INVALID_VALUE error in XBV file | |
309 | * CSR_RESULT_FAILURE SDIO error | |
310 | * | |
311 | * Notes: | |
312 | * Stops and resets the chip, does the download and runs the new | |
313 | * firmware. | |
314 | * --------------------------------------------------------------------------- | |
315 | */ | |
316 | CsrResult unifi_dl_firmware(card_t *card, void *dlpriv) | |
317 | { | |
318 | xbv1_t *fwinfo; | |
319 | CsrResult r; | |
320 | ||
70128792 | 321 | fwinfo = kmalloc(sizeof(xbv1_t), GFP_KERNEL); |
635d2b00 GKH |
322 | if (fwinfo == NULL) |
323 | { | |
324 | unifi_error(card->ospriv, "Failed to allocate memory for firmware\n"); | |
325 | return CSR_WIFI_HIP_RESULT_NO_MEMORY; | |
326 | } | |
327 | ||
328 | /* | |
329 | * Scan the firmware file to find the TLVs we are interested in. | |
330 | * These are: | |
331 | * - check we support the file format version in VERF | |
332 | * - SLTP Symbol Lookup Table Pointer | |
333 | * - FWDL firmware download segments | |
334 | * - FWOV firmware overlay segment | |
335 | * - VMEQ Register probe tests to verify matching h/w | |
336 | */ | |
337 | r = xbv1_parse(card, unifi_fw_read, dlpriv, fwinfo); | |
338 | if (r != CSR_RESULT_SUCCESS || fwinfo->mode != xbv_firmware) | |
339 | { | |
340 | unifi_error(card->ospriv, "File type is %s, expected firmware.\n", | |
341 | fwinfo->mode == xbv_patch?"patch" : "unknown"); | |
4fe9db37 | 342 | kfree(fwinfo); |
635d2b00 GKH |
343 | return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
344 | } | |
345 | ||
346 | /* UF6xxx doesn't accept firmware, only patches. Therefore we convert | |
347 | * the file to patch format with version numbers matching the current | |
348 | * running firmware, and then download via the patch mechanism. | |
349 | * The sole purpose of this is to support production test firmware across | |
350 | * different ROM releases, the test firmware being provided in non-patch | |
351 | * format. | |
352 | */ | |
353 | if (card->chip_id > SDIO_CARD_ID_UNIFI_2) | |
354 | { | |
355 | unifi_info(card->ospriv, "Must convert f/w to patch format\n"); | |
356 | r = do_patch_convert_download(card, dlpriv, fwinfo); | |
357 | } | |
358 | else | |
359 | { | |
360 | /* Older UniFi chips allowed firmware to be directly loaded onto the | |
361 | * chip, which is no longer supported. | |
362 | */ | |
363 | unifi_error(card->ospriv, "Only patch downloading supported\n"); | |
364 | r = CSR_WIFI_HIP_RESULT_INVALID_VALUE; | |
365 | } | |
366 | ||
4fe9db37 | 367 | kfree(fwinfo); |
635d2b00 GKH |
368 | return r; |
369 | } /* unifi_dl_firmware() */ | |
370 | ||
371 | ||
372 | /* | |
373 | * --------------------------------------------------------------------------- | |
374 | * unifi_dl_patch | |
375 | * | |
376 | * Load the given patch set into UniFi. | |
377 | * | |
378 | * Arguments: | |
379 | * card Pointer to card struct | |
380 | * dlpriv The os specific handle to the firmware file. | |
381 | * boot_ctrl The address of the boot loader control structure. | |
382 | * | |
383 | * Returns: | |
384 | * CSR_RESULT_SUCCESS on success, | |
385 | * CSR_WIFI_HIP_RESULT_NO_MEMORY memory allocation failed | |
386 | * CSR_WIFI_HIP_RESULT_INVALID_VALUE error in XBV file | |
387 | * CSR_RESULT_FAILURE SDIO error | |
388 | * | |
389 | * Notes: | |
390 | * This ends up telling UniFi to restart. | |
391 | * --------------------------------------------------------------------------- | |
392 | */ | |
26a6b2e1 | 393 | CsrResult unifi_dl_patch(card_t *card, void *dlpriv, u32 boot_ctrl) |
635d2b00 GKH |
394 | { |
395 | xbv1_t *fwinfo; | |
396 | CsrResult r; | |
397 | ||
635d2b00 GKH |
398 | unifi_info(card->ospriv, "unifi_dl_patch %p %08x\n", dlpriv, boot_ctrl); |
399 | ||
70128792 | 400 | fwinfo = kmalloc(sizeof(xbv1_t), GFP_KERNEL); |
635d2b00 GKH |
401 | if (fwinfo == NULL) |
402 | { | |
403 | unifi_error(card->ospriv, "Failed to allocate memory for patches\n"); | |
635d2b00 GKH |
404 | return CSR_WIFI_HIP_RESULT_NO_MEMORY; |
405 | } | |
406 | ||
407 | /* | |
408 | * Scan the firmware file to find the TLVs we are interested in. | |
409 | * These are: | |
410 | * - check we support the file format version in VERF | |
411 | * - FWID The build ID of the ROM that we can patch | |
412 | * - PTDL patch download segments | |
413 | */ | |
414 | r = xbv1_parse(card, unifi_fw_read, dlpriv, fwinfo); | |
415 | if (r != CSR_RESULT_SUCCESS || fwinfo->mode != xbv_patch) | |
416 | { | |
4fe9db37 | 417 | kfree(fwinfo); |
635d2b00 | 418 | unifi_error(card->ospriv, "Failed to read in patch file\n"); |
635d2b00 GKH |
419 | return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
420 | } | |
421 | ||
422 | /* | |
423 | * We have to check the build id read from the SLUT against that | |
424 | * for the patch file. They have to match exactly. | |
425 | * "card->build_id" == XBV1.PTCH.FWID | |
426 | */ | |
427 | if (card->build_id != fwinfo->build_id) | |
428 | { | |
429 | unifi_error(card->ospriv, "Wrong patch file for chip (chip = %lu, file = %lu)\n", | |
430 | card->build_id, fwinfo->build_id); | |
4fe9db37 | 431 | kfree(fwinfo); |
635d2b00 | 432 | #ifndef CSR_WIFI_IGNORE_PATCH_VERSION_MISMATCH |
635d2b00 GKH |
433 | return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
434 | #else | |
435 | fwinfo = NULL; | |
436 | dlpriv = NULL; | |
437 | return CSR_RESULT_SUCCESS; | |
438 | #endif | |
439 | } | |
440 | ||
441 | r = do_patch_download(card, dlpriv, fwinfo, boot_ctrl); | |
442 | if (r != CSR_RESULT_SUCCESS) | |
443 | { | |
444 | unifi_error(card->ospriv, "Failed to patch image\n"); | |
445 | } | |
446 | ||
4fe9db37 | 447 | kfree(fwinfo); |
635d2b00 | 448 | |
635d2b00 GKH |
449 | return r; |
450 | } /* unifi_dl_patch() */ | |
451 | ||
452 | ||
163eb0d8 | 453 | void* unifi_dl_fw_read_start(card_t *card, s8 is_fw) |
635d2b00 GKH |
454 | { |
455 | card_info_t card_info; | |
456 | ||
457 | unifi_card_info(card, &card_info); | |
458 | unifi_trace(card->ospriv, UDBG5, | |
459 | "id=%d, ver=0x%x, fw_build=%u, fw_hip=0x%x, block_size=%d\n", | |
460 | card_info.chip_id, card_info.chip_version, | |
461 | card_info.fw_build, card_info.fw_hip_version, | |
462 | card_info.sdio_block_size); | |
463 | ||
464 | return unifi_fw_read_start(card->ospriv, is_fw, &card_info); | |
465 | } | |
466 | ||
467 | ||
468 | /* | |
469 | * --------------------------------------------------------------------------- | |
470 | * safe_read_shared_location | |
471 | * | |
472 | * Read a shared memory location repeatedly until we get two readings | |
473 | * the same. | |
474 | * | |
475 | * Arguments: | |
476 | * card Pointer to card context struct. | |
477 | * unifi_addr UniFi shared-data-memory address to access. | |
478 | * pdata Pointer to a byte variable for the value read. | |
479 | * | |
480 | * | |
481 | * Returns: | |
482 | * CSR_RESULT_SUCCESS on success, CSR error code on failure | |
483 | * --------------------------------------------------------------------------- | |
484 | */ | |
26a6b2e1 | 485 | static CsrResult safe_read_shared_location(card_t *card, u32 address, u8 *pdata) |
635d2b00 GKH |
486 | { |
487 | CsrResult r; | |
8c87f69a | 488 | u16 limit = 1000; |
7e6f5794 | 489 | u8 b, b2; |
635d2b00 GKH |
490 | |
491 | *pdata = 0; | |
492 | ||
493 | r = unifi_read_8_or_16(card, address, &b); | |
494 | if (r != CSR_RESULT_SUCCESS) | |
495 | { | |
496 | return r; | |
497 | } | |
498 | ||
499 | while (limit--) | |
500 | { | |
501 | r = unifi_read_8_or_16(card, address, &b2); | |
502 | if (r != CSR_RESULT_SUCCESS) | |
503 | { | |
504 | return r; | |
505 | } | |
506 | ||
507 | /* When we have a stable value, return it */ | |
508 | if (b == b2) | |
509 | { | |
510 | *pdata = b; | |
511 | return CSR_RESULT_SUCCESS; | |
512 | } | |
513 | ||
514 | b = b2; | |
515 | } | |
516 | ||
517 | return CSR_RESULT_FAILURE; | |
518 | } /* safe_read_shared_location() */ | |
519 | ||
520 | ||
521 | /* | |
522 | * --------------------------------------------------------------------------- | |
523 | * unifi_do_loader_op | |
524 | * | |
525 | * Send a loader / boot_loader command to the UniFi and wait for | |
526 | * it to complete. | |
527 | * | |
528 | * Arguments: | |
529 | * card Pointer to card context struct. | |
530 | * op_addr The address of the loader operation control word. | |
531 | * opcode The operation to perform. | |
532 | * | |
533 | * Returns: | |
534 | * CSR_RESULT_SUCCESS on success | |
535 | * CSR_RESULT_FAILURE SDIO error or SDIO/XAP timeout | |
536 | * --------------------------------------------------------------------------- | |
537 | */ | |
538 | ||
539 | /* | |
540 | * Ideally instead of sleeping, we want to busy wait. | |
541 | * Currently there is no framework API to do this. When it becomes available, | |
542 | * we can use it to busy wait using usecs | |
543 | */ | |
544 | #define OPERATION_TIMEOUT_LOOPS (100) /* when OPERATION_TIMEOUT_DELAY==1, (500) otherwise */ | |
545 | #define OPERATION_TIMEOUT_DELAY 1 /* msec, or 200usecs */ | |
546 | ||
26a6b2e1 | 547 | CsrResult unifi_do_loader_op(card_t *card, u32 op_addr, u8 opcode) |
635d2b00 GKH |
548 | { |
549 | CsrResult r; | |
ab2b8c73 | 550 | s16 op_retries; |
635d2b00 GKH |
551 | |
552 | unifi_trace(card->ospriv, UDBG4, "Loader cmd 0x%0x -> 0x%08x\n", opcode, op_addr); | |
553 | ||
554 | /* Set the Operation command byte to the opcode */ | |
555 | r = unifi_write_8_or_16(card, op_addr, opcode); | |
556 | if (r != CSR_RESULT_SUCCESS) | |
557 | { | |
558 | unifi_error(card->ospriv, "Failed to write loader copy command\n"); | |
559 | return r; | |
560 | } | |
561 | ||
562 | /* Wait for Operation command byte to be Idle */ | |
563 | /* Typically takes ~100us */ | |
564 | op_retries = 0; | |
565 | r = CSR_RESULT_SUCCESS; | |
566 | while (1) | |
567 | { | |
7e6f5794 | 568 | u8 op; |
635d2b00 GKH |
569 | |
570 | /* | |
571 | * Read the memory location until two successive reads give | |
572 | * the same value. | |
573 | * Then handle it. | |
574 | */ | |
575 | r = safe_read_shared_location(card, op_addr, &op); | |
576 | if (r != CSR_RESULT_SUCCESS) | |
577 | { | |
578 | unifi_error(card->ospriv, "Failed to read loader status\n"); | |
579 | break; | |
580 | } | |
581 | ||
582 | if (op == UNIFI_LOADER_IDLE) | |
583 | { | |
584 | /* Success */ | |
585 | break; | |
586 | } | |
587 | ||
588 | if (op != opcode) | |
589 | { | |
590 | unifi_error(card->ospriv, "Error reported by loader: 0x%X\n", op); | |
591 | r = CSR_RESULT_FAILURE; | |
592 | break; | |
593 | } | |
594 | ||
595 | /* Allow 500us timeout */ | |
596 | if (++op_retries >= OPERATION_TIMEOUT_LOOPS) | |
597 | { | |
598 | unifi_error(card->ospriv, "Timeout waiting for loader to ack transfer\n"); | |
599 | /* Stop XAPs to aid post-mortem */ | |
600 | r = unifi_card_stop_processor(card, UNIFI_PROC_BOTH); | |
601 | if (r != CSR_RESULT_SUCCESS) | |
602 | { | |
603 | unifi_error(card->ospriv, "Failed to stop UniFi processors\n"); | |
604 | } | |
605 | else | |
606 | { | |
607 | r = CSR_RESULT_FAILURE; | |
608 | } | |
609 | break; | |
610 | } | |
611 | CsrThreadSleep(OPERATION_TIMEOUT_DELAY); | |
612 | } /* Loop exits with r != CSR_RESULT_SUCCESS on error */ | |
613 | ||
614 | return r; | |
615 | } /* unifi_do_loader_op() */ | |
616 | ||
617 | ||
618 | /* | |
619 | * --------------------------------------------------------------------------- | |
620 | * send_ptdl_to_unifi | |
621 | * | |
622 | * Copy a patch block from userland to the UniFi. | |
623 | * This function reads data, 2K at a time, from userland and writes | |
624 | * it to the UniFi. | |
625 | * | |
626 | * Arguments: | |
627 | * card A pointer to the card structure | |
628 | * dlpriv The os specific handle for the firmware file | |
629 | * ptdl A pointer ot the PTDL block | |
630 | * handle The buffer handle to use for the xfer | |
631 | * op_addr The address of the loader operation control word | |
632 | * | |
633 | * Returns: | |
634 | * Number of bytes sent (Positive) or negative value indicating | |
635 | * error code: | |
636 | * CSR_WIFI_HIP_RESULT_NO_MEMORY memory allocation failed | |
637 | * CSR_WIFI_HIP_RESULT_INVALID_VALUE error in XBV file | |
638 | * CSR_RESULT_FAILURE SDIO error | |
639 | * --------------------------------------------------------------------------- | |
640 | */ | |
641 | static CsrResult send_ptdl_to_unifi(card_t *card, void *dlpriv, | |
26a6b2e1 GKH |
642 | const struct PTDL *ptdl, u32 handle, |
643 | u32 op_addr) | |
635d2b00 | 644 | { |
26a6b2e1 | 645 | u32 offset; |
7e6f5794 | 646 | u8 *buf; |
95e326c2 | 647 | s32 data_len; |
26a6b2e1 | 648 | u32 write_len; |
635d2b00 | 649 | CsrResult r; |
8c87f69a | 650 | const u16 buf_size = 2 * 1024; |
635d2b00 GKH |
651 | |
652 | offset = ptdl->dl_offset; | |
653 | data_len = ptdl->dl_size; | |
654 | ||
655 | if (data_len > buf_size) | |
656 | { | |
657 | unifi_error(card->ospriv, "PTDL block is too large (%u)\n", | |
658 | ptdl->dl_size); | |
659 | return CSR_WIFI_HIP_RESULT_INVALID_VALUE; | |
660 | } | |
661 | ||
4becf12d | 662 | buf = kmalloc(buf_size, GFP_KERNEL); |
635d2b00 GKH |
663 | if (buf == NULL) |
664 | { | |
665 | unifi_error(card->ospriv, "Failed to allocate transfer buffer for firmware download\n"); | |
666 | return CSR_WIFI_HIP_RESULT_NO_MEMORY; | |
667 | } | |
668 | ||
669 | r = CSR_RESULT_SUCCESS; | |
670 | ||
671 | if (unifi_fw_read(card->ospriv, dlpriv, offset, buf, data_len) != data_len) | |
672 | { | |
673 | unifi_error(card->ospriv, "Failed to read from file\n"); | |
674 | } | |
675 | else | |
676 | { | |
677 | /* We can always round these if the host wants to */ | |
678 | if (card->sdio_io_block_pad) | |
679 | { | |
680 | write_len = (data_len + (card->sdio_io_block_size - 1)) & | |
681 | ~(card->sdio_io_block_size - 1); | |
682 | ||
683 | /* Zero out the rest of the buffer (This isn't needed, but it | |
684 | * makes debugging things later much easier). */ | |
b7244a31 | 685 | memset(buf + data_len, 0, write_len - data_len); |
635d2b00 GKH |
686 | } |
687 | else | |
688 | { | |
689 | write_len = data_len; | |
690 | } | |
691 | ||
692 | r = unifi_bulk_rw_noretry(card, handle, buf, write_len, UNIFI_SDIO_WRITE); | |
693 | if (r != CSR_RESULT_SUCCESS) | |
694 | { | |
695 | unifi_error(card->ospriv, "CMD53 failed writing %d bytes to handle %ld\n", | |
696 | data_len, handle); | |
697 | } | |
698 | else | |
699 | { | |
700 | /* | |
701 | * Can change the order of things to overlap read from file | |
702 | * with copy to unifi | |
703 | */ | |
704 | r = unifi_do_loader_op(card, op_addr, UNIFI_BOOT_LOADER_PATCH); | |
705 | } | |
706 | } | |
707 | ||
4fe9db37 | 708 | kfree(buf); |
635d2b00 GKH |
709 | |
710 | if (r != CSR_RESULT_SUCCESS && r != CSR_WIFI_HIP_RESULT_NO_DEVICE) | |
711 | { | |
712 | unifi_error(card->ospriv, "Failed to copy block of %u bytes to UniFi\n", | |
713 | ptdl->dl_size); | |
714 | } | |
715 | ||
716 | return r; | |
717 | } /* send_ptdl_to_unifi() */ | |
718 | ||
719 | ||
720 | /* | |
721 | * --------------------------------------------------------------------------- | |
722 | * do_patch_download | |
723 | * | |
724 | * This function downloads a set of patches to UniFi and then | |
725 | * causes it to restart. | |
726 | * | |
727 | * Arguments: | |
728 | * card Pointer to card struct. | |
729 | * dlpriv A context pointer from the calling function to be | |
730 | * used when reading the XBV file. This can be NULL | |
731 | * in which case not patches are applied. | |
732 | * pfwinfo Pointer to a fwinfo struct describing the f/w | |
733 | * XBV file. | |
734 | * boot_ctrl_addr The address of the boot loader control structure. | |
735 | * | |
736 | * Returns: | |
737 | * 0 on success, or an error code | |
738 | * CSR_WIFI_HIP_RESULT_INVALID_VALUE for a bad laoader version number | |
739 | * --------------------------------------------------------------------------- | |
740 | */ | |
26a6b2e1 | 741 | static CsrResult do_patch_download(card_t *card, void *dlpriv, xbv1_t *pfwinfo, u32 boot_ctrl_addr) |
635d2b00 GKH |
742 | { |
743 | CsrResult r; | |
95e326c2 | 744 | s32 i; |
8c87f69a GKH |
745 | u16 loader_version; |
746 | u16 handle; | |
26a6b2e1 | 747 | u32 total_bytes; |
635d2b00 GKH |
748 | |
749 | /* | |
750 | * Read info from the SDIO Loader Control Data Structure | |
751 | */ | |
752 | /* Check the loader version */ | |
753 | r = unifi_card_read16(card, boot_ctrl_addr, &loader_version); | |
754 | if (r != CSR_RESULT_SUCCESS) | |
755 | { | |
756 | unifi_error(card->ospriv, "Patch download: Failed to read loader version\n"); | |
757 | return r; | |
758 | } | |
759 | unifi_trace(card->ospriv, UDBG2, "Patch download: boot loader version 0x%04X\n", loader_version); | |
760 | switch (loader_version) | |
761 | { | |
762 | case 0x0000: | |
763 | break; | |
764 | ||
765 | default: | |
766 | unifi_error(card->ospriv, "Patch loader version (0x%04X) is not supported by this driver\n", | |
767 | loader_version); | |
768 | return CSR_WIFI_HIP_RESULT_INVALID_VALUE; | |
769 | } | |
770 | ||
771 | /* Retrieve the handle to use with CMD53 */ | |
772 | r = unifi_card_read16(card, boot_ctrl_addr + 4, &handle); | |
773 | if (r != CSR_RESULT_SUCCESS) | |
774 | { | |
775 | unifi_error(card->ospriv, "Patch download: Failed to read loader handle\n"); | |
776 | return r; | |
777 | } | |
778 | ||
779 | /* Set the mask of LEDs to flash */ | |
780 | if (card->loader_led_mask) | |
781 | { | |
782 | r = unifi_card_write16(card, boot_ctrl_addr + 2, | |
8c87f69a | 783 | (u16)card->loader_led_mask); |
635d2b00 GKH |
784 | if (r != CSR_RESULT_SUCCESS) |
785 | { | |
786 | unifi_error(card->ospriv, "Patch download: Failed to write LED mask\n"); | |
787 | return r; | |
788 | } | |
789 | } | |
790 | ||
791 | total_bytes = 0; | |
792 | ||
793 | /* Copy download data to UniFi memory */ | |
794 | for (i = 0; i < pfwinfo->num_ptdl; i++) | |
795 | { | |
796 | unifi_trace(card->ospriv, UDBG3, "Patch download: %d Downloading for %d from offset %d\n", | |
797 | i, | |
798 | pfwinfo->ptdl[i].dl_size, | |
799 | pfwinfo->ptdl[i].dl_offset); | |
800 | ||
801 | r = send_ptdl_to_unifi(card, dlpriv, &pfwinfo->ptdl[i], | |
802 | handle, boot_ctrl_addr + 6); | |
803 | if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) | |
804 | { | |
805 | return r; | |
806 | } | |
807 | if (r != CSR_RESULT_SUCCESS) | |
808 | { | |
809 | unifi_error(card->ospriv, "Patch failed after %u bytes\n", | |
810 | total_bytes); | |
811 | return r; | |
812 | } | |
813 | total_bytes += pfwinfo->ptdl[i].dl_size; | |
814 | } | |
815 | ||
816 | return CSR_RESULT_SUCCESS; | |
817 | } /* do_patch_download() */ | |
818 | ||
819 |