summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/nvm.c')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/nvm.c249
1 files changed, 126 insertions, 123 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
index 6fc5cc1f2b5b..953218f1e025 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
@@ -1,73 +1,15 @@
-/******************************************************************************
- *
- * This file is provided under a dual BSD/GPLv2 license. When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright(c) 2018 Intel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
- * Contact Information:
- * Intel Linux Wireless <linuxwifi@intel.com>
- * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
- *
- * BSD LICENSE
- *
- * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
- * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
- * Copyright(c) 2016 - 2017 Intel Deutschland GmbH
- * Copyright(c) 2018 Intel Corporation
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name Intel Corporation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+/*
+ * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
+ * Copyright (C) 2016-2017 Intel Deutschland GmbH
+ */
#include <linux/firmware.h>
#include <linux/rtnetlink.h>
#include "iwl-trans.h"
#include "iwl-csr.h"
#include "mvm.h"
-#include "iwl-eeprom-parse.h"
-#include "iwl-eeprom-read.h"
+#include "iwl-nvm-utils.h"
#include "iwl-nvm-parse.h"
#include "iwl-prph.h"
#include "fw/acpi.h"
@@ -178,8 +120,8 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
} else {
IWL_DEBUG_EEPROM(mvm->trans->dev,
"NVM access command failed with status %d (device: %s)\n",
- ret, mvm->cfg->name);
- ret = -EIO;
+ ret, mvm->trans->info.name);
+ ret = -ENODATA;
}
goto exit;
}
@@ -249,7 +191,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
while (ret == length) {
/* Check no memory assumptions fail and cause an overflow */
if ((size_read + offset + length) >
- mvm->cfg->base_params->eeprom_size) {
+ mvm->trans->mac_cfg->base->eeprom_size) {
IWL_ERR(mvm, "EEPROM size is too small for NVM\n");
return -ENOBUFS;
}
@@ -264,7 +206,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
offset += ret;
}
- iwl_nvm_fixups(mvm->trans->hw_id, section, data, offset);
+ iwl_nvm_fixups(mvm->trans->info.hw_id, section, data, offset);
IWL_DEBUG_EEPROM(mvm->trans->dev,
"NVM section %d read completed\n", section);
@@ -277,13 +219,14 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
struct iwl_nvm_section *sections = mvm->nvm_sections;
const __be16 *hw;
const __le16 *sw, *calib, *regulatory, *mac_override, *phy_sku;
- bool lar_enabled;
+ u8 tx_ant = mvm->fw->valid_tx_ant;
+ u8 rx_ant = mvm->fw->valid_rx_ant;
int regulatory_type;
/* Checking for required sections */
- if (mvm->trans->cfg->nvm_type != IWL_NVM_EXT) {
+ if (mvm->trans->cfg->nvm_type == IWL_NVM) {
if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
- !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
+ !mvm->nvm_sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data) {
IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n");
return NULL;
}
@@ -301,7 +244,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
return NULL;
}
/* MAC_OVERRIDE or at least HW section must exist */
- if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
+ if (!mvm->nvm_sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data &&
!mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
IWL_ERR(mvm,
"Can't parse mac_address, empty sections\n");
@@ -309,14 +252,15 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
}
/* PHY_SKU section is mandatory in B0 */
- if (!mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
+ if (mvm->trans->cfg->nvm_type == IWL_NVM_EXT &&
+ !mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
IWL_ERR(mvm,
"Can't parse phy_sku in B0, empty sections\n");
return NULL;
}
}
- hw = (const __be16 *)sections[mvm->cfg->nvm_hw_section_num].data;
+ hw = (const __be16 *)sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data;
sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
mac_override =
@@ -327,14 +271,15 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm)
(const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY_SDP].data :
(const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data;
- lar_enabled = !iwlwifi_mod_params.lar_disable &&
- fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
+ if (mvm->set_tx_ant)
+ tx_ant &= mvm->set_tx_ant;
+
+ if (mvm->set_rx_ant)
+ rx_ant &= mvm->set_rx_ant;
- return iwl_parse_nvm_data(mvm->trans, mvm->cfg, hw, sw, calib,
+ return iwl_parse_nvm_data(mvm->trans, mvm->cfg, mvm->fw, hw, sw, calib,
regulatory, mac_override, phy_sku,
- mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant,
- lar_enabled);
+ tx_ant, rx_ant);
}
/* Loads the NVM data stored in mvm->nvm_sections into the NIC */
@@ -363,16 +308,15 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
int ret, section;
u32 size_read = 0;
u8 *nvm_buffer, *temp;
- const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
- if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
+ if (WARN_ON_ONCE(mvm->trans->mac_cfg->base->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
return -EINVAL;
/* load NVM values from nic */
/* Read From FW NVM */
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
- nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size,
+ nvm_buffer = kmalloc(mvm->trans->mac_cfg->base->eeprom_size,
GFP_KERNEL);
if (!nvm_buffer)
return -ENOMEM;
@@ -380,8 +324,12 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
/* we override the constness for initial read */
ret = iwl_nvm_read_section(mvm, section, nvm_buffer,
size_read);
- if (ret < 0)
+ if (ret == -ENODATA) {
+ ret = 0;
continue;
+ }
+ if (ret < 0)
+ break;
size_read += ret;
temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
if (!temp) {
@@ -389,7 +337,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
break;
}
- iwl_nvm_fixups(mvm->trans->hw_id, section, temp, ret);
+ iwl_nvm_fixups(mvm->trans->info.hw_id, section, temp, ret);
mvm->nvm_sections[section].data = temp;
mvm->nvm_sections[section].length = ret;
@@ -412,8 +360,13 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
mvm->nvm_phy_sku_blob.data = temp;
mvm->nvm_phy_sku_blob.size = ret;
break;
+ case NVM_SECTION_TYPE_REGULATORY_SDP:
+ case NVM_SECTION_TYPE_REGULATORY:
+ mvm->nvm_reg_blob.data = temp;
+ mvm->nvm_reg_blob.size = ret;
+ break;
default:
- if (section == mvm->cfg->nvm_hw_section_num) {
+ if (section == mvm->trans->mac_cfg->base->nvm_hw_section_num) {
mvm->nvm_hw_blob.data = temp;
mvm->nvm_hw_blob.size = ret;
break;
@@ -430,21 +383,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
/* read External NVM file from the mod param */
ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name,
mvm->nvm_sections);
- if (ret) {
- mvm->nvm_file_name = nvm_file_C;
-
- if ((ret == -EFAULT || ret == -ENOENT) &&
- mvm->nvm_file_name) {
- /* in case nvm file was failed try again */
- ret = iwl_read_external_nvm(mvm->trans,
- mvm->nvm_file_name,
- mvm->nvm_sections);
- if (ret)
- return ret;
- } else {
- return ret;
- }
- }
+ if (ret)
+ return ret;
}
/* parse the relevant nvm sections */
@@ -454,10 +394,10 @@ int iwl_nvm_init(struct iwl_mvm *mvm)
IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n",
mvm->nvm_data->nvm_version);
- return 0;
+ return ret < 0 ? ret : 0;
}
-struct iwl_mcc_update_resp *
+struct iwl_mcc_update_resp_v8 *
iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
enum iwl_mcc_source src_id)
{
@@ -465,15 +405,15 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
.mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
.source_id = (u8)src_id,
};
- struct iwl_mcc_update_resp *resp_cp;
+ struct iwl_mcc_update_resp_v8 *resp_cp;
struct iwl_rx_packet *pkt;
struct iwl_host_cmd cmd = {
.id = MCC_UPDATE_CMD,
- .flags = CMD_WANT_SKB,
+ .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
.data = { &mcc_update_cmd },
};
- int ret;
+ int ret, resp_ver;
u32 status;
int resp_len, n_channels;
u16 mcc;
@@ -492,25 +432,70 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
pkt = cmd.resp_pkt;
+ resp_ver = iwl_fw_lookup_notif_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP,
+ MCC_UPDATE_CMD, 0);
+
/* Extract MCC response */
- if (fw_has_capa(&mvm->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT)) {
- struct iwl_mcc_update_resp *mcc_resp = (void *)pkt->data;
-
- n_channels = __le32_to_cpu(mcc_resp->n_channels);
- resp_len = sizeof(struct iwl_mcc_update_resp) +
- n_channels * sizeof(__le32);
- resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL);
+ if (resp_ver >= 8) {
+ struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (void *)pkt->data;
+
+ n_channels = __le32_to_cpu(mcc_resp_v8->n_channels);
+ if (iwl_rx_packet_payload_len(pkt) !=
+ struct_size(mcc_resp_v8, channels, n_channels)) {
+ resp_cp = ERR_PTR(-EINVAL);
+ goto exit;
+ }
+ resp_len = struct_size(resp_cp, channels, n_channels);
+ resp_cp = kzalloc(resp_len, GFP_KERNEL);
+ if (!resp_cp) {
+ resp_cp = ERR_PTR(-ENOMEM);
+ goto exit;
+ }
+ resp_cp->status = mcc_resp_v8->status;
+ resp_cp->mcc = mcc_resp_v8->mcc;
+ resp_cp->cap = mcc_resp_v8->cap;
+ resp_cp->source_id = mcc_resp_v8->source_id;
+ resp_cp->time = mcc_resp_v8->time;
+ resp_cp->geo_info = mcc_resp_v8->geo_info;
+ resp_cp->n_channels = mcc_resp_v8->n_channels;
+ memcpy(resp_cp->channels, mcc_resp_v8->channels,
+ n_channels * sizeof(__le32));
+ } else if (fw_has_capa(&mvm->fw->ucode_capa,
+ IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT)) {
+ struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (void *)pkt->data;
+
+ n_channels = __le32_to_cpu(mcc_resp_v4->n_channels);
+ if (iwl_rx_packet_payload_len(pkt) !=
+ struct_size(mcc_resp_v4, channels, n_channels)) {
+ resp_cp = ERR_PTR(-EINVAL);
+ goto exit;
+ }
+ resp_len = struct_size(resp_cp, channels, n_channels);
+ resp_cp = kzalloc(resp_len, GFP_KERNEL);
if (!resp_cp) {
resp_cp = ERR_PTR(-ENOMEM);
goto exit;
}
+
+ resp_cp->status = mcc_resp_v4->status;
+ resp_cp->mcc = mcc_resp_v4->mcc;
+ resp_cp->cap = cpu_to_le32(le16_to_cpu(mcc_resp_v4->cap));
+ resp_cp->source_id = mcc_resp_v4->source_id;
+ resp_cp->time = mcc_resp_v4->time;
+ resp_cp->geo_info = mcc_resp_v4->geo_info;
+ resp_cp->n_channels = mcc_resp_v4->n_channels;
+ memcpy(resp_cp->channels, mcc_resp_v4->channels,
+ n_channels * sizeof(__le32));
} else {
struct iwl_mcc_update_resp_v3 *mcc_resp_v3 = (void *)pkt->data;
n_channels = __le32_to_cpu(mcc_resp_v3->n_channels);
- resp_len = sizeof(struct iwl_mcc_update_resp) +
- n_channels * sizeof(__le32);
+ if (iwl_rx_packet_payload_len(pkt) !=
+ struct_size(mcc_resp_v3, channels, n_channels)) {
+ resp_cp = ERR_PTR(-EINVAL);
+ goto exit;
+ }
+ resp_len = struct_size(resp_cp, channels, n_channels);
resp_cp = kzalloc(resp_len, GFP_KERNEL);
if (!resp_cp) {
resp_cp = ERR_PTR(-ENOMEM);
@@ -519,7 +504,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
resp_cp->status = mcc_resp_v3->status;
resp_cp->mcc = mcc_resp_v3->mcc;
- resp_cp->cap = cpu_to_le16(mcc_resp_v3->cap);
+ resp_cp->cap = cpu_to_le32(mcc_resp_v3->cap);
resp_cp->source_id = mcc_resp_v3->source_id;
resp_cp->time = mcc_resp_v3->time;
resp_cp->geo_info = mcc_resp_v3->geo_info;
@@ -555,7 +540,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
struct ieee80211_regdomain *regd;
char mcc[3];
- if (mvm->cfg->nvm_type == IWL_NVM_EXT) {
+ if (mvm->trans->cfg->nvm_type == IWL_NVM_EXT) {
tlv_lar = fw_has_capa(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
nvm_lar = mvm->nvm_data->lar_enabled;
@@ -573,7 +558,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
* try to replay the last set MCC to FW. If it doesn't exist,
* queue an update to cfg80211 to retrieve the default alpha2 from FW.
*/
- retval = iwl_mvm_init_fw_regd(mvm);
+ retval = iwl_mvm_init_fw_regd(mvm, true);
if (retval != -ENOENT)
return retval;
@@ -590,7 +575,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
return -EIO;
if (iwl_mvm_is_wifi_mcc_supported(mvm) &&
- !iwl_acpi_get_mcc(mvm->dev, mcc)) {
+ !iwl_bios_get_mcc(&mvm->fwrt, mcc)) {
kfree(regd);
regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc,
MCC_SOURCE_BIOS, NULL);
@@ -598,7 +583,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
return -EIO;
}
- retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
+ retval = regulatory_set_wiphy_regd_sync(mvm->hw->wiphy, regd);
kfree(regd);
return retval;
}
@@ -611,6 +596,8 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
enum iwl_mcc_source src;
char mcc[3];
struct ieee80211_regdomain *regd;
+ int wgds_tbl_idx;
+ bool changed = false;
lockdep_assert_held(&mvm->mutex);
@@ -630,10 +617,26 @@ void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
IWL_DEBUG_LAR(mvm,
"RX: received chub update mcc cmd (mcc '%s' src %d)\n",
mcc, src);
- regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL);
+ regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, &changed);
if (IS_ERR_OR_NULL(regd))
return;
+ if (!changed) {
+ IWL_DEBUG_LAR(mvm, "RX: No change in the regulatory data\n");
+ goto out;
+ }
+
+ wgds_tbl_idx = iwl_mvm_get_sar_geo_profile(mvm);
+ if (wgds_tbl_idx < 1)
+ IWL_DEBUG_INFO(mvm,
+ "SAR WGDS is disabled or error received (%d)\n",
+ wgds_tbl_idx);
+ else
+ IWL_DEBUG_INFO(mvm, "SAR WGDS: geo profile %d is configured\n",
+ wgds_tbl_idx);
+
regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
+
+out:
kfree(regd);
}