diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/fw.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 227 |
1 files changed, 165 insertions, 62 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 863fec150e53..6f4690e56a46 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -12,8 +12,6 @@ #include "iwl-op-mode.h" #include "fw/img.h" #include "iwl-debug.h" -#include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */ -#include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */ #include "iwl-prph.h" #include "fw/acpi.h" #include "fw/pnvm.h" @@ -32,6 +30,9 @@ #define IWL_PPAG_MASK 3 #define IWL_PPAG_ETSI_MASK BIT(0) +#define IWL_TAS_US_MCC 0x5553 +#define IWL_TAS_CANADA_MCC 0x4341 + struct iwl_mvm_alive_data { bool valid; u32 scd_base_addr; @@ -123,13 +124,15 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, struct iwl_lmac_alive *lmac2 = NULL; u16 status; u32 lmac_error_event_table, umac_error_table; + u32 version = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + UCODE_ALIVE_NTFY, 0); /* * 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) { + if (version == 5 || version == 6) { + /* v5 and v6 are compatible (only IMR addition) */ struct iwl_alive_ntf_v5 *palive; if (pkt_len < sizeof(*palive)) @@ -516,7 +519,6 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, cpu_to_le32(IWL_MVM_PHY_FILTER_CHAIN_D); } } - #else /* CONFIG_ACPI */ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, @@ -525,6 +527,49 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, } #endif /* CONFIG_ACPI */ +#if defined(CONFIG_ACPI) && defined(CONFIG_EFI) +static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) +{ + u8 cmd_ver; + int ret; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + SAR_OFFSET_MAPPING_TABLE_CMD), + .flags = 0, + .data[0] = &mvm->fwrt.sgom_table, + .len[0] = sizeof(mvm->fwrt.sgom_table), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + + if (!mvm->fwrt.sgom_enabled) { + IWL_DEBUG_RADIO(mvm, "SGOM table is disabled\n"); + return 0; + } + + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, REGULATORY_AND_NVM_GROUP, + SAR_OFFSET_MAPPING_TABLE_CMD, + IWL_FW_CMD_VER_UNKNOWN); + + if (cmd_ver != 2) { + IWL_DEBUG_RADIO(mvm, "command version is unsupported. version = %d\n", + cmd_ver); + return 0; + } + + ret = iwl_mvm_send_cmd(mvm, &cmd); + if (ret < 0) + IWL_ERR(mvm, "failed to send SAR_OFFSET_MAPPING_CMD (%d)\n", ret); + + return ret; +} +#else + +static int iwl_mvm_sgom_init(struct iwl_mvm *mvm) +{ + return 0; +} +#endif + static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) { struct iwl_phy_cfg_cmd_v3 phy_cfg_cmd; @@ -757,6 +802,8 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) if (ret) return ret; + iwl_mei_set_power_limit(per_chain); + 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); } @@ -820,6 +867,7 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) u16 len; u32 n_bands; u32 n_profiles; + u32 sk = 0; int ret; u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD, @@ -879,19 +927,26 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) if (ret) return 0; + /* Only set to South Korea if the table revision is 1 */ + if (mvm->fwrt.geo_rev == 1) + sk = 1; + /* - * Set the revision on versions that contain it. + * Set the table_revision to South Korea (1) or not (0). The + * element name is misleading, as it doesn't contain the table + * revision number, but whether the South Korea variation + * should be used. * This must be done after calling iwl_sar_geo_init(). */ if (cmd_ver == 5) - cmd.v5.table_revision = cpu_to_le32(mvm->fwrt.geo_rev); + cmd.v5.table_revision = cpu_to_le32(sk); else if (cmd_ver == 4) - cmd.v4.table_revision = cpu_to_le32(mvm->fwrt.geo_rev); + cmd.v4.table_revision = cpu_to_le32(sk); else if (cmd_ver == 3) - cmd.v3.table_revision = cpu_to_le32(mvm->fwrt.geo_rev); + cmd.v3.table_revision = cpu_to_le32(sk); 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); + cmd.v2.table_revision = cpu_to_le32(sk); return iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(PHY_OPS_GROUP, @@ -904,13 +959,8 @@ static int iwl_mvm_get_ppag_table(struct iwl_mvm *mvm) union acpi_object *wifi_pkg, *data, *flags; int i, j, ret, tbl_rev, num_sub_bands; int idx = 2; - s8 *gain; - /* - * The 'flags' field is the same in v1 and in v2 so we can just - * use v1 to access it. - */ - mvm->fwrt.ppag_table.v1.flags = cpu_to_le32(0); + mvm->fwrt.ppag_flags = 0; data = iwl_acpi_get_object(mvm->dev, ACPI_PPAG_METHOD); if (IS_ERR(data)) @@ -922,8 +972,6 @@ static int iwl_mvm_get_ppag_table(struct iwl_mvm *mvm) if (!IS_ERR(wifi_pkg)) { if (tbl_rev == 1 || tbl_rev == 2) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; - gain = mvm->fwrt.ppag_table.v2.gain[0]; - mvm->fwrt.ppag_ver = tbl_rev; IWL_DEBUG_RADIO(mvm, "Reading PPAG table v2 (tbl_rev=%d)\n", tbl_rev); @@ -943,8 +991,6 @@ static int iwl_mvm_get_ppag_table(struct iwl_mvm *mvm) goto out_free; } num_sub_bands = IWL_NUM_SUB_BANDS_V1; - gain = mvm->fwrt.ppag_table.v1.gain[0]; - mvm->fwrt.ppag_ver = 0; IWL_DEBUG_RADIO(mvm, "Reading PPAG table v1 (tbl_rev=0)\n"); goto read_table; } @@ -952,6 +998,7 @@ static int iwl_mvm_get_ppag_table(struct iwl_mvm *mvm) goto out_free; read_table: + mvm->fwrt.ppag_ver = tbl_rev; flags = &wifi_pkg->package.elements[1]; if (flags->type != ACPI_TYPE_INTEGER) { @@ -959,10 +1006,9 @@ read_table: goto out_free; } - mvm->fwrt.ppag_table.v1.flags = cpu_to_le32(flags->integer.value & - IWL_PPAG_MASK); + mvm->fwrt.ppag_flags = flags->integer.value & IWL_PPAG_MASK; - if (!mvm->fwrt.ppag_table.v1.flags) { + if (!mvm->fwrt.ppag_flags) { ret = 0; goto out_free; } @@ -982,15 +1028,15 @@ read_table: goto out_free; } - gain[i * num_sub_bands + j] = ent->integer.value; + mvm->fwrt.ppag_chains[i].subbands[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)) || + (mvm->fwrt.ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB || + mvm->fwrt.ppag_chains[i].subbands[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.flags = cpu_to_le32(0); + (mvm->fwrt.ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB || + mvm->fwrt.ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) { + mvm->fwrt.ppag_flags = 0; ret = -EINVAL; goto out_free; } @@ -1005,6 +1051,7 @@ out_free: int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { + union iwl_ppag_table_cmd cmd; u8 cmd_ver; int i, j, ret, num_sub_bands, cmd_size; s8 *gain; @@ -1014,37 +1061,39 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) "PPAG capability not supported by FW, command not sent.\n"); return 0; } - if (!mvm->fwrt.ppag_table.v1.flags) { + if (!mvm->fwrt.ppag_flags) { IWL_DEBUG_RADIO(mvm, "PPAG not enabled, command not sent.\n"); return 0; } + /* The 'flags' field is the same in v1 and in v2 so we can just + * use v1 to access it. + */ + cmd.v1.flags = cpu_to_le32(mvm->fwrt.ppag_flags); 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_V1; - gain = mvm->fwrt.ppag_table.v1.gain[0]; - cmd_size = sizeof(mvm->fwrt.ppag_table.v1); + gain = cmd.v1.gain[0]; + cmd_size = sizeof(cmd.v1); if (mvm->fwrt.ppag_ver == 1 || mvm->fwrt.ppag_ver == 2) { IWL_DEBUG_RADIO(mvm, "PPAG table rev is %d but FW supports v1, sending truncated table\n", mvm->fwrt.ppag_ver); - mvm->fwrt.ppag_table.v1.flags &= - cpu_to_le32(IWL_PPAG_ETSI_MASK); + cmd.v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); } } else if (cmd_ver == 2 || cmd_ver == 3) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; - gain = mvm->fwrt.ppag_table.v2.gain[0]; - cmd_size = sizeof(mvm->fwrt.ppag_table.v2); + gain = cmd.v2.gain[0]; + cmd_size = sizeof(cmd.v2); if (mvm->fwrt.ppag_ver == 0) { IWL_DEBUG_RADIO(mvm, "PPAG table is v1 but FW supports v2, sending padded table\n"); } else if (cmd_ver == 2 && mvm->fwrt.ppag_ver == 2) { IWL_DEBUG_RADIO(mvm, "PPAG table is v3 but FW supports v2, sending partial bitmap.\n"); - mvm->fwrt.ppag_table.v1.flags &= - cpu_to_le32(IWL_PPAG_ETSI_MASK); + cmd.v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); } } else { IWL_DEBUG_RADIO(mvm, "Unsupported PPAG command version\n"); @@ -1053,6 +1102,8 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { for (j = 0; j < num_sub_bands; j++) { + gain[i * num_sub_bands + j] = + mvm->fwrt.ppag_chains[i].subbands[j]; IWL_DEBUG_RADIO(mvm, "PPAG table: chain[%d] band[%d]: gain = %d\n", i, j, gain[i * num_sub_bands + j]); @@ -1061,7 +1112,7 @@ int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) 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); + 0, cmd_size, &cmd); if (ret < 0) IWL_ERR(mvm, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n", ret); @@ -1100,18 +1151,63 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "System vendor '%s' is not in the approved list, disabling PPAG.\n", dmi_get_system_info(DMI_SYS_VENDOR)); - mvm->fwrt.ppag_table.v1.flags = cpu_to_le32(0); + mvm->fwrt.ppag_flags = 0; return 0; } return iwl_mvm_ppag_send_cmd(mvm); } +static const struct dmi_system_id dmi_tas_approved_list[] = { + { .ident = "HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + }, + }, + { .ident = "SAMSUNG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), + }, + }, + { .ident = "LENOVO", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Lenovo"), + }, + }, + { .ident = "DELL", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + }, + }, + + /* keep last */ + {} +}; + +static bool iwl_mvm_add_to_tas_block_list(__le32 *list, __le32 *le_size, unsigned int mcc) +{ + int i; + u32 size = le32_to_cpu(*le_size); + + /* Verify that there is room for another country */ + if (size >= IWL_TAS_BLOCK_LIST_MAX) + return false; + + for (i = 0; i < size; i++) { + if (list[i] == cpu_to_le32(mcc)) + return true; + } + + list[size++] = cpu_to_le32(mcc); + *le_size = cpu_to_le32(size); + return true; +} + static void iwl_mvm_tas_init(struct iwl_mvm *mvm) { int ret; - struct iwl_tas_config_cmd cmd = {}; - int list_size; + struct iwl_tas_config_cmd_v3 cmd = {}; + int cmd_size; BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) < APCI_WTAS_BLACK_LIST_MAX); @@ -1121,7 +1217,7 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) return; } - ret = iwl_acpi_get_tas(&mvm->fwrt, cmd.block_list_array, &list_size); + ret = iwl_acpi_get_tas(&mvm->fwrt, &cmd); if (ret < 0) { IWL_DEBUG_RADIO(mvm, "TAS table invalid or unavailable. (%d)\n", @@ -1129,15 +1225,32 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) return; } - if (list_size < 0) + if (ret == 0) return; - /* list size if TAS enabled can only be non-negative */ - cmd.block_list_size = cpu_to_le32((u32)list_size); + if (!dmi_check_system(dmi_tas_approved_list)) { + IWL_DEBUG_RADIO(mvm, + "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", + dmi_get_system_info(DMI_SYS_VENDOR)); + if ((!iwl_mvm_add_to_tas_block_list(cmd.block_list_array, + &cmd.block_list_size, IWL_TAS_US_MCC)) || + (!iwl_mvm_add_to_tas_block_list(cmd.block_list_array, + &cmd.block_list_size, IWL_TAS_CANADA_MCC))) { + IWL_DEBUG_RADIO(mvm, + "Unable to add US/Canada to TAS block list, disabling TAS\n"); + return; + } + } + + cmd_size = iwl_fw_lookup_cmd_ver(mvm->fw, REGULATORY_AND_NVM_GROUP, + TAS_CONFIG, + IWL_FW_CMD_VER_UNKNOWN) < 3 ? + sizeof(struct iwl_tas_config_cmd_v2) : + sizeof(struct iwl_tas_config_cmd_v3); ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG), - 0, sizeof(cmd), &cmd); + 0, cmd_size, &cmd); if (ret < 0) IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); } @@ -1336,6 +1449,7 @@ static u8 iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) void iwl_mvm_get_acpi_tables(struct iwl_mvm *mvm) { } + #endif /* CONFIG_ACPI */ void iwl_mvm_send_recovery_cmd(struct iwl_mvm *mvm, u32 flags) @@ -1401,7 +1515,6 @@ static int iwl_mvm_load_rt_fw(struct iwl_mvm *mvm) if (iwl_mvm_has_unified_ucode(mvm)) return iwl_run_unified_mvm_ucode(mvm); - WARN_ON(!mvm->nvm_data); ret = iwl_run_init_mvm_ucode(mvm); if (ret) { @@ -1631,6 +1744,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) else if (ret < 0) goto error; + ret = iwl_mvm_sgom_init(mvm); + if (ret) + goto error; + iwl_mvm_tas_init(mvm); iwl_mvm_leds_sync(mvm); @@ -1705,20 +1822,6 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) return ret; } -void iwl_mvm_rx_card_state_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_card_state_notif *card_state_notif = (void *)pkt->data; - u32 flags = le32_to_cpu(card_state_notif->flags); - - IWL_DEBUG_RF_KILL(mvm, "Card state received: HW:%s SW:%s CT:%s\n", - (flags & HW_CARD_DISABLED) ? "Kill" : "On", - (flags & SW_CARD_DISABLED) ? "Kill" : "On", - (flags & CT_KILL_CARD_DISABLED) ? - "Reached" : "Not reached"); -} - void iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { |