diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/nvm.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/nvm.c | 290 |
1 files changed, 286 insertions, 4 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index 5383429d96c1..123e0a16aea8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c | |||
@@ -63,12 +63,16 @@ | |||
63 | * | 63 | * |
64 | *****************************************************************************/ | 64 | *****************************************************************************/ |
65 | #include <linux/firmware.h> | 65 | #include <linux/firmware.h> |
66 | #include <linux/rtnetlink.h> | ||
67 | #include <linux/pci.h> | ||
68 | #include <linux/acpi.h> | ||
66 | #include "iwl-trans.h" | 69 | #include "iwl-trans.h" |
67 | #include "iwl-csr.h" | 70 | #include "iwl-csr.h" |
68 | #include "mvm.h" | 71 | #include "mvm.h" |
69 | #include "iwl-eeprom-parse.h" | 72 | #include "iwl-eeprom-parse.h" |
70 | #include "iwl-eeprom-read.h" | 73 | #include "iwl-eeprom-read.h" |
71 | #include "iwl-nvm-parse.h" | 74 | #include "iwl-nvm-parse.h" |
75 | #include "iwl-prph.h" | ||
72 | 76 | ||
73 | /* Default NVM size to read */ | 77 | /* Default NVM size to read */ |
74 | #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) | 78 | #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) |
@@ -262,7 +266,9 @@ static struct iwl_nvm_data * | |||
262 | iwl_parse_nvm_sections(struct iwl_mvm *mvm) | 266 | iwl_parse_nvm_sections(struct iwl_mvm *mvm) |
263 | { | 267 | { |
264 | struct iwl_nvm_section *sections = mvm->nvm_sections; | 268 | struct iwl_nvm_section *sections = mvm->nvm_sections; |
265 | const __le16 *hw, *sw, *calib, *regulatory, *mac_override; | 269 | const __le16 *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; |
270 | bool is_family_8000_a_step = false, lar_enabled; | ||
271 | u32 mac_addr0, mac_addr1; | ||
266 | 272 | ||
267 | /* Checking for required sections */ | 273 | /* Checking for required sections */ |
268 | if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { | 274 | if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { |
@@ -286,22 +292,43 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) | |||
286 | "Can't parse mac_address, empty sections\n"); | 292 | "Can't parse mac_address, empty sections\n"); |
287 | return NULL; | 293 | return NULL; |
288 | } | 294 | } |
295 | |||
296 | if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) | ||
297 | is_family_8000_a_step = true; | ||
298 | |||
299 | /* PHY_SKU section is mandatory in B0 */ | ||
300 | if (!is_family_8000_a_step && | ||
301 | !mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) { | ||
302 | IWL_ERR(mvm, | ||
303 | "Can't parse phy_sku in B0, empty sections\n"); | ||
304 | return NULL; | ||
305 | } | ||
289 | } | 306 | } |
290 | 307 | ||
291 | if (WARN_ON(!mvm->cfg)) | 308 | if (WARN_ON(!mvm->cfg)) |
292 | return NULL; | 309 | return NULL; |
293 | 310 | ||
311 | /* read the mac address from WFMP registers */ | ||
312 | mac_addr0 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_0); | ||
313 | mac_addr1 = iwl_trans_read_prph(mvm->trans, WFMP_MAC_ADDR_1); | ||
314 | |||
294 | hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; | 315 | hw = (const __le16 *)sections[mvm->cfg->nvm_hw_section_num].data; |
295 | sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; | 316 | sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; |
296 | calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; | 317 | calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; |
297 | regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; | 318 | regulatory = (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data; |
298 | mac_override = | 319 | mac_override = |
299 | (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; | 320 | (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data; |
321 | phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data; | ||
322 | |||
323 | lar_enabled = !iwlwifi_mod_params.lar_disable && | ||
324 | (mvm->fw->ucode_capa.capa[0] & | ||
325 | IWL_UCODE_TLV_CAPA_LAR_SUPPORT); | ||
300 | 326 | ||
301 | return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, | 327 | return iwl_parse_nvm_data(mvm->trans->dev, mvm->cfg, hw, sw, calib, |
302 | regulatory, mac_override, | 328 | regulatory, mac_override, phy_sku, |
303 | mvm->fw->valid_tx_ant, | 329 | mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant, |
304 | mvm->fw->valid_rx_ant); | 330 | lar_enabled, is_family_8000_a_step, |
331 | mac_addr0, mac_addr1); | ||
305 | } | 332 | } |
306 | 333 | ||
307 | #define MAX_NVM_FILE_LEN 16384 | 334 | #define MAX_NVM_FILE_LEN 16384 |
@@ -570,3 +597,258 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) | |||
570 | 597 | ||
571 | return 0; | 598 | return 0; |
572 | } | 599 | } |
600 | |||
601 | struct iwl_mcc_update_resp * | ||
602 | iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, | ||
603 | enum iwl_mcc_source src_id) | ||
604 | { | ||
605 | struct iwl_mcc_update_cmd mcc_update_cmd = { | ||
606 | .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), | ||
607 | .source_id = (u8)src_id, | ||
608 | }; | ||
609 | struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; | ||
610 | struct iwl_rx_packet *pkt; | ||
611 | struct iwl_host_cmd cmd = { | ||
612 | .id = MCC_UPDATE_CMD, | ||
613 | .flags = CMD_WANT_SKB, | ||
614 | .data = { &mcc_update_cmd }, | ||
615 | }; | ||
616 | |||
617 | int ret; | ||
618 | u32 status; | ||
619 | int resp_len, n_channels; | ||
620 | u16 mcc; | ||
621 | |||
622 | if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) | ||
623 | return ERR_PTR(-EOPNOTSUPP); | ||
624 | |||
625 | cmd.len[0] = sizeof(struct iwl_mcc_update_cmd); | ||
626 | |||
627 | IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n", | ||
628 | alpha2[0], alpha2[1], src_id); | ||
629 | |||
630 | ret = iwl_mvm_send_cmd(mvm, &cmd); | ||
631 | if (ret) | ||
632 | return ERR_PTR(ret); | ||
633 | |||
634 | pkt = cmd.resp_pkt; | ||
635 | if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { | ||
636 | IWL_ERR(mvm, "Bad return from MCC_UPDATE_COMMAND (0x%08X)\n", | ||
637 | pkt->hdr.flags); | ||
638 | ret = -EIO; | ||
639 | goto exit; | ||
640 | } | ||
641 | |||
642 | /* Extract MCC response */ | ||
643 | mcc_resp = (void *)pkt->data; | ||
644 | status = le32_to_cpu(mcc_resp->status); | ||
645 | |||
646 | mcc = le16_to_cpu(mcc_resp->mcc); | ||
647 | |||
648 | /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ | ||
649 | if (mcc == 0) { | ||
650 | mcc = 0x3030; /* "00" - world */ | ||
651 | mcc_resp->mcc = cpu_to_le16(mcc); | ||
652 | } | ||
653 | |||
654 | n_channels = __le32_to_cpu(mcc_resp->n_channels); | ||
655 | IWL_DEBUG_LAR(mvm, | ||
656 | "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", | ||
657 | status, mcc, mcc >> 8, mcc & 0xff, | ||
658 | !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); | ||
659 | |||
660 | resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32); | ||
661 | resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); | ||
662 | if (!resp_cp) { | ||
663 | ret = -ENOMEM; | ||
664 | goto exit; | ||
665 | } | ||
666 | |||
667 | ret = 0; | ||
668 | exit: | ||
669 | iwl_free_resp(&cmd); | ||
670 | if (ret) | ||
671 | return ERR_PTR(ret); | ||
672 | return resp_cp; | ||
673 | } | ||
674 | |||
675 | #ifdef CONFIG_ACPI | ||
676 | #define WRD_METHOD "WRDD" | ||
677 | #define WRDD_WIFI (0x07) | ||
678 | #define WRDD_WIGIG (0x10) | ||
679 | |||
680 | static u32 iwl_mvm_wrdd_get_mcc(struct iwl_mvm *mvm, union acpi_object *wrdd) | ||
681 | { | ||
682 | union acpi_object *mcc_pkg, *domain_type, *mcc_value; | ||
683 | u32 i; | ||
684 | |||
685 | if (wrdd->type != ACPI_TYPE_PACKAGE || | ||
686 | wrdd->package.count < 2 || | ||
687 | wrdd->package.elements[0].type != ACPI_TYPE_INTEGER || | ||
688 | wrdd->package.elements[0].integer.value != 0) { | ||
689 | IWL_DEBUG_LAR(mvm, "Unsupported wrdd structure\n"); | ||
690 | return 0; | ||
691 | } | ||
692 | |||
693 | for (i = 1 ; i < wrdd->package.count ; ++i) { | ||
694 | mcc_pkg = &wrdd->package.elements[i]; | ||
695 | |||
696 | if (mcc_pkg->type != ACPI_TYPE_PACKAGE || | ||
697 | mcc_pkg->package.count < 2 || | ||
698 | mcc_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || | ||
699 | mcc_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { | ||
700 | mcc_pkg = NULL; | ||
701 | continue; | ||
702 | } | ||
703 | |||
704 | domain_type = &mcc_pkg->package.elements[0]; | ||
705 | if (domain_type->integer.value == WRDD_WIFI) | ||
706 | break; | ||
707 | |||
708 | mcc_pkg = NULL; | ||
709 | } | ||
710 | |||
711 | if (mcc_pkg) { | ||
712 | mcc_value = &mcc_pkg->package.elements[1]; | ||
713 | return mcc_value->integer.value; | ||
714 | } | ||
715 | |||
716 | return 0; | ||
717 | } | ||
718 | |||
719 | static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) | ||
720 | { | ||
721 | acpi_handle root_handle; | ||
722 | acpi_handle handle; | ||
723 | struct acpi_buffer wrdd = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
724 | acpi_status status; | ||
725 | u32 mcc_val; | ||
726 | struct pci_dev *pdev = to_pci_dev(mvm->dev); | ||
727 | |||
728 | root_handle = ACPI_HANDLE(&pdev->dev); | ||
729 | if (!root_handle) { | ||
730 | IWL_DEBUG_LAR(mvm, | ||
731 | "Could not retrieve root port ACPI handle\n"); | ||
732 | return -ENOENT; | ||
733 | } | ||
734 | |||
735 | /* Get the method's handle */ | ||
736 | status = acpi_get_handle(root_handle, (acpi_string)WRD_METHOD, &handle); | ||
737 | if (ACPI_FAILURE(status)) { | ||
738 | IWL_DEBUG_LAR(mvm, "WRD method not found\n"); | ||
739 | return -ENOENT; | ||
740 | } | ||
741 | |||
742 | /* Call WRDD with no arguments */ | ||
743 | status = acpi_evaluate_object(handle, NULL, NULL, &wrdd); | ||
744 | if (ACPI_FAILURE(status)) { | ||
745 | IWL_DEBUG_LAR(mvm, "WRDC invocation failed (0x%x)\n", status); | ||
746 | return -ENOENT; | ||
747 | } | ||
748 | |||
749 | mcc_val = iwl_mvm_wrdd_get_mcc(mvm, wrdd.pointer); | ||
750 | kfree(wrdd.pointer); | ||
751 | if (!mcc_val) | ||
752 | return -ENOENT; | ||
753 | |||
754 | mcc[0] = (mcc_val >> 8) & 0xff; | ||
755 | mcc[1] = mcc_val & 0xff; | ||
756 | mcc[2] = '\0'; | ||
757 | return 0; | ||
758 | } | ||
759 | #else /* CONFIG_ACPI */ | ||
760 | static int iwl_mvm_get_bios_mcc(struct iwl_mvm *mvm, char *mcc) | ||
761 | { | ||
762 | return -ENOENT; | ||
763 | } | ||
764 | #endif | ||
765 | |||
766 | int iwl_mvm_init_mcc(struct iwl_mvm *mvm) | ||
767 | { | ||
768 | bool tlv_lar; | ||
769 | bool nvm_lar; | ||
770 | int retval; | ||
771 | struct ieee80211_regdomain *regd; | ||
772 | char mcc[3]; | ||
773 | |||
774 | if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000) { | ||
775 | tlv_lar = mvm->fw->ucode_capa.capa[0] & | ||
776 | IWL_UCODE_TLV_CAPA_LAR_SUPPORT; | ||
777 | nvm_lar = mvm->nvm_data->lar_enabled; | ||
778 | if (tlv_lar != nvm_lar) | ||
779 | IWL_INFO(mvm, | ||
780 | "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n", | ||
781 | tlv_lar ? "enabled" : "disabled", | ||
782 | nvm_lar ? "enabled" : "disabled"); | ||
783 | } | ||
784 | |||
785 | if (!iwl_mvm_is_lar_supported(mvm)) | ||
786 | return 0; | ||
787 | |||
788 | /* | ||
789 | * During HW restart, only replay the last set MCC to FW. Otherwise, | ||
790 | * queue an update to cfg80211 to retrieve the default alpha2 from FW. | ||
791 | */ | ||
792 | if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { | ||
793 | /* This should only be called during vif up and hold RTNL */ | ||
794 | return iwl_mvm_init_fw_regd(mvm); | ||
795 | } | ||
796 | |||
797 | /* | ||
798 | * Driver regulatory hint for initial update, this also informs the | ||
799 | * firmware we support wifi location updates. | ||
800 | * Disallow scans that might crash the FW while the LAR regdomain | ||
801 | * is not set. | ||
802 | */ | ||
803 | mvm->lar_regdom_set = false; | ||
804 | |||
805 | regd = iwl_mvm_get_current_regdomain(mvm, NULL); | ||
806 | if (IS_ERR_OR_NULL(regd)) | ||
807 | return -EIO; | ||
808 | |||
809 | if (iwl_mvm_is_wifi_mcc_supported(mvm) && | ||
810 | !iwl_mvm_get_bios_mcc(mvm, mcc)) { | ||
811 | kfree(regd); | ||
812 | regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, | ||
813 | MCC_SOURCE_BIOS, NULL); | ||
814 | if (IS_ERR_OR_NULL(regd)) | ||
815 | return -EIO; | ||
816 | } | ||
817 | |||
818 | retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd); | ||
819 | kfree(regd); | ||
820 | return retval; | ||
821 | } | ||
822 | |||
823 | int iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm, | ||
824 | struct iwl_rx_cmd_buffer *rxb, | ||
825 | struct iwl_device_cmd *cmd) | ||
826 | { | ||
827 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
828 | struct iwl_mcc_chub_notif *notif = (void *)pkt->data; | ||
829 | enum iwl_mcc_source src; | ||
830 | char mcc[3]; | ||
831 | struct ieee80211_regdomain *regd; | ||
832 | |||
833 | lockdep_assert_held(&mvm->mutex); | ||
834 | |||
835 | if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) | ||
836 | return 0; | ||
837 | |||
838 | mcc[0] = notif->mcc >> 8; | ||
839 | mcc[1] = notif->mcc & 0xff; | ||
840 | mcc[2] = '\0'; | ||
841 | src = notif->source_id; | ||
842 | |||
843 | IWL_DEBUG_LAR(mvm, | ||
844 | "RX: received chub update mcc cmd (mcc '%s' src %d)\n", | ||
845 | mcc, src); | ||
846 | regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL); | ||
847 | if (IS_ERR_OR_NULL(regd)) | ||
848 | return 0; | ||
849 | |||
850 | regulatory_set_wiphy_regd(mvm->hw->wiphy, regd); | ||
851 | kfree(regd); | ||
852 | |||
853 | return 0; | ||
854 | } | ||