diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-05-05 13:32:35 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-05-05 13:32:35 -0400 |
commit | a70171dce9cd44cb06c7d299eba9fa87a8933045 (patch) | |
tree | 5425df5f33fadc617c7dec99578d06f0d933578e /drivers/net/wireless/wl12xx/boot.c | |
parent | 5a412ad7f4c95bb5b756aa12b52646e857e7c75d (diff) | |
parent | eaef6a93bd52a2cc47b9fce201310010707afdb4 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
Conflicts:
drivers/net/wireless/libertas/if_cs.c
drivers/net/wireless/rtlwifi/pci.c
net/bluetooth/l2cap_sock.c
Diffstat (limited to 'drivers/net/wireless/wl12xx/boot.c')
-rw-r--r-- | drivers/net/wireless/wl12xx/boot.c | 285 |
1 files changed, 240 insertions, 45 deletions
diff --git a/drivers/net/wireless/wl12xx/boot.c b/drivers/net/wireless/wl12xx/boot.c index 6934dffd5174..d263ebb6f974 100644 --- a/drivers/net/wireless/wl12xx/boot.c +++ b/drivers/net/wireless/wl12xx/boot.c | |||
@@ -22,6 +22,7 @@ | |||
22 | */ | 22 | */ |
23 | 23 | ||
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <linux/wl12xx.h> | ||
25 | 26 | ||
26 | #include "acx.h" | 27 | #include "acx.h" |
27 | #include "reg.h" | 28 | #include "reg.h" |
@@ -243,33 +244,57 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) | |||
243 | if (wl->nvs == NULL) | 244 | if (wl->nvs == NULL) |
244 | return -ENODEV; | 245 | return -ENODEV; |
245 | 246 | ||
246 | /* | 247 | if (wl->chip.id == CHIP_ID_1283_PG20) { |
247 | * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz band | 248 | struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; |
248 | * configurations) can be removed when those NVS files stop floating | 249 | |
249 | * around. | 250 | if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { |
250 | */ | 251 | if (nvs->general_params.dual_mode_select) |
251 | if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || | 252 | wl->enable_11a = true; |
252 | wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { | 253 | } else { |
253 | /* for now 11a is unsupported in AP mode */ | 254 | wl1271_error("nvs size is not as expected: %zu != %zu", |
254 | if (wl->bss_type != BSS_TYPE_AP_BSS && | 255 | wl->nvs_len, |
255 | wl->nvs->general_params.dual_mode_select) | 256 | sizeof(struct wl128x_nvs_file)); |
256 | wl->enable_11a = true; | 257 | kfree(wl->nvs); |
257 | } | 258 | wl->nvs = NULL; |
259 | wl->nvs_len = 0; | ||
260 | return -EILSEQ; | ||
261 | } | ||
258 | 262 | ||
259 | if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && | 263 | /* only the first part of the NVS needs to be uploaded */ |
260 | (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || | 264 | nvs_len = sizeof(nvs->nvs); |
261 | wl->enable_11a)) { | 265 | nvs_ptr = (u8 *)nvs->nvs; |
262 | wl1271_error("nvs size is not as expected: %zu != %zu", | 266 | |
263 | wl->nvs_len, sizeof(struct wl1271_nvs_file)); | 267 | } else { |
264 | kfree(wl->nvs); | 268 | struct wl1271_nvs_file *nvs = |
265 | wl->nvs = NULL; | 269 | (struct wl1271_nvs_file *)wl->nvs; |
266 | wl->nvs_len = 0; | 270 | /* |
267 | return -EILSEQ; | 271 | * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz |
268 | } | 272 | * band configurations) can be removed when those NVS files stop |
273 | * floating around. | ||
274 | */ | ||
275 | if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || | ||
276 | wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { | ||
277 | /* for now 11a is unsupported in AP mode */ | ||
278 | if (wl->bss_type != BSS_TYPE_AP_BSS && | ||
279 | nvs->general_params.dual_mode_select) | ||
280 | wl->enable_11a = true; | ||
281 | } | ||
269 | 282 | ||
270 | /* only the first part of the NVS needs to be uploaded */ | 283 | if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && |
271 | nvs_len = sizeof(wl->nvs->nvs); | 284 | (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || |
272 | nvs_ptr = (u8 *)wl->nvs->nvs; | 285 | wl->enable_11a)) { |
286 | wl1271_error("nvs size is not as expected: %zu != %zu", | ||
287 | wl->nvs_len, sizeof(struct wl1271_nvs_file)); | ||
288 | kfree(wl->nvs); | ||
289 | wl->nvs = NULL; | ||
290 | wl->nvs_len = 0; | ||
291 | return -EILSEQ; | ||
292 | } | ||
293 | |||
294 | /* only the first part of the NVS needs to be uploaded */ | ||
295 | nvs_len = sizeof(nvs->nvs); | ||
296 | nvs_ptr = (u8 *) nvs->nvs; | ||
297 | } | ||
273 | 298 | ||
274 | /* update current MAC address to NVS */ | 299 | /* update current MAC address to NVS */ |
275 | nvs_ptr[11] = wl->mac_addr[0]; | 300 | nvs_ptr[11] = wl->mac_addr[0]; |
@@ -319,10 +344,13 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) | |||
319 | /* | 344 | /* |
320 | * We've reached the first zero length, the first NVS table | 345 | * We've reached the first zero length, the first NVS table |
321 | * is located at an aligned offset which is at least 7 bytes further. | 346 | * is located at an aligned offset which is at least 7 bytes further. |
347 | * NOTE: The wl->nvs->nvs element must be first, in order to | ||
348 | * simplify the casting, we assume it is at the beginning of | ||
349 | * the wl->nvs structure. | ||
322 | */ | 350 | */ |
323 | nvs_ptr = (u8 *)wl->nvs->nvs + | 351 | nvs_ptr = (u8 *)wl->nvs + |
324 | ALIGN(nvs_ptr - (u8 *)wl->nvs->nvs + 7, 4); | 352 | ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); |
325 | nvs_len -= nvs_ptr - (u8 *)wl->nvs->nvs; | 353 | nvs_len -= nvs_ptr - (u8 *)wl->nvs; |
326 | 354 | ||
327 | /* Now we must set the partition correctly */ | 355 | /* Now we must set the partition correctly */ |
328 | wl1271_set_partition(wl, &part_table[PART_WORK]); | 356 | wl1271_set_partition(wl, &part_table[PART_WORK]); |
@@ -450,10 +478,14 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl) | |||
450 | DISCONNECT_EVENT_COMPLETE_ID | | 478 | DISCONNECT_EVENT_COMPLETE_ID | |
451 | RSSI_SNR_TRIGGER_0_EVENT_ID | | 479 | RSSI_SNR_TRIGGER_0_EVENT_ID | |
452 | PSPOLL_DELIVERY_FAILURE_EVENT_ID | | 480 | PSPOLL_DELIVERY_FAILURE_EVENT_ID | |
453 | SOFT_GEMINI_SENSE_EVENT_ID; | 481 | SOFT_GEMINI_SENSE_EVENT_ID | |
482 | MAX_TX_RETRY_EVENT_ID; | ||
454 | 483 | ||
455 | if (wl->bss_type == BSS_TYPE_AP_BSS) | 484 | if (wl->bss_type == BSS_TYPE_AP_BSS) |
456 | wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID; | 485 | wl->event_mask |= STA_REMOVE_COMPLETE_EVENT_ID | |
486 | INACTIVE_STA_EVENT_ID; | ||
487 | else | ||
488 | wl->event_mask |= DUMMY_PACKET_EVENT_ID; | ||
457 | 489 | ||
458 | ret = wl1271_event_unmask(wl); | 490 | ret = wl1271_event_unmask(wl); |
459 | if (ret < 0) { | 491 | if (ret < 0) { |
@@ -493,24 +525,159 @@ static void wl1271_boot_hw_version(struct wl1271 *wl) | |||
493 | wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; | 525 | wl->quirks |= WL12XX_QUIRK_END_OF_TRANSACTION; |
494 | } | 526 | } |
495 | 527 | ||
496 | /* uploads NVS and firmware */ | 528 | static int wl128x_switch_tcxo_to_fref(struct wl1271 *wl) |
497 | int wl1271_load_firmware(struct wl1271 *wl) | ||
498 | { | 529 | { |
499 | int ret = 0; | 530 | u16 spare_reg; |
500 | u32 tmp, clk, pause; | 531 | |
532 | /* Mask bits [2] & [8:4] in the sys_clk_cfg register */ | ||
533 | spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG); | ||
534 | if (spare_reg == 0xFFFF) | ||
535 | return -EFAULT; | ||
536 | spare_reg |= (BIT(3) | BIT(5) | BIT(6)); | ||
537 | wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg); | ||
538 | |||
539 | /* Enable FREF_CLK_REQ & mux MCS and coex PLLs to FREF */ | ||
540 | wl1271_top_reg_write(wl, SYS_CLK_CFG_REG, | ||
541 | WL_CLK_REQ_TYPE_PG2 | MCS_PLL_CLK_SEL_FREF); | ||
542 | |||
543 | /* Delay execution for 15msec, to let the HW settle */ | ||
544 | mdelay(15); | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static bool wl128x_is_tcxo_valid(struct wl1271 *wl) | ||
550 | { | ||
551 | u16 tcxo_detection; | ||
552 | |||
553 | tcxo_detection = wl1271_top_reg_read(wl, TCXO_CLK_DETECT_REG); | ||
554 | if (tcxo_detection & TCXO_DET_FAILED) | ||
555 | return false; | ||
556 | |||
557 | return true; | ||
558 | } | ||
559 | |||
560 | static bool wl128x_is_fref_valid(struct wl1271 *wl) | ||
561 | { | ||
562 | u16 fref_detection; | ||
563 | |||
564 | fref_detection = wl1271_top_reg_read(wl, FREF_CLK_DETECT_REG); | ||
565 | if (fref_detection & FREF_CLK_DETECT_FAIL) | ||
566 | return false; | ||
567 | |||
568 | return true; | ||
569 | } | ||
570 | |||
571 | static int wl128x_manually_configure_mcs_pll(struct wl1271 *wl) | ||
572 | { | ||
573 | wl1271_top_reg_write(wl, MCS_PLL_M_REG, MCS_PLL_M_REG_VAL); | ||
574 | wl1271_top_reg_write(wl, MCS_PLL_N_REG, MCS_PLL_N_REG_VAL); | ||
575 | wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, MCS_PLL_CONFIG_REG_VAL); | ||
576 | |||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static int wl128x_configure_mcs_pll(struct wl1271 *wl, int clk) | ||
581 | { | ||
582 | u16 spare_reg; | ||
583 | u16 pll_config; | ||
584 | u8 input_freq; | ||
585 | |||
586 | /* Mask bits [3:1] in the sys_clk_cfg register */ | ||
587 | spare_reg = wl1271_top_reg_read(wl, WL_SPARE_REG); | ||
588 | if (spare_reg == 0xFFFF) | ||
589 | return -EFAULT; | ||
590 | spare_reg |= BIT(2); | ||
591 | wl1271_top_reg_write(wl, WL_SPARE_REG, spare_reg); | ||
592 | |||
593 | /* Handle special cases of the TCXO clock */ | ||
594 | if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_8 || | ||
595 | wl->tcxo_clock == WL12XX_TCXOCLOCK_33_6) | ||
596 | return wl128x_manually_configure_mcs_pll(wl); | ||
597 | |||
598 | /* Set the input frequency according to the selected clock source */ | ||
599 | input_freq = (clk & 1) + 1; | ||
600 | |||
601 | pll_config = wl1271_top_reg_read(wl, MCS_PLL_CONFIG_REG); | ||
602 | if (pll_config == 0xFFFF) | ||
603 | return -EFAULT; | ||
604 | pll_config |= (input_freq << MCS_SEL_IN_FREQ_SHIFT); | ||
605 | pll_config |= MCS_PLL_ENABLE_HP; | ||
606 | wl1271_top_reg_write(wl, MCS_PLL_CONFIG_REG, pll_config); | ||
607 | |||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | /* | ||
612 | * WL128x has two clocks input - TCXO and FREF. | ||
613 | * TCXO is the main clock of the device, while FREF is used to sync | ||
614 | * between the GPS and the cellular modem. | ||
615 | * In cases where TCXO is 32.736MHz or 16.368MHz, the FREF will be used | ||
616 | * as the WLAN/BT main clock. | ||
617 | */ | ||
618 | static int wl128x_boot_clk(struct wl1271 *wl, int *selected_clock) | ||
619 | { | ||
620 | u16 sys_clk_cfg; | ||
621 | |||
622 | /* For XTAL-only modes, FREF will be used after switching from TCXO */ | ||
623 | if (wl->ref_clock == WL12XX_REFCLOCK_26_XTAL || | ||
624 | wl->ref_clock == WL12XX_REFCLOCK_38_XTAL) { | ||
625 | if (!wl128x_switch_tcxo_to_fref(wl)) | ||
626 | return -EINVAL; | ||
627 | goto fref_clk; | ||
628 | } | ||
629 | |||
630 | /* Query the HW, to determine which clock source we should use */ | ||
631 | sys_clk_cfg = wl1271_top_reg_read(wl, SYS_CLK_CFG_REG); | ||
632 | if (sys_clk_cfg == 0xFFFF) | ||
633 | return -EINVAL; | ||
634 | if (sys_clk_cfg & PRCM_CM_EN_MUX_WLAN_FREF) | ||
635 | goto fref_clk; | ||
636 | |||
637 | /* If TCXO is either 32.736MHz or 16.368MHz, switch to FREF */ | ||
638 | if (wl->tcxo_clock == WL12XX_TCXOCLOCK_16_368 || | ||
639 | wl->tcxo_clock == WL12XX_TCXOCLOCK_32_736) { | ||
640 | if (!wl128x_switch_tcxo_to_fref(wl)) | ||
641 | return -EINVAL; | ||
642 | goto fref_clk; | ||
643 | } | ||
644 | |||
645 | /* TCXO clock is selected */ | ||
646 | if (!wl128x_is_tcxo_valid(wl)) | ||
647 | return -EINVAL; | ||
648 | *selected_clock = wl->tcxo_clock; | ||
649 | goto config_mcs_pll; | ||
650 | |||
651 | fref_clk: | ||
652 | /* FREF clock is selected */ | ||
653 | if (!wl128x_is_fref_valid(wl)) | ||
654 | return -EINVAL; | ||
655 | *selected_clock = wl->ref_clock; | ||
656 | |||
657 | config_mcs_pll: | ||
658 | return wl128x_configure_mcs_pll(wl, *selected_clock); | ||
659 | } | ||
660 | |||
661 | static int wl127x_boot_clk(struct wl1271 *wl) | ||
662 | { | ||
663 | u32 pause; | ||
664 | u32 clk; | ||
501 | 665 | ||
502 | wl1271_boot_hw_version(wl); | 666 | wl1271_boot_hw_version(wl); |
503 | 667 | ||
504 | if (wl->ref_clock == 0 || wl->ref_clock == 2 || wl->ref_clock == 4) | 668 | if (wl->ref_clock == CONF_REF_CLK_19_2_E || |
669 | wl->ref_clock == CONF_REF_CLK_38_4_E || | ||
670 | wl->ref_clock == CONF_REF_CLK_38_4_M_XTAL) | ||
505 | /* ref clk: 19.2/38.4/38.4-XTAL */ | 671 | /* ref clk: 19.2/38.4/38.4-XTAL */ |
506 | clk = 0x3; | 672 | clk = 0x3; |
507 | else if (wl->ref_clock == 1 || wl->ref_clock == 3) | 673 | else if (wl->ref_clock == CONF_REF_CLK_26_E || |
674 | wl->ref_clock == CONF_REF_CLK_52_E) | ||
508 | /* ref clk: 26/52 */ | 675 | /* ref clk: 26/52 */ |
509 | clk = 0x5; | 676 | clk = 0x5; |
510 | else | 677 | else |
511 | return -EINVAL; | 678 | return -EINVAL; |
512 | 679 | ||
513 | if (wl->ref_clock != 0) { | 680 | if (wl->ref_clock != CONF_REF_CLK_19_2_E) { |
514 | u16 val; | 681 | u16 val; |
515 | /* Set clock type (open drain) */ | 682 | /* Set clock type (open drain) */ |
516 | val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); | 683 | val = wl1271_top_reg_read(wl, OCP_REG_CLK_TYPE); |
@@ -540,6 +707,26 @@ int wl1271_load_firmware(struct wl1271 *wl) | |||
540 | pause |= WU_COUNTER_PAUSE_VAL; | 707 | pause |= WU_COUNTER_PAUSE_VAL; |
541 | wl1271_write32(wl, WU_COUNTER_PAUSE, pause); | 708 | wl1271_write32(wl, WU_COUNTER_PAUSE, pause); |
542 | 709 | ||
710 | return 0; | ||
711 | } | ||
712 | |||
713 | /* uploads NVS and firmware */ | ||
714 | int wl1271_load_firmware(struct wl1271 *wl) | ||
715 | { | ||
716 | int ret = 0; | ||
717 | u32 tmp, clk; | ||
718 | int selected_clock = -1; | ||
719 | |||
720 | if (wl->chip.id == CHIP_ID_1283_PG20) { | ||
721 | ret = wl128x_boot_clk(wl, &selected_clock); | ||
722 | if (ret < 0) | ||
723 | goto out; | ||
724 | } else { | ||
725 | ret = wl127x_boot_clk(wl); | ||
726 | if (ret < 0) | ||
727 | goto out; | ||
728 | } | ||
729 | |||
543 | /* Continue the ELP wake up sequence */ | 730 | /* Continue the ELP wake up sequence */ |
544 | wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); | 731 | wl1271_write32(wl, WELP_ARM_COMMAND, WELP_ARM_COMMAND_VAL); |
545 | udelay(500); | 732 | udelay(500); |
@@ -555,7 +742,12 @@ int wl1271_load_firmware(struct wl1271 *wl) | |||
555 | 742 | ||
556 | wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); | 743 | wl1271_debug(DEBUG_BOOT, "clk2 0x%x", clk); |
557 | 744 | ||
558 | clk |= (wl->ref_clock << 1) << 4; | 745 | if (wl->chip.id == CHIP_ID_1283_PG20) { |
746 | clk |= ((selected_clock & 0x3) << 1) << 4; | ||
747 | } else { | ||
748 | clk |= (wl->ref_clock << 1) << 4; | ||
749 | } | ||
750 | |||
559 | wl1271_write32(wl, DRPW_SCRATCH_START, clk); | 751 | wl1271_write32(wl, DRPW_SCRATCH_START, clk); |
560 | 752 | ||
561 | wl1271_set_partition(wl, &part_table[PART_WORK]); | 753 | wl1271_set_partition(wl, &part_table[PART_WORK]); |
@@ -585,16 +777,12 @@ int wl1271_load_firmware(struct wl1271 *wl) | |||
585 | /* 6. read the EEPROM parameters */ | 777 | /* 6. read the EEPROM parameters */ |
586 | tmp = wl1271_read32(wl, SCR_PAD2); | 778 | tmp = wl1271_read32(wl, SCR_PAD2); |
587 | 779 | ||
588 | ret = wl1271_boot_write_irq_polarity(wl); | ||
589 | if (ret < 0) | ||
590 | goto out; | ||
591 | |||
592 | wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, | ||
593 | WL1271_ACX_ALL_EVENTS_VECTOR); | ||
594 | |||
595 | /* WL1271: The reference driver skips steps 7 to 10 (jumps directly | 780 | /* WL1271: The reference driver skips steps 7 to 10 (jumps directly |
596 | * to upload_fw) */ | 781 | * to upload_fw) */ |
597 | 782 | ||
783 | if (wl->chip.id == CHIP_ID_1283_PG20) | ||
784 | wl1271_top_reg_write(wl, SDIO_IO_DS, wl->conf.hci_io_ds); | ||
785 | |||
598 | ret = wl1271_boot_upload_firmware(wl); | 786 | ret = wl1271_boot_upload_firmware(wl); |
599 | if (ret < 0) | 787 | if (ret < 0) |
600 | goto out; | 788 | goto out; |
@@ -618,6 +806,13 @@ int wl1271_boot(struct wl1271 *wl) | |||
618 | if (ret < 0) | 806 | if (ret < 0) |
619 | goto out; | 807 | goto out; |
620 | 808 | ||
809 | ret = wl1271_boot_write_irq_polarity(wl); | ||
810 | if (ret < 0) | ||
811 | goto out; | ||
812 | |||
813 | wl1271_write32(wl, ACX_REG_INTERRUPT_MASK, | ||
814 | WL1271_ACX_ALL_EVENTS_VECTOR); | ||
815 | |||
621 | /* Enable firmware interrupts now */ | 816 | /* Enable firmware interrupts now */ |
622 | wl1271_boot_enable_interrupts(wl); | 817 | wl1271_boot_enable_interrupts(wl); |
623 | 818 | ||