diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/fw.c')
| -rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 157 |
1 files changed, 143 insertions, 14 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 6cc78661116e..f05df3a3300e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -459,16 +459,11 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, static void iwl_mvm_uats_init(struct iwl_mvm *mvm) { + int cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + MCC_ALLOWED_AP_TYPE_CMD); + struct iwl_mcc_allowed_ap_type_cmd_v1 cmd = {}; u8 cmd_ver; int ret; - struct iwl_host_cmd cmd = { - .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, - MCC_ALLOWED_AP_TYPE_CMD), - .flags = 0, - .data[0] = &mvm->fwrt.uats_table, - .len[0] = sizeof(mvm->fwrt.uats_table), - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - }; if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210 || !mvm->trans->cfg->uhb_supported) { @@ -476,7 +471,7 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) return; } - cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd.id, + cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, IWL_FW_CMD_VER_UNKNOWN); if (cmd_ver != 1) { IWL_DEBUG_RADIO(mvm, @@ -487,10 +482,17 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt); - if (!mvm->fwrt.uats_valid) + if (!mvm->fwrt.ap_type_cmd_valid) return; - ret = iwl_mvm_send_cmd(mvm, &cmd); + BUILD_BUG_ON(sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map) != + sizeof(cmd.mcc_to_ap_type_map)); + + memcpy(cmd.mcc_to_ap_type_map, + mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map, + sizeof(mvm->fwrt.ap_type_cmd.mcc_to_ap_type_map)); + + ret = iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, sizeof(cmd), &cmd); if (ret < 0) IWL_ERR(mvm, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", ret); @@ -907,7 +909,7 @@ int iwl_mvm_sar_select_profile(struct iwl_mvm *mvm, int prof_a, int prof_b) int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) { - union iwl_geo_tx_power_profiles_cmd geo_tx_cmd; + union iwl_geo_tx_power_profiles_cmd geo_tx_cmd = {}; struct iwl_geo_tx_power_profiles_resp *resp; u16 len; int ret; @@ -959,7 +961,7 @@ int iwl_mvm_get_sar_geo_profile(struct iwl_mvm *mvm) static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) { u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD); - union iwl_geo_tx_power_profiles_cmd cmd; + union iwl_geo_tx_power_profiles_cmd cmd = {}; u16 len; u32 n_bands; u32 n_profiles; @@ -1033,12 +1035,139 @@ static int iwl_mvm_sar_geo_init(struct iwl_mvm *mvm) return iwl_mvm_send_cmd_pdu(mvm, cmd_id, 0, len, &cmd); } +static bool iwl_mvm_ppag_value_valid(struct iwl_fw_runtime *fwrt, int chain, + int subband) +{ + s8 ppag_val = fwrt->ppag_chains[chain].subbands[subband]; + + if ((subband == 0 && + (ppag_val > IWL_PPAG_MAX_LB || ppag_val < IWL_PPAG_MIN_LB)) || + (subband != 0 && + (ppag_val > IWL_PPAG_MAX_HB || ppag_val < IWL_PPAG_MIN_HB))) { + IWL_DEBUG_RADIO(fwrt, "Invalid PPAG value: %d\n", ppag_val); + return false; + } + return true; +} + +static int iwl_mvm_fill_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, + int *cmd_size) +{ + u8 cmd_ver; + int i, j, num_sub_bands; + s8 *gain; + bool send_ppag_always; + + /* many firmware images for JF lie about this */ + if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) == + CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF)) + return -EOPNOTSUPP; + + if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) { + IWL_DEBUG_RADIO(fwrt, + "PPAG capability not supported by FW, command not sent.\n"); + return -EINVAL; + } + + cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, + WIDE_ID(PHY_OPS_GROUP, + PER_PLATFORM_ANT_GAIN_CMD), 1); + /* + * Starting from ver 4, driver needs to send the PPAG CMD regardless + * if PPAG is enabled/disabled or valid/invalid. + */ + send_ppag_always = cmd_ver > 3; + + /* Don't send PPAG if it is disabled */ + if (!send_ppag_always && !fwrt->ppag_flags) { + IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); + return -EINVAL; + } + + IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver); + if (cmd_ver == 1) { + num_sub_bands = IWL_NUM_SUB_BANDS_V1; + gain = cmd->v1.gain[0]; + *cmd_size = sizeof(cmd->v1); + cmd->v1.flags = + cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V1_MASK); + if (fwrt->ppag_bios_rev >= 1) { + /* in this case FW supports revision 0 */ + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is %d, send truncated table\n", + fwrt->ppag_bios_rev); + } + } else if (cmd_ver == 5) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v5.gain[0]; + *cmd_size = sizeof(cmd->v5); + cmd->v5.flags = + cpu_to_le32(fwrt->ppag_flags & IWL_PPAG_CMD_V5_MASK); + if (fwrt->ppag_bios_rev == 0) { + /* in this case FW supports revisions 1,2 or 3 */ + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is 0, send padded table\n"); + } + } else if (cmd_ver == 7) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v7.gain[0]; + *cmd_size = sizeof(cmd->v7); + cmd->v7.ppag_config_info.hdr.table_source = + fwrt->ppag_bios_source; + cmd->v7.ppag_config_info.hdr.table_revision = + fwrt->ppag_bios_rev; + cmd->v7.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags); + } else { + IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n"); + return -EINVAL; + } + + /* ppag mode */ + IWL_DEBUG_RADIO(fwrt, + "PPAG MODE bits were read from bios: %d\n", + fwrt->ppag_flags); + + if (cmd_ver == 1 && + !fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) { + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); + IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n"); + } else { + IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n"); + } + + /* The 'flags' field is the same in v1 and v5 so we can just + * use v1 to access it. + */ + IWL_DEBUG_RADIO(fwrt, + "PPAG MODE bits going to be sent: %d\n", + (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) : + le32_to_cpu(cmd->v7.ppag_config_info.value)); + + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + if (!send_ppag_always && + !iwl_mvm_ppag_value_valid(fwrt, i, j)) + return -EINVAL; + + gain[i * num_sub_bands + j] = + fwrt->ppag_chains[i].subbands[j]; + IWL_DEBUG_RADIO(fwrt, + "PPAG table: chain[%d] band[%d]: gain = %d\n", + i, j, gain[i * num_sub_bands + j]); + } + } + + return 0; +} + int iwl_mvm_ppag_send_cmd(struct iwl_mvm *mvm) { union iwl_ppag_table_cmd cmd; int ret, cmd_size; - ret = iwl_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size); + ret = iwl_mvm_fill_ppag_table(&mvm->fwrt, &cmd, &cmd_size); /* Not supporting PPAG table is a valid scenario */ if (ret < 0) return 0; |
