iwlwifi: mvm: support getting nvm data from firmware
authorSara Sharon <sara.sharon@intel.com>
Sun, 8 Jan 2017 14:46:14 +0000 (16:46 +0200)
committerLuca Coelho <luciano.coelho@intel.com>
Fri, 2 Jun 2017 11:07:36 +0000 (14:07 +0300)
This API replaces the complex NVM parsing of the iwlwifi module.
Instead, we get all needed data from firmware.

Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
drivers/net/wireless/intel/iwlwifi/mvm/fw.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c

index f545c5f9e4e38e8c1d565885b4b60222589b6230..0996127897c15e5a1977a08f9287bcb54784c490 100644 (file)
@@ -345,6 +345,7 @@ enum iwl_prot_offload_subcmd_ids {
 
 enum iwl_regulatory_and_nvm_subcmd_ids {
        NVM_ACCESS_COMPLETE = 0x0,
+       NVM_GET_INFO = 0x2,
 };
 
 enum iwl_debug_cmds {
@@ -2259,4 +2260,80 @@ struct iwl_init_extended_cfg_cmd {
        __le32 init_flags;
 } __packed; /* INIT_EXTENDED_CFG_CMD_API_S_VER_1 */
 
+/*
+ * struct iwl_nvm_get_info - request to get NVM data
+ */
+struct iwl_nvm_get_info {
+       __le32 reserved;
+} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_S_VER_1 */
+
+/**
+ * struct iwl_nvm_get_info_general - general NVM data
+ * @flags: 1 - empty, 0 - valid
+ * @nvm_version: nvm version
+ * @board_type: board type
+ */
+struct iwl_nvm_get_info_general {
+       __le32 flags;
+       __le16 nvm_version;
+       u8 board_type;
+       u8 reserved;
+} __packed; /* GRP_REGULATORY_NVM_GET_INFO_GENERAL_S_VER_1 */
+
+/**
+ * struct iwl_nvm_get_info_sku - mac information
+ * @enable_24g: band 2.4G enabled
+ * @enable_5g: band 5G enabled
+ * @enable_11n: 11n enabled
+ * @enable_11ac: 11ac enabled
+ * @mimo_disable: MIMO enabled
+ * @ext_crypto: Extended crypto enabled
+ */
+struct iwl_nvm_get_info_sku {
+       __le32 enable_24g;
+       __le32 enable_5g;
+       __le32 enable_11n;
+       __le32 enable_11ac;
+       __le32 mimo_disable;
+       __le32 ext_crypto;
+} __packed; /* GRP_REGULATORY_NVM_GET_INFO_MAC_SKU_SECTION_S_VER_1 */
+
+/**
+ * struct iwl_nvm_get_info_phy - phy information
+ * @tx_chains: BIT 0 chain A, BIT 1 chain B
+ * @rx_chains: BIT 0 chain A, BIT 1 chain B
+ */
+struct iwl_nvm_get_info_phy {
+       __le32 tx_chains;
+       __le32 rx_chains;
+} __packed; /* GRP_REGULATORY_NVM_GET_INFO_PHY_SKU_SECTION_S_VER_1 */
+
+#define IWL_NUM_CHANNELS (51)
+
+/**
+ * struct iwl_nvm_get_info_regulatory - regulatory information
+ * @lar_enabled: is LAR enabled
+ * @channel_profile: regulatory data of this channel
+ * @regulatory: regulatory data, see &enum iwl_nvm_channel_flags for data
+ */
+struct iwl_nvm_get_info_regulatory {
+       __le32 lar_enabled;
+       __le16 channel_profile[IWL_NUM_CHANNELS];
+       __le16 reserved;
+} __packed; /* GRP_REGULATORY_NVM_GET_INFO_REGULATORY_S_VER_1 */
+
+/**
+ * struct iwl_nvm_get_info_rsp - response to get NVM data
+ * @general: general NVM data
+ * @mac_sku: data relating to MAC sku
+ * @phy_sku: data relating to PHY sku
+ * @regulatory: regulatory data
+ */
+struct iwl_nvm_get_info_rsp {
+       struct iwl_nvm_get_info_general general;
+       struct iwl_nvm_get_info_sku mac_sku;
+       struct iwl_nvm_get_info_phy phy_sku;
+       struct iwl_nvm_get_info_regulatory regulatory;
+} __packed; /* GRP_REGULATORY_NVM_GET_INFO_CMD_RSP_S_VER_1 */
+
 #endif /* __fw_api_h__ */
index e6c9528eeedac795ddf41e330470969ec2ecd142..3d68595243fce0804d57fb339722471499227d80 100644 (file)
@@ -738,23 +738,11 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
                goto error;
        }
 
-       /* Read the NVM only at driver load time, no need to do this twice */
-       if (read_nvm) {
-               /* Read nvm */
-               ret = iwl_nvm_init(mvm, true);
-               if (ret) {
-                       IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
-                       goto error;
-               }
-       }
-
-       /* In case we read the NVM from external file, load it to the NIC */
-       if (mvm->nvm_file_name)
+       /* Load NVM to NIC if needed */
+       if (mvm->nvm_file_name) {
+               iwl_mvm_read_external_nvm(mvm);
                iwl_mvm_load_nvm_to_nic(mvm);
-
-       ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans);
-       if (WARN_ON(ret))
-               goto error;
+       }
 
        ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP,
                                                NVM_ACCESS_COMPLETE), 0,
