diff options
Diffstat (limited to 'drivers/net/wireless/wl12xx/wl1251_boot.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/wl1251_boot.c | 256 |
1 files changed, 245 insertions, 11 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1251_boot.c b/drivers/net/wireless/wl12xx/wl1251_boot.c index 8b50d44d8243..88e9cb0947d5 100644 --- a/drivers/net/wireless/wl12xx/wl1251_boot.c +++ b/drivers/net/wireless/wl12xx/wl1251_boot.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "wl1251_io.h" | 28 | #include "wl1251_io.h" |
29 | #include "wl1251_spi.h" | 29 | #include "wl1251_spi.h" |
30 | #include "wl1251_event.h" | 30 | #include "wl1251_event.h" |
31 | #include "wl1251_acx.h" | ||
31 | 32 | ||
32 | void wl1251_boot_target_enable_interrupts(struct wl1251 *wl) | 33 | void wl1251_boot_target_enable_interrupts(struct wl1251 *wl) |
33 | { | 34 | { |
@@ -208,18 +209,30 @@ int wl1251_boot_init_seq(struct wl1251 *wl) | |||
208 | return 0; | 209 | return 0; |
209 | } | 210 | } |
210 | 211 | ||
212 | static void wl1251_boot_set_ecpu_ctrl(struct wl1251 *wl, u32 flag) | ||
213 | { | ||
214 | u32 cpu_ctrl; | ||
215 | |||
216 | /* 10.5.0 run the firmware (I) */ | ||
217 | cpu_ctrl = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); | ||
218 | |||
219 | /* 10.5.1 run the firmware (II) */ | ||
220 | cpu_ctrl &= ~flag; | ||
221 | wl1251_reg_write32(wl, ACX_REG_ECPU_CONTROL, cpu_ctrl); | ||
222 | } | ||
223 | |||
211 | int wl1251_boot_run_firmware(struct wl1251 *wl) | 224 | int wl1251_boot_run_firmware(struct wl1251 *wl) |
212 | { | 225 | { |
213 | int loop, ret; | 226 | int loop, ret; |
214 | u32 chip_id, interrupt; | 227 | u32 chip_id, interrupt; |
215 | 228 | ||
216 | wl->chip.op_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); | 229 | wl1251_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); |
217 | 230 | ||
218 | chip_id = wl1251_reg_read32(wl, CHIP_ID_B); | 231 | chip_id = wl1251_reg_read32(wl, CHIP_ID_B); |
219 | 232 | ||
220 | wl1251_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); | 233 | wl1251_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x", chip_id); |
221 | 234 | ||
222 | if (chip_id != wl->chip.id) { | 235 | if (chip_id != wl->chip_id) { |
223 | wl1251_error("chip id doesn't match after firmware boot"); | 236 | wl1251_error("chip id doesn't match after firmware boot"); |
224 | return -EIO; | 237 | return -EIO; |
225 | } | 238 | } |
@@ -236,9 +249,9 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) | |||
236 | return -EIO; | 249 | return -EIO; |
237 | } | 250 | } |
238 | /* check that ACX_INTR_INIT_COMPLETE is enabled */ | 251 | /* check that ACX_INTR_INIT_COMPLETE is enabled */ |
239 | else if (interrupt & wl->chip.intr_init_complete) { | 252 | else if (interrupt & WL1251_ACX_INTR_INIT_COMPLETE) { |
240 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, | 253 | wl1251_reg_write32(wl, ACX_REG_INTERRUPT_ACK, |
241 | wl->chip.intr_init_complete); | 254 | WL1251_ACX_INTR_INIT_COMPLETE); |
242 | break; | 255 | break; |
243 | } | 256 | } |
244 | } | 257 | } |
@@ -256,16 +269,15 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) | |||
256 | wl->event_box_addr = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); | 269 | wl->event_box_addr = wl1251_reg_read32(wl, REG_EVENT_MAILBOX_PTR); |
257 | 270 | ||
258 | /* set the working partition to its "running" mode offset */ | 271 | /* set the working partition to its "running" mode offset */ |
259 | wl1251_set_partition(wl, | 272 | wl1251_set_partition(wl, WL1251_PART_WORK_MEM_START, |
260 | wl->chip.p_table[PART_WORK].mem.start, | 273 | WL1251_PART_WORK_MEM_SIZE, |
261 | wl->chip.p_table[PART_WORK].mem.size, | 274 | WL1251_PART_WORK_REG_START, |
262 | wl->chip.p_table[PART_WORK].reg.start, | 275 | WL1251_PART_WORK_REG_SIZE); |
263 | wl->chip.p_table[PART_WORK].reg.size); | ||
264 | 276 | ||
265 | wl1251_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", | 277 | wl1251_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x event_box_addr 0x%x", |
266 | wl->cmd_box_addr, wl->event_box_addr); | 278 | wl->cmd_box_addr, wl->event_box_addr); |
267 | 279 | ||
268 | wl->chip.op_fw_version(wl); | 280 | wl1251_acx_fw_version(wl, wl->fw_ver, sizeof(wl->fw_ver)); |
269 | 281 | ||
270 | /* | 282 | /* |
271 | * in case of full asynchronous mode the firmware event must be | 283 | * in case of full asynchronous mode the firmware event must be |
@@ -275,7 +287,14 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) | |||
275 | /* enable gpio interrupts */ | 287 | /* enable gpio interrupts */ |
276 | wl1251_enable_interrupts(wl); | 288 | wl1251_enable_interrupts(wl); |
277 | 289 | ||
278 | wl->chip.op_target_enable_interrupts(wl); | 290 | /* Enable target's interrupts */ |
291 | wl->intr_mask = WL1251_ACX_INTR_RX0_DATA | | ||
292 | WL1251_ACX_INTR_RX1_DATA | | ||
293 | WL1251_ACX_INTR_TX_RESULT | | ||
294 | WL1251_ACX_INTR_EVENT_A | | ||
295 | WL1251_ACX_INTR_EVENT_B | | ||
296 | WL1251_ACX_INTR_INIT_COMPLETE; | ||
297 | wl1251_boot_target_enable_interrupts(wl); | ||
279 | 298 | ||
280 | /* unmask all mbox events */ | 299 | /* unmask all mbox events */ |
281 | wl->event_mask = 0xffffffff; | 300 | wl->event_mask = 0xffffffff; |
@@ -291,3 +310,218 @@ int wl1251_boot_run_firmware(struct wl1251 *wl) | |||
291 | /* firmware startup completed */ | 310 | /* firmware startup completed */ |
292 | return 0; | 311 | return 0; |
293 | } | 312 | } |
313 | |||
314 | static int wl1251_boot_upload_firmware(struct wl1251 *wl) | ||
315 | { | ||
316 | int addr, chunk_num, partition_limit; | ||
317 | size_t fw_data_len; | ||
318 | u8 *p; | ||
319 | |||
320 | /* whal_FwCtrl_LoadFwImageSm() */ | ||
321 | |||
322 | wl1251_debug(DEBUG_BOOT, "chip id before fw upload: 0x%x", | ||
323 | wl1251_reg_read32(wl, CHIP_ID_B)); | ||
324 | |||
325 | /* 10.0 check firmware length and set partition */ | ||
326 | fw_data_len = (wl->fw[4] << 24) | (wl->fw[5] << 16) | | ||
327 | (wl->fw[6] << 8) | (wl->fw[7]); | ||
328 | |||
329 | wl1251_debug(DEBUG_BOOT, "fw_data_len %zu chunk_size %d", fw_data_len, | ||
330 | CHUNK_SIZE); | ||
331 | |||
332 | if ((fw_data_len % 4) != 0) { | ||
333 | wl1251_error("firmware length not multiple of four"); | ||
334 | return -EIO; | ||
335 | } | ||
336 | |||
337 | wl1251_set_partition(wl, WL1251_PART_DOWN_MEM_START, | ||
338 | WL1251_PART_DOWN_MEM_SIZE, | ||
339 | WL1251_PART_DOWN_REG_START, | ||
340 | WL1251_PART_DOWN_REG_SIZE); | ||
341 | |||
342 | /* 10.1 set partition limit and chunk num */ | ||
343 | chunk_num = 0; | ||
344 | partition_limit = WL1251_PART_DOWN_MEM_SIZE; | ||
345 | |||
346 | while (chunk_num < fw_data_len / CHUNK_SIZE) { | ||
347 | /* 10.2 update partition, if needed */ | ||
348 | addr = WL1251_PART_DOWN_MEM_START + | ||
349 | (chunk_num + 2) * CHUNK_SIZE; | ||
350 | if (addr > partition_limit) { | ||
351 | addr = WL1251_PART_DOWN_MEM_START + | ||
352 | chunk_num * CHUNK_SIZE; | ||
353 | partition_limit = chunk_num * CHUNK_SIZE + | ||
354 | WL1251_PART_DOWN_MEM_SIZE; | ||
355 | wl1251_set_partition(wl, | ||
356 | addr, | ||
357 | WL1251_PART_DOWN_MEM_SIZE, | ||
358 | WL1251_PART_DOWN_REG_START, | ||
359 | WL1251_PART_DOWN_REG_SIZE); | ||
360 | } | ||
361 | |||
362 | /* 10.3 upload the chunk */ | ||
363 | addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; | ||
364 | p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; | ||
365 | wl1251_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x", | ||
366 | p, addr); | ||
367 | wl1251_mem_write(wl, addr, p, CHUNK_SIZE); | ||
368 | |||
369 | chunk_num++; | ||
370 | } | ||
371 | |||
372 | /* 10.4 upload the last chunk */ | ||
373 | addr = WL1251_PART_DOWN_MEM_START + chunk_num * CHUNK_SIZE; | ||
374 | p = wl->fw + FW_HDR_SIZE + chunk_num * CHUNK_SIZE; | ||
375 | wl1251_debug(DEBUG_BOOT, "uploading fw last chunk (%zu B) 0x%p to 0x%x", | ||
376 | fw_data_len % CHUNK_SIZE, p, addr); | ||
377 | wl1251_mem_write(wl, addr, p, fw_data_len % CHUNK_SIZE); | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static int wl1251_boot_upload_nvs(struct wl1251 *wl) | ||
383 | { | ||
384 | size_t nvs_len, nvs_bytes_written, burst_len; | ||
385 | int nvs_start, i; | ||
386 | u32 dest_addr, val; | ||
387 | u8 *nvs_ptr, *nvs; | ||
388 | |||
389 | nvs = wl->nvs; | ||
390 | if (nvs == NULL) | ||
391 | return -ENODEV; | ||
392 | |||
393 | nvs_ptr = nvs; | ||
394 | |||
395 | nvs_len = wl->nvs_len; | ||
396 | nvs_start = wl->fw_len; | ||
397 | |||
398 | /* | ||
399 | * Layout before the actual NVS tables: | ||
400 | * 1 byte : burst length. | ||
401 | * 2 bytes: destination address. | ||
402 | * n bytes: data to burst copy. | ||
403 | * | ||
404 | * This is ended by a 0 length, then the NVS tables. | ||
405 | */ | ||
406 | |||
407 | while (nvs_ptr[0]) { | ||
408 | burst_len = nvs_ptr[0]; | ||
409 | dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); | ||
410 | |||
411 | /* We move our pointer to the data */ | ||
412 | nvs_ptr += 3; | ||
413 | |||
414 | for (i = 0; i < burst_len; i++) { | ||
415 | val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | ||
416 | | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); | ||
417 | |||
418 | wl1251_debug(DEBUG_BOOT, | ||
419 | "nvs burst write 0x%x: 0x%x", | ||
420 | dest_addr, val); | ||
421 | wl1251_mem_write32(wl, dest_addr, val); | ||
422 | |||
423 | nvs_ptr += 4; | ||
424 | dest_addr += 4; | ||
425 | } | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * We've reached the first zero length, the first NVS table | ||
430 | * is 7 bytes further. | ||
431 | */ | ||
432 | nvs_ptr += 7; | ||
433 | nvs_len -= nvs_ptr - nvs; | ||
434 | nvs_len = ALIGN(nvs_len, 4); | ||
435 | |||
436 | /* Now we must set the partition correctly */ | ||
437 | wl1251_set_partition(wl, nvs_start, | ||
438 | WL1251_PART_DOWN_MEM_SIZE, | ||
439 | WL1251_PART_DOWN_REG_START, | ||
440 | WL1251_PART_DOWN_REG_SIZE); | ||
441 | |||
442 | /* And finally we upload the NVS tables */ | ||
443 | nvs_bytes_written = 0; | ||
444 | while (nvs_bytes_written < nvs_len) { | ||
445 | val = (nvs_ptr[0] | (nvs_ptr[1] << 8) | ||
446 | | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); | ||
447 | |||
448 | val = cpu_to_le32(val); | ||
449 | |||
450 | wl1251_debug(DEBUG_BOOT, | ||
451 | "nvs write table 0x%x: 0x%x", | ||
452 | nvs_start, val); | ||
453 | wl1251_mem_write32(wl, nvs_start, val); | ||
454 | |||
455 | nvs_ptr += 4; | ||
456 | nvs_bytes_written += 4; | ||
457 | nvs_start += 4; | ||
458 | } | ||
459 | |||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | int wl1251_boot(struct wl1251 *wl) | ||
464 | { | ||
465 | int ret = 0, minor_minor_e2_ver; | ||
466 | u32 tmp, boot_data; | ||
467 | |||
468 | ret = wl1251_boot_soft_reset(wl); | ||
469 | if (ret < 0) | ||
470 | goto out; | ||
471 | |||
472 | /* 2. start processing NVS file */ | ||
473 | ret = wl1251_boot_upload_nvs(wl); | ||
474 | if (ret < 0) | ||
475 | goto out; | ||
476 | |||
477 | /* write firmware's last address (ie. it's length) to | ||
478 | * ACX_EEPROMLESS_IND_REG */ | ||
479 | wl1251_reg_write32(wl, ACX_EEPROMLESS_IND_REG, wl->fw_len); | ||
480 | |||
481 | /* 6. read the EEPROM parameters */ | ||
482 | tmp = wl1251_reg_read32(wl, SCR_PAD2); | ||
483 | |||
484 | /* 7. read bootdata */ | ||
485 | wl->boot_attr.radio_type = (tmp & 0x0000FF00) >> 8; | ||
486 | wl->boot_attr.major = (tmp & 0x00FF0000) >> 16; | ||
487 | tmp = wl1251_reg_read32(wl, SCR_PAD3); | ||
488 | |||
489 | /* 8. check bootdata and call restart sequence */ | ||
490 | wl->boot_attr.minor = (tmp & 0x00FF0000) >> 16; | ||
491 | minor_minor_e2_ver = (tmp & 0xFF000000) >> 24; | ||
492 | |||
493 | wl1251_debug(DEBUG_BOOT, "radioType 0x%x majorE2Ver 0x%x " | ||
494 | "minorE2Ver 0x%x minor_minor_e2_ver 0x%x", | ||
495 | wl->boot_attr.radio_type, wl->boot_attr.major, | ||
496 | wl->boot_attr.minor, minor_minor_e2_ver); | ||
497 | |||
498 | ret = wl1251_boot_init_seq(wl); | ||
499 | if (ret < 0) | ||
500 | goto out; | ||
501 | |||
502 | /* 9. NVS processing done */ | ||
503 | boot_data = wl1251_reg_read32(wl, ACX_REG_ECPU_CONTROL); | ||
504 | |||
505 | wl1251_debug(DEBUG_BOOT, "halt boot_data 0x%x", boot_data); | ||
506 | |||
507 | /* 10. check that ECPU_CONTROL_HALT bits are set in | ||
508 | * pWhalBus->uBootData and start uploading firmware | ||
509 | */ | ||
510 | if ((boot_data & ECPU_CONTROL_HALT) == 0) { | ||
511 | wl1251_error("boot failed, ECPU_CONTROL_HALT not set"); | ||
512 | ret = -EIO; | ||
513 | goto out; | ||
514 | } | ||
515 | |||
516 | ret = wl1251_boot_upload_firmware(wl); | ||
517 | if (ret < 0) | ||
518 | goto out; | ||
519 | |||
520 | /* 10.5 start firmware */ | ||
521 | ret = wl1251_boot_run_firmware(wl); | ||
522 | if (ret < 0) | ||
523 | goto out; | ||
524 | |||
525 | out: | ||
526 | return ret; | ||
527 | } | ||