From 1c055e55a242a33e574e48be530e06770a210dcd Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 19 Feb 2024 03:26:26 +0000 Subject: [PATCH] add r8169 read mac form eeprom --- kernel/drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 1165 ++++++++++++++++++++++++++++++++++++++------------------- 1 files changed, 777 insertions(+), 388 deletions(-) diff --git a/kernel/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/kernel/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index c7e2b88..54b28f0 100644 --- a/kernel/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/kernel/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -5,10 +5,9 @@ * * 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 + * Copyright(c) 2012 - 2014, 2018 - 2020 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 @@ -19,11 +18,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * * The full GNU General Public License is included in this distribution * in the file called COPYING. * @@ -33,10 +27,9 @@ * * 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 + * Copyright(c) 2012 - 2014, 2018 - 2020 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -77,6 +70,7 @@ #include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */ #include "iwl-prph.h" #include "fw/acpi.h" +#include "fw/pnvm.h" #include "mvm.h" #include "fw/dbg.h" @@ -84,8 +78,8 @@ #include "iwl-modparams.h" #include "iwl-nvm-parse.h" -#define MVM_UCODE_ALIVE_TIMEOUT HZ -#define MVM_UCODE_CALIB_TIMEOUT (2*HZ) +#define MVM_UCODE_ALIVE_TIMEOUT (HZ) +#define MVM_UCODE_CALIB_TIMEOUT (2 * HZ) #define UCODE_VALID_OK cpu_to_le32(0x1) @@ -139,7 +133,14 @@ .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; - /* Do not configure default queue, it is configured via context info */ + /* + * The default queue is configured via context info, so if we + * have a single queue, there's nothing to do here. + */ + if (mvm->trans->num_rx_queues == 1) + return 0; + + /* skip the default queue */ num_queues = mvm->trans->num_rx_queues - 1; size = struct_size(cmd, data, num_queues); @@ -217,51 +218,82 @@ struct iwl_mvm *mvm = container_of(notif_wait, struct iwl_mvm, notif_wait); struct iwl_mvm_alive_data *alive_data = data; - struct mvm_alive_resp_v3 *palive3; - struct mvm_alive_resp *palive; struct iwl_umac_alive *umac; struct iwl_lmac_alive *lmac1; struct iwl_lmac_alive *lmac2 = NULL; u16 status; - u32 umac_error_event_table; + u32 lmac_error_event_table, umac_error_table; - if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { + /* + * For v5 and above, we can check the version, for older + * versions we need to check the size. + */ + if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + UCODE_ALIVE_NTFY, 0) == 5) { + struct iwl_alive_ntf_v5 *palive; + palive = (void *)pkt->data; umac = &palive->umac_data; lmac1 = &palive->lmac_data[0]; lmac2 = &palive->lmac_data[1]; status = le16_to_cpu(palive->status); - } else { + + mvm->trans->sku_id[0] = le32_to_cpu(palive->sku_id.data[0]); + mvm->trans->sku_id[1] = le32_to_cpu(palive->sku_id.data[1]); + mvm->trans->sku_id[2] = le32_to_cpu(palive->sku_id.data[2]); + + IWL_DEBUG_FW(mvm, "Got sku_id: 0x0%x 0x0%x 0x0%x\n", + mvm->trans->sku_id[0], + mvm->trans->sku_id[1], + mvm->trans->sku_id[2]); + } else if (iwl_rx_packet_payload_len(pkt) == sizeof(struct iwl_alive_ntf_v4)) { + struct iwl_alive_ntf_v4 *palive; + + palive = (void *)pkt->data; + umac = &palive->umac_data; + lmac1 = &palive->lmac_data[0]; + lmac2 = &palive->lmac_data[1]; + status = le16_to_cpu(palive->status); + } else if (iwl_rx_packet_payload_len(pkt) == + sizeof(struct iwl_alive_ntf_v3)) { + struct iwl_alive_ntf_v3 *palive3; + palive3 = (void *)pkt->data; umac = &palive3->umac_data; lmac1 = &palive3->lmac_data; status = le16_to_cpu(palive3->status); - } - - mvm->error_event_table[0] = le32_to_cpu(lmac1->error_event_table_ptr); - if (lmac2) - mvm->error_event_table[1] = - le32_to_cpu(lmac2->error_event_table_ptr); - mvm->log_event_table = le32_to_cpu(lmac1->log_event_table_ptr); - - umac_error_event_table = le32_to_cpu(umac->error_info_addr); - - if (!umac_error_event_table) { - mvm->support_umac_log = false; - } else if (umac_error_event_table >= - mvm->trans->cfg->min_umac_error_event_table) { - mvm->support_umac_log = true; - mvm->umac_error_event_table = umac_error_event_table; } else { - IWL_ERR(mvm, - "Not valid error log pointer 0x%08X for %s uCode\n", - mvm->umac_error_event_table, - (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) ? - "Init" : "RT"); - mvm->support_umac_log = false; + WARN(1, "unsupported alive notification (size %d)\n", + iwl_rx_packet_payload_len(pkt)); + /* get timeout later */ + return false; } - alive_data->scd_base_addr = le32_to_cpu(lmac1->scd_base_ptr); + lmac_error_event_table = + le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr); + iwl_fw_lmac1_set_alive_err_table(mvm->trans, lmac_error_event_table); + + if (lmac2) + mvm->trans->dbg.lmac_error_event_table[1] = + le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr); + + umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr); + + if (umac_error_table) { + if (umac_error_table >= + mvm->trans->cfg->min_umac_error_event_table) { + iwl_fw_umac_set_alive_err_table(mvm->trans, + umac_error_table); + } else { + IWL_ERR(mvm, + "Not valid error log pointer 0x%08X for %s uCode\n", + umac_error_table, + (mvm->fwrt.cur_fw_img == IWL_UCODE_INIT) ? + "Init" : "RT"); + } + } + + alive_data->scd_base_addr = le32_to_cpu(lmac1->dbg_ptrs.scd_base_ptr); alive_data->valid = status == IWL_ALIVE_STATUS_OK; IWL_DEBUG_FW(mvm, @@ -275,6 +307,8 @@ "UMAC version: Major - 0x%x, Minor - 0x%x\n", le32_to_cpu(umac->umac_major), le32_to_cpu(umac->umac_minor)); + + iwl_fwrt_update_fw_versions(&mvm->fwrt, lmac1, umac); return true; } @@ -306,11 +340,13 @@ enum iwl_ucode_type ucode_type) { struct iwl_notification_wait alive_wait; - struct iwl_mvm_alive_data alive_data; + struct iwl_mvm_alive_data alive_data = {}; const struct fw_img *fw; - int ret, i; + int ret; enum iwl_ucode_type old_type = mvm->fwrt.cur_fw_img; - static const u16 alive_cmd[] = { MVM_ALIVE }; + static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY }; + bool run_in_rfkill = + ucode_type == IWL_UCODE_INIT || iwl_mvm_has_unified_ucode(mvm); if (ucode_type == IWL_UCODE_REGULAR && iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) && @@ -328,7 +364,12 @@ alive_cmd, ARRAY_SIZE(alive_cmd), iwl_alive_fn, &alive_data); - ret = iwl_trans_start_fw(mvm->trans, fw, ucode_type == IWL_UCODE_INIT); + /* + * We want to load the INIT firmware even in RFKILL + * For the unified firmware case, the ucode_type is not + * INIT, but we still need to run it. + */ + ret = iwl_trans_start_fw(mvm->trans, fw, run_in_rfkill); if (ret) { iwl_fw_set_current_image(&mvm->fwrt, old_type); iwl_remove_notification(&mvm->notif_wait, &alive_wait); @@ -344,16 +385,35 @@ if (ret) { struct iwl_trans *trans = mvm->trans; - if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) + if (trans->trans_cfg->device_family >= + IWL_DEVICE_FAMILY_22000) { IWL_ERR(mvm, "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", - iwl_read_prph(trans, UMAG_SB_CPU_1_STATUS), - iwl_read_prph(trans, UMAG_SB_CPU_2_STATUS)); - else if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) + iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS), + iwl_read_umac_prph(trans, + UMAG_SB_CPU_2_STATUS)); + IWL_ERR(mvm, "UMAC PC: 0x%x\n", + iwl_read_umac_prph(trans, + UREG_UMAC_CURRENT_PC)); + IWL_ERR(mvm, "LMAC PC: 0x%x\n", + iwl_read_umac_prph(trans, + UREG_LMAC1_CURRENT_PC)); + if (iwl_mvm_is_cdb_supported(mvm)) + IWL_ERR(mvm, "LMAC2 PC: 0x%x\n", + iwl_read_umac_prph(trans, + UREG_LMAC2_CURRENT_PC)); + } else if (trans->trans_cfg->device_family >= + IWL_DEVICE_FAMILY_8000) { IWL_ERR(mvm, "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", iwl_read_prph(trans, SB_CPU_1_STATUS), iwl_read_prph(trans, SB_CPU_2_STATUS)); + } + + if (ret == -ETIMEDOUT) + iwl_fw_dbg_error_collect(&mvm->fwrt, + FW_DBG_TRIGGER_ALIVE_TIMEOUT); + iwl_fw_set_current_image(&mvm->fwrt, old_type); return ret; } @@ -362,6 +422,13 @@ IWL_ERR(mvm, "Loaded ucode is not valid!\n"); iwl_fw_set_current_image(&mvm->fwrt, old_type); return -EIO; + } + + ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait); + if (ret) { + IWL_ERR(mvm, "Timeout waiting for PNVM load!\n"); + iwl_fw_set_current_image(&mvm->fwrt, old_type); + return ret; } iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); @@ -376,12 +443,19 @@ */ memset(&mvm->queue_info, 0, sizeof(mvm->queue_info)); - mvm->queue_info[IWL_MVM_DQA_CMD_QUEUE].hw_queue_refcount = 1; - - for (i = 0; i < IEEE80211_MAX_QUEUES; i++) - atomic_set(&mvm->mac80211_queue_stop_count[i], 0); + /* + * Set a 'fake' TID for the command queue, since we use the + * hweight() of the tid_bitmap as a refcount now. Not that + * we ever even consider the command queue as one we might + * want to reuse, but be safe nevertheless. + */ + mvm->queue_info[IWL_MVM_DQA_CMD_QUEUE].tid_bitmap = + BIT(IWL_MAX_TID_COUNT + 2); set_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_fw_set_dbg_rec_on(&mvm->fwrt); +#endif return 0; } @@ -398,7 +472,12 @@ }; int ret; + if (mvm->trans->cfg->tx_with_siso_diversity) + init_cfg.init_flags |= cpu_to_le32(BIT(IWL_INIT_PHY)); + lockdep_assert_held(&mvm->mutex); + + mvm->rfkill_safe_init_done = false; iwl_init_notification_wait(&mvm->notif_wait, &init_wait, @@ -407,18 +486,23 @@ iwl_wait_init_complete, NULL); + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); + /* Will also start the device */ ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); goto error; } + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, + NULL); /* Send init config command to mark that we are sending NVM access * commands */ ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(SYSTEM_GROUP, - INIT_EXTENDED_CFG_CMD), 0, + INIT_EXTENDED_CFG_CMD), + CMD_SEND_IN_RFKILL, sizeof(init_cfg), &init_cfg); if (ret) { IWL_ERR(mvm, "Failed to run init config command: %d\n", @@ -442,7 +526,8 @@ } ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, - NVM_ACCESS_COMPLETE), 0, + NVM_ACCESS_COMPLETE), + CMD_SEND_IN_RFKILL, sizeof(nvm_complete), &nvm_complete); if (ret) { IWL_ERR(mvm, "Failed to run complete NVM access: %d\n", @@ -467,6 +552,8 @@ } } + mvm->rfkill_safe_init_done = true; + return 0; error: @@ -474,27 +561,90 @@ return ret; } +#ifdef CONFIG_ACPI +static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, + struct iwl_phy_specific_cfg *phy_filters) +{ + /* + * TODO: read specific phy config from BIOS + * ACPI table for this feature has not been defined yet, + * so for now we use hardcoded values. + */ + + if (IWL_MVM_PHY_FILTER_CHAIN_A) { + phy_filters->filter_cfg_chain_a = + cpu_to_le32(IWL_MVM_PHY_FILTER_CHAIN_A); + } + if (IWL_MVM_PHY_FILTER_CHAIN_B) { + phy_filters->filter_cfg_chain_b = + cpu_to_le32(IWL_MVM_PHY_FILTER_CHAIN_B); + } + if (IWL_MVM_PHY_FILTER_CHAIN_C) { + phy_filters->filter_cfg_chain_c = + cpu_to_le32(IWL_MVM_PHY_FILTER_CHAIN_C); + } + if (IWL_MVM_PHY_FILTER_CHAIN_D) { + phy_filters->filter_cfg_chain_d = + cpu_to_le32(IWL_MVM_PHY_FILTER_CHAIN_D); + } +} + +#else /* CONFIG_ACPI */ + +static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, + struct iwl_phy_specific_cfg *phy_filters) +{ +} +#endif /* CONFIG_ACPI */ + static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) { - struct iwl_phy_cfg_cmd phy_cfg_cmd; + struct iwl_phy_cfg_cmd_v3 phy_cfg_cmd; enum iwl_ucode_type ucode_type = mvm->fwrt.cur_fw_img; + struct iwl_phy_specific_cfg phy_filters = {}; + u8 cmd_ver; + size_t cmd_size; + + if (iwl_mvm_has_unified_ucode(mvm) && + !mvm->trans->cfg->tx_with_siso_diversity) + return 0; + + if (mvm->trans->cfg->tx_with_siso_diversity) { + /* + * TODO: currently we don't set the antenna but letting the NIC + * to decide which antenna to use. This should come from BIOS. + */ + phy_cfg_cmd.phy_cfg = + cpu_to_le32(FW_PHY_CFG_CHAIN_SAD_ENABLED); + } /* Set parameters */ phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); /* set flags extra PHY configuration flags from the device's cfg */ - phy_cfg_cmd.phy_cfg |= cpu_to_le32(mvm->cfg->extra_phy_cfg_flags); + phy_cfg_cmd.phy_cfg |= + cpu_to_le32(mvm->trans->trans_cfg->extra_phy_cfg_flags); phy_cfg_cmd.calib_control.event_trigger = mvm->fw->default_calib[ucode_type].event_trigger; phy_cfg_cmd.calib_control.flow_trigger = mvm->fw->default_calib[ucode_type].flow_trigger; + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, IWL_ALWAYS_LONG_GROUP, + PHY_CONFIGURATION_CMD, + IWL_FW_CMD_VER_UNKNOWN); + if (cmd_ver == 3) { + iwl_mvm_phy_filter_init(mvm, &phy_filters); + memcpy(&phy_cfg_cmd.phy_specific_cfg, &phy_filters, + sizeof(struct iwl_phy_specific_cfg)); + } + IWL_DEBUG_INFO(mvm, "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); - + cmd_size = (cmd_ver == 3) ? sizeof(struct iwl_phy_cfg_cmd_v3) : + sizeof(struct iwl_phy_cfg_cmd_v1); return iwl_mvm_send_cmd_pdu(mvm, PHY_CONFIGURATION_CMD, 0, - sizeof(phy_cfg_cmd), &phy_cfg_cmd); + cmd_size, &phy_cfg_cmd); } int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) @@ -511,8 +661,7 @@ lockdep_assert_held(&mvm->mutex); - if (WARN_ON_ONCE(mvm->calibrating)) - return 0; + mvm->rfkill_safe_init_done = false; iwl_init_notification_wait(&mvm->notif_wait, &calib_wait, @@ -528,7 +677,7 @@ goto remove_notif; } - if (mvm->cfg->device_family < IWL_DEVICE_FAMILY_8000) { + if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) { ret = iwl_mvm_send_bt_init_conf(mvm); if (ret) goto remove_notif; @@ -561,7 +710,7 @@ goto remove_notif; } - mvm->calibrating = true; + mvm->rfkill_safe_init_done = true; /* Send TX valid antennas before triggering calibrations */ ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); @@ -597,7 +746,7 @@ remove_notif: iwl_remove_notification(&mvm->notif_wait, &calib_wait); out: - mvm->calibrating = false; + mvm->rfkill_safe_init_done = false; if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) { /* we want to debug INIT and we have no NVM - fake */ mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) + @@ -631,250 +780,85 @@ } #ifdef CONFIG_ACPI -static int iwl_mvm_sar_set_profile(struct iwl_mvm *mvm, - union acpi_object *table, - struct iwl_mvm_sar_profile *profile, - bool enabled) -{ - int i; - - profile->enabled = enabled; - - for (i = 0; i < ACPI_SAR_TABLE_SIZE; i++) { - if ((table[i].type != ACPI_TYPE_INTEGER) || - (table[i].integer.value > U8_MAX)) - return -EINVAL; - - profile->table[i] = table[i].integer.value; - } - - return 0; -} - -static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) -{ - union acpi_object *wifi_pkg, *table, *data; - bool enabled; - int ret; - - data = iwl_acpi_get_object(mvm->dev, ACPI_WRDS_METHOD); - if (IS_ERR(data)) - return PTR_ERR(data); - - wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_WRDS_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { - ret = PTR_ERR(wifi_pkg); - goto out_free; - } - - if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { - ret = -EINVAL; - goto out_free; - } - - enabled = !!(wifi_pkg->package.elements[1].integer.value); - - /* position of the actual table */ - table = &wifi_pkg->package.elements[2]; - - /* The profile from WRDS is officially profile 1, but goes - * into sar_profiles[0] (because we don't have a profile 0). - */ - ret = iwl_mvm_sar_set_profile(mvm, table, &mvm->sar_profiles[0], - enabled); -out_free: - kfree(data); - return ret; -} - -static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) -{ - union acpi_object *wifi_pkg, *data; - bool enabled; - int i, n_profiles, ret; - - data = iwl_acpi_get_object(mvm->dev, ACPI_EWRD_METHOD); - if (IS_ERR(data)) - return PTR_ERR(data); - - wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_EWRD_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { - ret = PTR_ERR(wifi_pkg); - goto out_free; - } - - if ((wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) || - (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER)) { - ret = -EINVAL; - goto out_free; - } - - enabled = !!(wifi_pkg->package.elements[1].integer.value); - n_profiles = wifi_pkg->package.elements[2].integer.value; - - /* - * Check the validity of n_profiles. The EWRD profiles start - * from index 1, so the maximum value allowed here is - * ACPI_SAR_PROFILES_NUM - 1. - */ - if (n_profiles <= 0 || n_profiles >= ACPI_SAR_PROFILE_NUM) { - ret = -EINVAL; - goto out_free; - } - - for (i = 0; i < n_profiles; i++) { - /* the tables start at element 3 */ - int pos = 3; - - /* The EWRD profiles officially go from 2 to 4, but we - * save them in sar_profiles[1-3] (because we don't - * have profile 0). So in the array we start from 1. - */ - ret = iwl_mvm_sar_set_profile(mvm, - &wifi_pkg->package.elements[pos], - &mvm->sar_profiles[i + 1], - enabled); - if (ret < 0) - break; - - /* go to the next table */ - pos += ACPI_SAR_TABLE_SIZE; - } - -out_free: - kfree(data); - return ret; -} - -static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm) -{ - union acpi_object *wifi_pkg, *data; - int i, j, ret; - int idx = 1; - - data = iwl_acpi_get_object(mvm->dev, ACPI_WGDS_METHOD); - if (IS_ERR(data)) - return PTR_ERR(data); - - wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, - ACPI_WGDS_WIFI_DATA_SIZE); - if (IS_ERR(wifi_pkg)) { - ret = PTR_ERR(wifi_pkg); - goto out_free; - } - - for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) { - for (j = 0; j < ACPI_GEO_TABLE_SIZE; j++) { - union acpi_object *entry; - - entry = &wifi_pkg->package.elements[idx++]; - if ((entry->type != ACPI_TYPE_INTEGER) || - (entry->integer.value > U8_MAX)) { - ret = -EINVAL; - goto out_free; - } - - mvm->geo_profiles[i].values[j] = entry->integer.value; - } - } - ret = 0; -out_free: - kfree(data); - return ret; -} - int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) { struct iwl_dev_tx_power_cmd cmd = { - .v3.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), + .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), }; - int i, j, idx; - int profs[ACPI_SAR_NUM_CHAIN_LIMITS] = { prof_a, prof_b }; - int len = sizeof(cmd); + __le16 *per_chain; + int ret; + u16 len = 0; + u32 n_subbands; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, + REDUCE_TX_POWER_CMD, + IWL_FW_CMD_VER_UNKNOWN); - BUILD_BUG_ON(ACPI_SAR_NUM_CHAIN_LIMITS < 2); - BUILD_BUG_ON(ACPI_SAR_NUM_CHAIN_LIMITS * ACPI_SAR_NUM_SUB_BANDS != - ACPI_SAR_TABLE_SIZE); - - if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) + if (cmd_ver == 6) { + len = sizeof(cmd.v6); + n_subbands = IWL_NUM_SUB_BANDS_V2; + per_chain = cmd.v6.per_chain[0][0]; + } else if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_REDUCE_TX_POWER)) { + len = sizeof(cmd.v5); + n_subbands = IWL_NUM_SUB_BANDS; + per_chain = cmd.v5.per_chain[0][0]; + } else if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_TX_POWER_ACK)) { + len = sizeof(cmd.v4); + n_subbands = IWL_NUM_SUB_BANDS; + per_chain = cmd.v4.per_chain[0][0]; + } else { len = sizeof(cmd.v3); - - for (i = 0; i < ACPI_SAR_NUM_CHAIN_LIMITS; i++) { - struct iwl_mvm_sar_profile *prof; - - /* don't allow SAR to be disabled (profile 0 means disable) */ - if (profs[i] == 0) - return -EPERM; - - /* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */ - if (profs[i] > ACPI_SAR_PROFILE_NUM) - return -EINVAL; - - /* profiles go from 1 to 4, so decrement to access the array */ - prof = &mvm->sar_profiles[profs[i] - 1]; - - /* if the profile is disabled, do nothing */ - if (!prof->enabled) { - IWL_DEBUG_RADIO(mvm, "SAR profile %d is disabled.\n", - profs[i]); - /* if one of the profiles is disabled, we fail all */ - return -ENOENT; - } - - IWL_DEBUG_RADIO(mvm, " Chain[%d]:\n", i); - for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS; j++) { - idx = (i * ACPI_SAR_NUM_SUB_BANDS) + j; - cmd.v3.per_chain_restriction[i][j] = - cpu_to_le16(prof->table[idx]); - IWL_DEBUG_RADIO(mvm, " Band[%d] = %d * .125dBm\n", - j, prof->table[idx]); - } + n_subbands = IWL_NUM_SUB_BANDS; + per_chain = cmd.v3.per_chain[0][0]; } + /* all structs have the same common part, add it */ + len += sizeof(cmd.common); + + ret = iwl_sar_select_profile(&mvm->fwrt, per_chain, ACPI_SAR_NUM_TABLES, + n_subbands, prof_a, prof_b); + + /* return on error or if the profile is disabled (positive number) */ + if (ret) + return ret; + IWL_DEBUG_RADIO(mvm, "Sending REDUCE_TX_POWER_CMD per chain\n"); - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0, len, &cmd); -} - -static bool iwl_mvm_sar_geo_support(struct iwl_mvm *mvm) -{ - /* - * The GEO_TX_POWER_LIMIT command is not supported on earlier - * firmware versions. Unfortunately, we don't have a TLV API - * flag to rely on, so rely on the major version which is in - * the first byte of ucode_ver. This was implemented - * initially on version 38 and then backported to 17. It was - * also backported to 29, but only for 7265D devices. The - * intention was to have it in 36 as well, but not all 8000 - * family got this feature enabled. The 8000 family is the - * only one using version 36, so skip this version entirely. - */ - return IWL_UCODE_SERIAL(mvm->fw->ucode_ver) >= 38 || - IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 17 || - (IWL_UCODE_SERIAL(mvm->fw->ucode_ver) == 29 && - ((mvm->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == - CSR_HW_REV_TYPE_7265D)); } int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) { + union iwl_geo_tx_power_profiles_cmd geo_tx_cmd; struct iwl_geo_tx_power_profiles_resp *resp; + u16 len; int ret; + struct iwl_host_cmd cmd; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, + GEO_TX_POWER_LIMIT, + IWL_FW_CMD_VER_UNKNOWN); - struct iwl_geo_tx_power_profiles_cmd geo_cmd = { - .ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE), - }; - struct iwl_host_cmd cmd = { - .id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT), - .len = { sizeof(geo_cmd), }, - .flags = CMD_WANT_SKB, - .data = { &geo_cmd }, - }; + /* the ops field is at the same spot for all versions, so set in v1 */ + geo_tx_cmd.v1.ops = + cpu_to_le32(IWL_PER_CHAIN_OFFSET_GET_CURRENT_TABLE); - if (!iwl_mvm_sar_geo_support(mvm)) + if (cmd_ver == 3) + len = sizeof(geo_tx_cmd.v3); + else if (fw_has_api(&mvm->fwrt.fw->ucode_capa, + IWL_UCODE_TLV_API_SAR_TABLE_VER)) + len = sizeof(geo_tx_cmd.v2); + else + len = sizeof(geo_tx_cmd.v1); + + if (!iwl_sar_geo_support(&mvm->fwrt)) return -EOPNOTSUPP; + + cmd = (struct iwl_host_cmd){ + .id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT), + .len = { len, }, + .flags = CMD_WANT_SKB, + .data = { &geo_tx_cmd }, + }; ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret) { @@ -884,10 +868,9 @@ resp = (void *)cmd.resp_pkt->data; ret = le32_to_cpu(resp->profile_idx); - if (WARN_ON(ret > ACPI_NUM_GEO_PROFILES)) { + + if (WARN_ON(ret > ACPI_NUM_GEO_PROFILES)) ret = -EIO; - IWL_WARN(mvm, "Invalid geographic profile idx (%d)\n", ret); - } iwl_free_resp(&cmd); return ret; @@ -895,63 +878,361 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) { - struct iwl_geo_tx_power_profiles_cmd cmd = { - .ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES), - }; - int ret, i, j; - u16 cmd_wide_id = WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT); + union iwl_geo_tx_power_profiles_cmd cmd; + u16 len; + u32 n_bands; + int ret; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, + GEO_TX_POWER_LIMIT, + IWL_FW_CMD_VER_UNKNOWN); - if (!iwl_mvm_sar_geo_support(mvm)) - return 0; + BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v1, ops) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, ops) || + offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, ops) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v3, ops)); + /* the ops field is at the same spot for all versions, so set in v1 */ + cmd.v1.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES); - ret = iwl_mvm_sar_get_wgds_table(mvm); - if (ret < 0) { - IWL_DEBUG_RADIO(mvm, - "Geo SAR BIOS table invalid or unavailable. (%d)\n", - ret); - /* we don't fail if the table is not available */ - return 0; + if (cmd_ver == 3) { + len = sizeof(cmd.v3); + n_bands = ARRAY_SIZE(cmd.v3.table[0]); + } else if (fw_has_api(&mvm->fwrt.fw->ucode_capa, + IWL_UCODE_TLV_API_SAR_TABLE_VER)) { + len = sizeof(cmd.v2); + n_bands = ARRAY_SIZE(cmd.v2.table[0]); + } else { + len = sizeof(cmd.v1); + n_bands = ARRAY_SIZE(cmd.v1.table[0]); } - IWL_DEBUG_RADIO(mvm, "Sending GEO_TX_POWER_LIMIT\n"); + BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v1, table) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, table) || + offsetof(struct iwl_geo_tx_power_profiles_cmd_v2, table) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v3, table)); + /* the table is at the same position for all versions, so set use v1 */ + ret = iwl_sar_geo_init(&mvm->fwrt, &cmd.v1.table[0][0], n_bands); - BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES * ACPI_WGDS_NUM_BANDS * - ACPI_WGDS_TABLE_SIZE + 1 != ACPI_WGDS_WIFI_DATA_SIZE); + /* + * It is a valid scenario to not support SAR, or miss wgds table, + * but in that case there is no need to send the command. + */ + if (ret) + return 0; - BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES > IWL_NUM_GEO_PROFILES); + /* + * Set the revision on versions that contain it. + * This must be done after calling iwl_sar_geo_init(). + */ + if (cmd_ver == 3) + cmd.v3.table_revision = cpu_to_le32(mvm->fwrt.geo_rev); + else if (fw_has_api(&mvm->fwrt.fw->ucode_capa, + IWL_UCODE_TLV_API_SAR_TABLE_VER)) + cmd.v2.table_revision = cpu_to_le32(mvm->fwrt.geo_rev); - for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) { - struct iwl_per_chain_offset *chain = - (struct iwl_per_chain_offset *)&cmd.table[i]; + return iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(PHY_OPS_GROUP, GEO_TX_POWER_LIMIT), + 0, len, &cmd); +} - for (j = 0; j < ACPI_WGDS_NUM_BANDS; j++) { - u8 *value; +static int iwl_mvm_get_ppag_table(struct iwl_mvm *mvm) +{ + union acpi_object *wifi_pkg, *data, *enabled; + int i, j, ret, tbl_rev, num_sub_bands; + int idx = 2; + s8 *gain; - value = &mvm->geo_profiles[i].values[j * - ACPI_GEO_PER_CHAIN_SIZE]; - chain[j].max_tx_power = cpu_to_le16(value[0]); - chain[j].chain_a = value[1]; - chain[j].chain_b = value[2]; - IWL_DEBUG_RADIO(mvm, - "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", - i, j, value[1], value[2], value[0]); + /* + * The 'enabled' field is the same in v1 and v2 so we can just + * use v1 to access it. + */ + mvm->fwrt.ppag_table.v1.enabled = cpu_to_le32(0); + data = iwl_acpi_get_object(mvm->dev, ACPI_PPAG_METHOD); + if (IS_ERR(data)) + return PTR_ERR(data); + + /* try to read ppag table revision 1 */ + wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, + ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); + if (!IS_ERR(wifi_pkg)) { + if (tbl_rev != 1) { + ret = -EINVAL; + goto out_free; + } + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = mvm->fwrt.ppag_table.v2.gain[0]; + mvm->fwrt.ppag_ver = 2; + IWL_DEBUG_RADIO(mvm, "Reading PPAG table v2 (tbl_rev=1)\n"); + goto read_table; + } + + /* try to read ppag table revision 0 */ + wifi_pkg = iwl_acpi_get_wifi_pkg(mvm->dev, data, + ACPI_PPAG_WIFI_DATA_SIZE, &tbl_rev); + if (!IS_ERR(wifi_pkg)) { + if (tbl_rev != 0) { + ret = -EINVAL; + goto out_free; + } + num_sub_bands = IWL_NUM_SUB_BANDS; + gain = mvm->fwrt.ppag_table.v1.gain[0]; + mvm->fwrt.ppag_ver = 1; + IWL_DEBUG_RADIO(mvm, "Reading PPAG table v1 (tbl_rev=0)\n"); + goto read_table; + } + ret = PTR_ERR(wifi_pkg); + goto out_free; + +read_table: + enabled = &wifi_pkg->package.elements[1]; + if (enabled->type != ACPI_TYPE_INTEGER || + (enabled->integer.value != 0 && enabled->integer.value != 1)) { + ret = -EINVAL; + goto out_free; + } + + mvm->fwrt.ppag_table.v1.enabled = cpu_to_le32(enabled->integer.value); + if (!mvm->fwrt.ppag_table.v1.enabled) { + ret = 0; + goto out_free; + } + + /* + * read, verify gain values and save them into the PPAG table. + * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the + * following sub-bands to High-Band (5GHz). + */ + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + union acpi_object *ent; + + ent = &wifi_pkg->package.elements[idx++]; + if (ent->type != ACPI_TYPE_INTEGER) { + ret = -EINVAL; + goto out_free; + } + + gain[i * num_sub_bands + j] = ent->integer.value; + + if ((j == 0 && + (gain[i * num_sub_bands + j] > ACPI_PPAG_MAX_LB || + gain[i * num_sub_bands + j] < ACPI_PPAG_MIN_LB)) || + (j != 0 && + (gain[i * num_sub_bands + j] > ACPI_PPAG_MAX_HB || + gain[i * num_sub_bands + j] < ACPI_PPAG_MIN_HB))) { + mvm->fwrt.ppag_table.v1.enabled = cpu_to_le32(0); + ret = -EINVAL; + goto out_free; + } } } - return iwl_mvm_send_cmd_pdu(mvm, cmd_wide_id, 0, sizeof(cmd), &cmd); + ret = 0; +out_free: + kfree(data); + return ret; } +int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) +{ + u8 cmd_ver; + int i, j, ret, num_sub_bands, cmd_size; + s8 *gain; + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) { + IWL_DEBUG_RADIO(mvm, + "PPAG capability not supported by FW, command not sent.\n"); + return 0; + } + if (!mvm->fwrt.ppag_table.v1.enabled) { + IWL_DEBUG_RADIO(mvm, "PPAG not enabled, command not sent.\n"); + return 0; + } + + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, + PER_PLATFORM_ANT_GAIN_CMD, + IWL_FW_CMD_VER_UNKNOWN); + if (cmd_ver == 1) { + num_sub_bands = IWL_NUM_SUB_BANDS; + gain = mvm->fwrt.ppag_table.v1.gain[0]; + cmd_size = sizeof(mvm->fwrt.ppag_table.v1); + if (mvm->fwrt.ppag_ver == 2) { + IWL_DEBUG_RADIO(mvm, + "PPAG table is v2 but FW supports v1, sending truncated table\n"); + } + } else if (cmd_ver == 2) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = mvm->fwrt.ppag_table.v2.gain[0]; + cmd_size = sizeof(mvm->fwrt.ppag_table.v2); + if (mvm->fwrt.ppag_ver == 1) { + IWL_DEBUG_RADIO(mvm, + "PPAG table is v1 but FW supports v2, sending padded table\n"); + } + } else { + IWL_DEBUG_RADIO(mvm, "Unsupported PPAG command version\n"); + return 0; + } + + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + IWL_DEBUG_RADIO(mvm, + "PPAG table: chain[%d] band[%d]: gain = %d\n", + i, j, gain[i * num_sub_bands + j]); + } + } + IWL_DEBUG_RADIO(mvm, "Sending PER_PLATFORM_ANT_GAIN_CMD\n"); + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP, + PER_PLATFORM_ANT_GAIN_CMD), + 0, cmd_size, &mvm->fwrt.ppag_table); + if (ret < 0) + IWL_ERR(mvm, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n", + ret); + + return ret; +} + +static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) +{ + int ret; + + ret = iwl_mvm_get_ppag_table(mvm); + if (ret < 0) { + IWL_DEBUG_RADIO(mvm, + "PPAG BIOS table invalid or unavailable. (%d)\n", + ret); + return 0; + } + return iwl_mvm_ppag_send_cmd(mvm); +} + +static void iwl_mvm_tas_init(struct iwl_mvm *mvm) +{ + int ret; + struct iwl_tas_config_cmd cmd = {}; + int list_size; + + BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) < + APCI_WTAS_BLACK_LIST_MAX); + + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { + IWL_DEBUG_RADIO(mvm, "TAS not enabled in FW\n"); + return; + } + + ret = iwl_acpi_get_tas(&mvm->fwrt, cmd.block_list_array, &list_size); + if (ret < 0) { + IWL_DEBUG_RADIO(mvm, + "TAS table invalid or unavailable. (%d)\n", + ret); + return; + } + + if (list_size < 0) + return; + + /* list size if TAS enabled can only be non-negative */ + cmd.block_list_size = cpu_to_le32((u32)list_size); + + ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, + TAS_CONFIG), + 0, sizeof(cmd), &cmd); + if (ret < 0) + IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); +} + +static u8 iwl_mvm_eval_dsm_indonesia_5g2(struct iwl_mvm *mvm) +{ + int ret = iwl_acpi_get_dsm_u8((&mvm->fwrt)->dev, 0, + DSM_FUNC_ENABLE_INDONESIA_5G2); + + if (ret < 0) + IWL_DEBUG_RADIO(mvm, + "Failed to evaluate DSM function ENABLE_INDONESIA_5G2, ret=%d\n", + ret); + + else if (ret >= DSM_VALUE_INDONESIA_MAX) + IWL_DEBUG_RADIO(mvm, + "DSM function ENABLE_INDONESIA_5G2 return invalid value, ret=%d\n", + ret); + + else if (ret == DSM_VALUE_INDONESIA_ENABLE) { + IWL_DEBUG_RADIO(mvm, + "Evaluated DSM function ENABLE_INDONESIA_5G2: Enabling 5g2\n"); + return DSM_VALUE_INDONESIA_ENABLE; + } + /* default behaviour is disabled */ + return DSM_VALUE_INDONESIA_DISABLE; +} + +static u8 iwl_mvm_eval_dsm_disable_srd(struct iwl_mvm *mvm) +{ + int ret = iwl_acpi_get_dsm_u8((&mvm->fwrt)->dev, 0, + DSM_FUNC_DISABLE_SRD); + + if (ret < 0) + IWL_DEBUG_RADIO(mvm, + "Failed to evaluate DSM function DISABLE_SRD, ret=%d\n", + ret); + + else if (ret >= DSM_VALUE_SRD_MAX) + IWL_DEBUG_RADIO(mvm, + "DSM function DISABLE_SRD return invalid value, ret=%d\n", + ret); + + else if (ret == DSM_VALUE_SRD_PASSIVE) { + IWL_DEBUG_RADIO(mvm, + "Evaluated DSM function DISABLE_SRD: setting SRD to passive\n"); + return DSM_VALUE_SRD_PASSIVE; + + } else if (ret == DSM_VALUE_SRD_DISABLE) { + IWL_DEBUG_RADIO(mvm, + "Evaluated DSM function DISABLE_SRD: disabling SRD\n"); + return DSM_VALUE_SRD_DISABLE; + } + /* default behaviour is active */ + return DSM_VALUE_SRD_ACTIVE; +} + +static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) +{ + u8 ret; + int cmd_ret; + struct iwl_lari_config_change_cmd cmd = {}; + + if (iwl_mvm_eval_dsm_indonesia_5g2(mvm) == DSM_VALUE_INDONESIA_ENABLE) + cmd.config_bitmap |= + cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); + + ret = iwl_mvm_eval_dsm_disable_srd(mvm); + if (ret == DSM_VALUE_SRD_PASSIVE) + cmd.config_bitmap |= + cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK); + + else if (ret == DSM_VALUE_SRD_DISABLE) + cmd.config_bitmap |= + cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK); + + /* apply more config masks here */ + + if (cmd.config_bitmap) { + IWL_DEBUG_RADIO(mvm, "sending LARI_CONFIG_CHANGE\n"); + cmd_ret = iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_CHANGE), + 0, sizeof(cmd), &cmd); + if (cmd_ret < 0) + IWL_DEBUG_RADIO(mvm, + "Failed to send LARI_CONFIG_CHANGE (%d)\n", + cmd_ret); + } +} #else /* CONFIG_ACPI */ -static int iwl_mvm_sar_get_wrds_table(struct iwl_mvm *mvm) + +inline int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, + int prof_a, int prof_b) { return -ENOENT; } -static int iwl_mvm_sar_get_ewrd_table(struct iwl_mvm *mvm) -{ - return -ENOENT; -} - -static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm) +inline int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) { return -ENOENT; } @@ -961,23 +1242,81 @@ return 0; } -int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, - int prof_b) +int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { return -ENOENT; } -int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) +static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) { - return -ENOENT; + return 0; +} + +static void iwl_mvm_tas_init(struct iwl_mvm *mvm) +{ +} + +static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) +{ } #endif /* CONFIG_ACPI */ + +void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) +{ + u32 error_log_size = mvm->fw->ucode_capa.error_log_size; + int ret; + u32 resp; + + struct iwl_fw_error_recovery_cmd recovery_cmd = { + .flags = cpu_to_le32(flags), + .buf_size = 0, + }; + struct iwl_host_cmd host_cmd = { + .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD), + .flags = CMD_WANT_SKB, + .data = {&recovery_cmd, }, + .len = {sizeof(recovery_cmd), }, + }; + + /* no error log was defined in TLV */ + if (!error_log_size) + return; + + if (flags & ERROR_RECOVERY_UPDATE_DB) { + /* no buf was allocated while HW reset */ + if (!mvm->error_recovery_buf) + return; + + host_cmd.data[1] = mvm->error_recovery_buf; + host_cmd.len[1] = error_log_size; + host_cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; + recovery_cmd.buf_size = cpu_to_le32(error_log_size); + } + + ret = iwl_mvm_send_cmd(mvm, &host_cmd); + kfree(mvm->error_recovery_buf); + mvm->error_recovery_buf = NULL; + + if (ret) { + IWL_ERR(mvm, "Failed to send recovery cmd %d\n", ret); + return; + } + + /* skb respond is only relevant in ERROR_RECOVERY_UPDATE_DB */ + if (flags & ERROR_RECOVERY_UPDATE_DB) { + resp = le32_to_cpu(*(__le32 *)host_cmd.resp_pkt->data); + if (resp) + IWL_ERR(mvm, + "Failed to send recovery cmd blob was invalid %d\n", + resp); + } +} static int iwl_mvm_sar_init(struct iwl_mvm *mvm) { int ret; - ret = iwl_mvm_sar_get_wrds_table(mvm); + ret = iwl_sar_get_wrds_table(&mvm->fwrt); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "WRDS SAR BIOS table invalid or unavailable. (%d)\n", @@ -989,25 +1328,14 @@ return 1; } - ret = iwl_mvm_sar_get_ewrd_table(mvm); + ret = iwl_sar_get_ewrd_table(&mvm->fwrt); /* if EWRD is not available, we can still use WRDS, so don't fail */ if (ret < 0) IWL_DEBUG_RADIO(mvm, "EWRD SAR BIOS table invalid or unavailable. (%d)\n", ret); - /* choose profile 1 (WRDS) as default for both chains */ - ret = iwl_mvm_sar_select_profile(mvm, 1, 1); - - /* - * If we don't have profile 0 from BIOS, just skip it. This - * means that SAR Geo will not be enabled either, even if we - * have other valid profiles. - */ - if (ret == -ENOENT) - return 1; - - return ret; + return iwl_mvm_sar_select_profile(mvm, 1, 1); } static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) @@ -1027,19 +1355,23 @@ return ret; } - /* - * Stop and start the transport without entering low power - * mode. This will save the state of other components on the - * device that are triggered by the INIT firwmare (MFUART). - */ - _iwl_trans_stop_device(mvm->trans, false); - ret = _iwl_trans_start_hw(mvm->trans, false); + iwl_fw_dbg_stop_sync(&mvm->fwrt); + iwl_trans_stop_device(mvm->trans); + ret = iwl_trans_start_hw(mvm->trans); if (ret) return ret; + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); + + mvm->rfkill_safe_init_done = false; ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); if (ret) return ret; + + mvm->rfkill_safe_init_done = true; + + iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, + NULL); return iwl_init_paging(&mvm->fwrt, mvm->fwrt.cur_fw_img); } @@ -1049,6 +1381,7 @@ int ret, i; struct ieee80211_channel *chan; struct cfg80211_chan_def chandef; + struct ieee80211_supported_band *sband = NULL; lockdep_assert_held(&mvm->mutex); @@ -1059,6 +1392,9 @@ ret = iwl_mvm_load_rt_fw(mvm); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); + if (ret != -ERFKILL) + iwl_fw_dbg_error_collect(&mvm->fwrt, + FW_DBG_TRIGGER_DRIVER); goto error; } @@ -1068,11 +1404,13 @@ if (ret) IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); - mvm->fwrt.dump.conf = FW_DBG_INVALID; - /* if we have a destination, assume EARLY START */ - if (mvm->fw->dbg_dest_tlv) - mvm->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE; - iwl_fw_start_dbg_conf(&mvm->fwrt, FW_DBG_START_FROM_ALIVE); + if (!iwl_trans_dbg_ini_valid(mvm->trans)) { + mvm->fwrt.dump.conf = FW_DBG_INVALID; + /* if we have a destination, assume EARLY START */ + if (mvm->fw->dbg.dest_tlv) + mvm->fwrt.dump.conf = FW_DBG_START_FROM_ALIVE; + iwl_fw_start_dbg_conf(&mvm->fwrt, FW_DBG_START_FROM_ALIVE); + } ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) @@ -1083,18 +1421,25 @@ ret = iwl_send_phy_db_data(mvm->phy_db); if (ret) goto error; - - ret = iwl_send_phy_cfg_cmd(mvm); - if (ret) - goto error; } + + ret = iwl_send_phy_cfg_cmd(mvm); + if (ret) + goto error; ret = iwl_mvm_send_bt_init_conf(mvm); if (ret) goto error; + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_SOC_LATENCY_SUPPORT)) { + ret = iwl_set_soc_latency(&mvm->fwrt); + if (ret) + goto error; + } + /* Init RSS configuration */ - if (mvm->trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) { + if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { ret = iwl_configure_rxq(mvm); if (ret) { IWL_ERR(mvm, "Failed to configure RX queues: %d\n", @@ -1113,7 +1458,7 @@ } /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) + for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); mvm->tdls_cs.peer.sta_id = IWL_MVM_INVALID_STA; @@ -1121,17 +1466,42 @@ /* reset quota debouncing buffer - 0xff will yield invalid data */ memset(&mvm->last_quota_cmd, 0xff, sizeof(mvm->last_quota_cmd)); - ret = iwl_mvm_send_dqa_cmd(mvm); - if (ret) - goto error; + if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_DQA_SUPPORT)) { + ret = iwl_mvm_send_dqa_cmd(mvm); + if (ret) + goto error; + } - /* Add auxiliary station for scanning */ - ret = iwl_mvm_add_aux_sta(mvm); - if (ret) - goto error; + /* + * Add auxiliary station for scanning. + * Newer versions of this command implies that the fw uses + * internal aux station for all aux activities that don't + * requires a dedicated data queue. + */ + if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, + ADD_STA, + 0) < 12) { + /* + * In old version the aux station uses mac id like other + * station and not lmac id + */ + ret = iwl_mvm_add_aux_sta(mvm, MAC_INDEX_AUX); + if (ret) + goto error; + } /* Add all the PHY contexts */ - chan = &mvm->hw->wiphy->bands[NL80211_BAND_2GHZ]->channels[0]; + i = 0; + while (!sband && i < NUM_NL80211_BANDS) + sband = mvm->hw->wiphy->bands[i++]; + + if (WARN_ON_ONCE(!sband)) { + ret = -ENODEV; + goto error; + } + + chan = &sband->channels[0]; + cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT); for (i = 0; i < NUM_PHY_CTX; i++) { /* @@ -1145,7 +1515,6 @@ goto error; } -#ifdef CONFIG_THERMAL if (iwl_mvm_is_tt_in_fw(mvm)) { /* in order to give the responsibility of ct-kill and * TX backoff to FW we need to send empty temperature reporting @@ -1157,6 +1526,7 @@ iwl_mvm_tt_tx_backoff(mvm, 0); } +#ifdef CONFIG_THERMAL /* TODO: read the budget from BIOS / Platform NVM */ /* @@ -1169,17 +1539,16 @@ if (ret) goto error; } -#else - /* Initialize tx backoffs to the minimal possible */ - iwl_mvm_tt_tx_backoff(mvm, 0); #endif - WARN_ON(iwl_mvm_config_ltr(mvm)); + if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_LTR_GEN2)) + WARN_ON(iwl_mvm_config_ltr(mvm)); ret = iwl_mvm_power_update_device(mvm); if (ret) goto error; + iwl_mvm_lari_cfg(mvm); /* * RTNL is not taken during Ct-kill, but we don't need to scan/Tx * anyway, so don't init MCC. @@ -1198,14 +1567,20 @@ goto error; } - /* allow FW/transport low power modes if not during restart */ - if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) - iwl_mvm_unref(mvm, IWL_MVM_REF_UCODE_DOWN); + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) + iwl_mvm_send_recovery_cmd(mvm, ERROR_RECOVERY_UPDATE_DB); + + if (iwl_acpi_get_eckv(mvm->dev, &mvm->ext_clock_valid)) + IWL_DEBUG_INFO(mvm, "ECKV table doesn't exist in BIOS\n"); + + ret = iwl_mvm_ppag_init(mvm); + if (ret) + goto error; ret = iwl_mvm_sar_init(mvm); if (ret == 0) { ret = iwl_mvm_sar_geo_init(mvm); - } else if (ret > 0 && !iwl_mvm_sar_get_wgds_table(mvm)) { + } else if (ret == -ENOENT && !iwl_sar_get_wgds_table(&mvm->fwrt)) { /* * If basic SAR is not available, we check for WGDS, * which should *not* be available either. If it is @@ -1218,7 +1593,10 @@ if (ret < 0) goto error; + iwl_mvm_tas_init(mvm); iwl_mvm_leds_sync(mvm); + + iwl_mvm_ftm_initiator_smooth_config(mvm); IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; @@ -1258,13 +1636,24 @@ goto error; /* init the fw <-> mac80211 STA mapping */ - for (i = 0; i < ARRAY_SIZE(mvm->fw_id_to_mac_id); i++) + for (i = 0; i < mvm->fw->ucode_capa.num_stations; i++) RCU_INIT_POINTER(mvm->fw_id_to_mac_id[i], NULL); - /* Add auxiliary station for scanning */ - ret = iwl_mvm_add_aux_sta(mvm); - if (ret) - goto error; + if (iwl_fw_lookup_cmd_ver(mvm->fw, LONG_GROUP, + ADD_STA, + 0) < 12) { + /* + * Add auxiliary station for scanning. + * Newer versions of this command implies that the fw uses + * internal aux station for all aux activities that don't + * requires a dedicated data queue. + * In old version the aux station uses mac id like other + * station and not lmac id + */ + ret = iwl_mvm_add_aux_sta(mvm, MAC_INDEX_AUX); + if (ret) + goto error; + } return 0; error: -- Gitblit v1.6.2