@@ -766,8 +754,21 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm)
        }
 
        /* We wait for the INIT complete notification */
-       return iwl_wait_notification(&mvm->notif_wait, &init_wait,
-                                    MVM_UCODE_ALIVE_TIMEOUT);
+       ret = iwl_wait_notification(&mvm->notif_wait, &init_wait,
+                                   MVM_UCODE_ALIVE_TIMEOUT);
+       if (ret)
+               return ret;
+
+       /* Read the NVM only at driver load time, no need to do this twice */
+       if (read_nvm) {
+               ret = iwl_mvm_nvm_get_from_fw(mvm);
+               if (ret) {
+                       IWL_ERR(mvm, "Failed to read NVM: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
 
 error:
        iwl_remove_notification(&mvm->notif_wait, &init_wait);
index 53fbdb9136ab57791723d007fe35207ca2739880..aa725bbb90ec6ea4601a8c763776af2993a612d3 100644 (file)
@@ -1383,7 +1383,9 @@ void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm);
 
 /* NVM */
 int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
+int iwl_mvm_nvm_get_from_fw(struct iwl_mvm *mvm);
 int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm);
+int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm);
 
 static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm)
 {
index 283c41df622cf1bb7693ef1b223ae5822074e59a..67a6c9c9af27693005ab56de2b9cbb449b69c448 100644 (file)
@@ -374,7 +374,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
  *
  * 4. save as "iNVM_xxx.bin" under /lib/firmware
  */
-static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
+int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
 {
        int ret, section_size;
        u16 section_id;
@@ -551,6 +551,94 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
        return ret;
 }
 
+int iwl_mvm_nvm_get_from_fw(struct iwl_mvm *mvm)
+{
+       struct iwl_nvm_get_info cmd = {};
+       struct iwl_nvm_get_info_rsp *rsp;
+       struct iwl_trans *trans = mvm->trans;
+       struct iwl_host_cmd hcmd = {
+               .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
+               .data = { &cmd, },
+               .len = { sizeof(cmd) },
+               .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, NVM_GET_INFO)
+       };
+       int  ret;
+       bool lar_fw_supported = !iwlwifi_mod_params.lar_disable &&
+                               fw_has_capa(&mvm->fw->ucode_capa,
+                                           IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
+
+       lockdep_assert_held(&mvm->mutex);
+
+       ret = iwl_mvm_send_cmd(mvm, &hcmd);
+       if (ret)
+               return ret;
+
+       if (WARN(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp),
+                "Invalid payload len in NVM response from FW %d",
+                iwl_rx_packet_payload_len(hcmd.resp_pkt))) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       rsp = (void *)hcmd.resp_pkt->data;
+       if (le32_to_cpu(rsp->general.flags)) {
+               IWL_ERR(mvm, "Invalid NVM data from FW\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       mvm->nvm_data = kzalloc(sizeof(*mvm->nvm_data) +
+                               sizeof(struct ieee80211_channel) *
+                               IWL_NUM_CHANNELS, GFP_KERNEL);
+       if (!mvm->nvm_data) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       iwl_set_hw_address_from_csr(trans, mvm->nvm_data);
+       /* TODO: if platform NVM has MAC address - override it here */
+
+       if (!is_valid_ether_addr(mvm->nvm_data->hw_addr)) {
+               IWL_ERR(trans, "no valid mac address was found\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* Initialize general data */
+       mvm->nvm_data->nvm_version = le16_to_cpu(rsp->general.nvm_version);
+
+       /* Initialize MAC sku data */
+       mvm->nvm_data->sku_cap_11ac_enable =
+               le32_to_cpu(rsp->mac_sku.enable_11ac);
+       mvm->nvm_data->sku_cap_11n_enable =
+               le32_to_cpu(rsp->mac_sku.enable_11n);
+       mvm->nvm_data->sku_cap_band_24GHz_enable =
+               le32_to_cpu(rsp->mac_sku.enable_24g);
+       mvm->nvm_data->sku_cap_band_52GHz_enable =
+               le32_to_cpu(rsp->mac_sku.enable_5g);
+       mvm->nvm_data->sku_cap_mimo_disabled =
+               le32_to_cpu(rsp->mac_sku.mimo_disable);
+
+       /* Initialize PHY sku data */
+       mvm->nvm_data->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains);
+       mvm->nvm_data->valid_rx_ant = (u8)le32_to_cpu(rsp->phy_sku.rx_chains);
+
+       /* Initialize regulatory data */
+       mvm->nvm_data->lar_enabled =
+               le32_to_cpu(rsp->regulatory.lar_enabled) && lar_fw_supported;
+
+       iwl_init_sbands(trans->dev, trans->cfg, mvm->nvm_data,
+                       rsp->regulatory.channel_profile,
+                       mvm->nvm_data->valid_tx_ant & mvm->fw->valid_tx_ant,
+                       mvm->nvm_data->valid_rx_ant & mvm->fw->valid_rx_ant,
+                       rsp->regulatory.lar_enabled && lar_fw_supported);
+
+       ret = 0;
+out:
+       iwl_free_resp(&hcmd);
+       return ret;
+}
+
 int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
 {
        int ret, section;
index 9ffff6ed813386418800cf38906b91989c5b6f52..607cc83908b5815bb38fefdee4c8825f99232ed3 100644 (file)
@@ -483,6 +483,7 @@ static const struct iwl_hcmd_names iwl_mvm_prot_offload_names[] = {
  */
 static const struct iwl_hcmd_names iwl_mvm_regulatory_and_nvm_names[] = {
        HCMD_NAME(NVM_ACCESS_COMPLETE),
+       HCMD_NAME(NVM_GET_INFO),
 };
 
 static const struct iwl_hcmd_arr iwl_mvm_groups[] = {
index 9fb46a6f47cf416e55d4416b228be50cdd460e4e..9c9bfbbabdf11ee597dfa7a3614e59d2e49236fe 100644 (file)
@@ -906,7 +906,7 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
 
        if (WARN_ON(iwl_rx_packet_payload_len(hcmd.resp_pkt) != sizeof(*rsp))) {
                ret = -EINVAL;
-               goto error;
+               goto error_free_resp;
        }
 
        rsp = (void *)hcmd.resp_pkt->data;
@@ -915,13 +915,13 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
        if (qid > ARRAY_SIZE(trans_pcie->txq)) {
                WARN_ONCE(1, "queue index %d unsupported", qid);
                ret = -EIO;
-               goto error;
+               goto error_free_resp;
        }
 
        if (test_and_set_bit(qid, trans_pcie->queue_used)) {
                WARN_ONCE(1, "queue %d already used", qid);
                ret = -EIO;
-               goto error;
+               goto error_free_resp;
        }
 
        txq->id = qid;
@@ -934,8 +934,11 @@ int iwl_trans_pcie_dyn_txq_alloc(struct iwl_trans *trans,
                           (txq->write_ptr) | (qid << 16));
        IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d\n", qid);
 
+       iwl_free_resp(&hcmd);
        return qid;
 
+error_free_resp:
+       iwl_free_resp(&hcmd);
 error:
        iwl_pcie_gen2_txq_free_memory(trans, txq);
        return ret;