diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/fw')
26 files changed, 1697 insertions, 865 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index b96f30d11644..4caf2e25a297 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -4,7 +4,6 @@ * Copyright (C) 2019-2023 Intel Corporation */ #include <linux/uuid.h> -#include <linux/dmi.h> #include "iwl-drv.h" #include "iwl-debug.h" #include "acpi.h" @@ -13,68 +12,21 @@ const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6, 0xA5, 0xB3, 0x1F, 0x73, 0x8E, 0x28, 0x5A, 0xDE); -IWL_EXPORT_SYMBOL(iwl_guid); -const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29, - 0x81, 0x4F, 0x75, 0xE4, - 0xDD, 0x26, 0xB5, 0xFD); -IWL_EXPORT_SYMBOL(iwl_rfi_guid); - -static const struct dmi_system_id dmi_ppag_approved_list[] = { - { .ident = "HP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - }, - }, - { .ident = "SAMSUNG", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), - }, - }, - { .ident = "MSFT", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - }, - }, - { .ident = "ASUS", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - }, - }, - { .ident = "GOOGLE-HP", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_BOARD_VENDOR, "HP"), - }, - }, - { .ident = "GOOGLE-ASUS", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."), - }, - }, - { .ident = "GOOGLE-SAMSUNG", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Google"), - DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), - }, - }, - { .ident = "DELL", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - }, - }, - { .ident = "DELL", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), - }, - }, - { .ident = "RAZER", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Razer"), - }, - }, - {} +static const size_t acpi_dsm_size[DSM_FUNC_NUM_FUNCS] = { + [DSM_FUNC_QUERY] = sizeof(u32), + [DSM_FUNC_DISABLE_SRD] = sizeof(u8), + [DSM_FUNC_ENABLE_INDONESIA_5G2] = sizeof(u8), + [DSM_FUNC_ENABLE_6E] = sizeof(u32), + [DSM_FUNC_REGULATORY_CONFIG] = sizeof(u32), + /* Not supported in driver */ + [5] = (size_t)0, + [DSM_FUNC_11AX_ENABLEMENT] = sizeof(u32), + [DSM_FUNC_ENABLE_UNII4_CHAN] = sizeof(u32), + [DSM_FUNC_ACTIVATE_CHANNEL] = sizeof(u32), + [DSM_FUNC_FORCE_DISABLE_CHANNELS] = sizeof(u32), + [DSM_FUNC_ENERGY_DETECTION_THRESHOLD] = sizeof(u32), + [DSM_FUNC_RFI_CONFIG] = sizeof(u32), }; static int iwl_acpi_get_handle(struct device *dev, acpi_string method, @@ -200,46 +152,41 @@ out: } /* - * Evaluate a DSM with no arguments and a u8 return value, + * This function receives a DSM function number, calculates its expected size + * according to Intel BIOS spec, and fills in the value in a 32-bit field. + * In case the expected size is smaller than 32-bit, padding will be added. */ -int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, - const guid_t *guid, u8 *value) +int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs func, u32 *value) { + size_t expected_size; + u64 tmp; int ret; - u64 val; - ret = iwl_acpi_get_dsm_integer(dev, rev, func, - guid, &val, sizeof(u8)); + BUILD_BUG_ON(ARRAY_SIZE(acpi_dsm_size) != DSM_FUNC_NUM_FUNCS); - if (ret < 0) - return ret; + if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size))) + return -EINVAL; - /* cast val (u64) to be u8 */ - *value = (u8)val; - return 0; -} -IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8); + expected_size = acpi_dsm_size[func]; -/* - * Evaluate a DSM with no arguments and a u32 return value, - */ -int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, - const guid_t *guid, u32 *value) -{ - int ret; - u64 val; - - ret = iwl_acpi_get_dsm_integer(dev, rev, func, - guid, &val, sizeof(u32)); + /* Currently all ACPI DSMs are either 8-bit or 32-bit */ + if (expected_size != sizeof(u8) && expected_size != sizeof(u32)) + return -EOPNOTSUPP; - if (ret < 0) + ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func, + &iwl_guid, &tmp, expected_size); + if (ret) return ret; - /* cast val (u64) to be u32 */ - *value = (u32)val; + if ((expected_size == sizeof(u8) && tmp != (u8)tmp) || + (expected_size == sizeof(u32) && tmp != (u32)tmp)) + IWL_DEBUG_RADIO(fwrt, + "DSM value overflows the expected size, truncating\n"); + *value = (u32)tmp; + return 0; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u32); static union acpi_object * iwl_acpi_get_wifi_pkg_range(struct device *dev, @@ -307,9 +254,8 @@ iwl_acpi_get_wifi_pkg(struct device *dev, tbl_rev); } - -int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver) +int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *tas_data) { union acpi_object *wifi_pkg, *data; int ret, tbl_rev, i, block_list_size, enabled; @@ -331,22 +277,9 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, ACPI_TYPE_INTEGER) { u32 tas_selection = (u32)wifi_pkg->package.elements[1].integer.value; - u16 override_iec = - (tas_selection & ACPI_WTAS_OVERRIDE_IEC_MSK) >> ACPI_WTAS_OVERRIDE_IEC_POS; - u16 enabled_iec = (tas_selection & ACPI_WTAS_ENABLE_IEC_MSK) >> - ACPI_WTAS_ENABLE_IEC_POS; - u8 usa_tas_uhb = (tas_selection & ACPI_WTAS_USA_UHB_MSK) >> ACPI_WTAS_USA_UHB_POS; - - enabled = tas_selection & ACPI_WTAS_ENABLED_MSK; - if (fw_ver <= 3) { - cmd->v3.override_tas_iec = cpu_to_le16(override_iec); - cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec); - } else { - cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb; - cmd->v4.override_tas_iec = (u8)override_iec; - cmd->v4.enable_tas_iec = (u8)enabled_iec; - } + enabled = iwl_parse_tas_selection(fwrt, tas_data, + tas_selection); } else if (tbl_rev == 0 && wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) { @@ -365,22 +298,16 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev); if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER || wifi_pkg->package.elements[2].integer.value > - APCI_WTAS_BLACK_LIST_MAX) { + IWL_WTAS_BLACK_LIST_MAX) { IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n", wifi_pkg->package.elements[2].integer.value); ret = -EINVAL; goto out_free; } block_list_size = wifi_pkg->package.elements[2].integer.value; - cmd->v4.block_list_size = cpu_to_le32(block_list_size); + tas_data->block_list_size = cpu_to_le32(block_list_size); IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size); - if (block_list_size > APCI_WTAS_BLACK_LIST_MAX) { - IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n", - block_list_size); - ret = -EINVAL; - goto out_free; - } for (i = 0; i < block_list_size; i++) { u32 country; @@ -394,7 +321,7 @@ int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, } country = wifi_pkg->package.elements[3 + i].integer.value; - cmd->v4.block_list_array[i] = cpu_to_le32(country); + tas_data->block_list_array[i] = cpu_to_le32(country); IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country); } @@ -403,19 +330,19 @@ out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_tas); -int iwl_acpi_get_mcc(struct device *dev, char *mcc) +int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) { union acpi_object *wifi_pkg, *data; u32 mcc_val; int ret, tbl_rev; - data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD); + data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDD_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE, + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_WRDD_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg)) { ret = PTR_ERR(wifi_pkg); @@ -439,46 +366,42 @@ out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc); -u64 iwl_acpi_get_pwr_limit(struct device *dev) +int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit) { union acpi_object *data, *wifi_pkg; - u64 dflt_pwr_limit; - int tbl_rev; + int tbl_rev, ret = -EINVAL; - data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD); - if (IS_ERR(data)) { - dflt_pwr_limit = 0; + *dflt_pwr_limit = 0; + data = iwl_acpi_get_object(fwrt->dev, ACPI_SPLC_METHOD); + if (IS_ERR(data)) goto out; - } - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg) || tbl_rev != 0 || - wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) { - dflt_pwr_limit = 0; + wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) goto out_free; - } - dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value; + *dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value; + ret = 0; out_free: kfree(data); out: - return dflt_pwr_limit; + return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit); -int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) +int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) { union acpi_object *wifi_pkg, *data; int ret, tbl_rev; - data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD); + data = iwl_acpi_get_object(fwrt->dev, ACPI_ECKV_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE, + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_ECKV_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg)) { ret = PTR_ERR(wifi_pkg); @@ -499,11 +422,11 @@ out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv); -static int iwl_sar_set_profile(union acpi_object *table, - struct iwl_sar_profile *profile, - bool enabled, u8 num_chains, u8 num_sub_bands) +static int iwl_acpi_sar_set_profile(union acpi_object *table, + struct iwl_sar_profile *profile, + bool enabled, u8 num_chains, + u8 num_sub_bands) { int i, j, idx = 0; @@ -511,8 +434,8 @@ static int iwl_sar_set_profile(union acpi_object *table, * The table from ACPI is flat, but we store it in a * structured array. */ - for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV2; i++) { - for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS_REV2; j++) { + for (i = 0; i < BIOS_SAR_MAX_CHAINS_PER_PROFILE; i++) { + for (j = 0; j < BIOS_SAR_MAX_SUB_BANDS_NUM; j++) { /* if we don't have the values, use the default */ if (i >= num_chains || j >= num_sub_bands) { profile->chains[i].subbands[j] = 0; @@ -535,73 +458,7 @@ static int iwl_sar_set_profile(union acpi_object *table, return 0; } -static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_subbands, - int prof_a, int prof_b) -{ - int profs[ACPI_SAR_NUM_CHAINS_REV0] = { prof_a, prof_b }; - int i, j; - - for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV0; i++) { - struct iwl_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 = &fwrt->sar_profiles[profs[i] - 1]; - - /* if the profile is disabled, do nothing */ - if (!prof->enabled) { - IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n", - profs[i]); - /* - * if one of the profiles is disabled, we - * ignore all of them and return 1 to - * differentiate disabled from other failures. - */ - return 1; - } - - IWL_DEBUG_INFO(fwrt, - "SAR EWRD: chain %d profile index %d\n", - i, profs[i]); - IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i); - for (j = 0; j < n_subbands; j++) { - per_chain[i * n_subbands + j] = - cpu_to_le16(prof->chains[i].subbands[j]); - IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n", - j, prof->chains[i].subbands[j]); - } - } - - return 0; -} - -int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_tables, u32 n_subbands, - int prof_a, int prof_b) -{ - int i, ret = 0; - - for (i = 0; i < n_tables; i++) { - ret = iwl_sar_fill_table(fwrt, - &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAINS_REV0], - n_subbands, prof_a, prof_b); - if (ret) - break; - } - - return ret; -} -IWL_EXPORT_SYMBOL(iwl_sar_select_profile); - -int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) +int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *table, *data; int ret, tbl_rev; @@ -618,7 +475,7 @@ int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) &tbl_rev); if (!IS_ERR(wifi_pkg)) { if (tbl_rev != 2) { - ret = PTR_ERR(wifi_pkg); + ret = -EINVAL; goto out_free; } @@ -634,7 +491,7 @@ int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) &tbl_rev); if (!IS_ERR(wifi_pkg)) { if (tbl_rev != 1) { - ret = PTR_ERR(wifi_pkg); + ret = -EINVAL; goto out_free; } @@ -650,7 +507,7 @@ int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) &tbl_rev); if (!IS_ERR(wifi_pkg)) { if (tbl_rev != 0) { - ret = PTR_ERR(wifi_pkg); + ret = -EINVAL; goto out_free; } @@ -680,16 +537,15 @@ read_table: /* The profile from WRDS is officially profile 1, but goes * into sar_profiles[0] (because we don't have a profile 0). */ - ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], - flags & IWL_SAR_ENABLE_MSK, - num_chains, num_sub_bands); + ret = iwl_acpi_sar_set_profile(table, &fwrt->sar_profiles[0], + flags & IWL_SAR_ENABLE_MSK, + num_chains, num_sub_bands); out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table); -int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) +int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *data; bool enabled; @@ -707,7 +563,7 @@ int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) &tbl_rev); if (!IS_ERR(wifi_pkg)) { if (tbl_rev != 2) { - ret = PTR_ERR(wifi_pkg); + ret = -EINVAL; goto out_free; } @@ -723,7 +579,7 @@ int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) &tbl_rev); if (!IS_ERR(wifi_pkg)) { if (tbl_rev != 1) { - ret = PTR_ERR(wifi_pkg); + ret = -EINVAL; goto out_free; } @@ -739,7 +595,7 @@ int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) &tbl_rev); if (!IS_ERR(wifi_pkg)) { if (tbl_rev != 0) { - ret = PTR_ERR(wifi_pkg); + ret = -EINVAL; goto out_free; } @@ -767,7 +623,7 @@ read_table: * 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) { + if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) { ret = -EINVAL; goto out_free; } @@ -776,13 +632,15 @@ read_table: pos = 3; for (i = 0; i < n_profiles; i++) { + union acpi_object *table = &wifi_pkg->package.elements[pos]; /* 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_sar_set_profile(&wifi_pkg->package.elements[pos], - &fwrt->sar_profiles[i + 1], enabled, - num_chains, num_sub_bands); + ret = iwl_acpi_sar_set_profile(table, + &fwrt->sar_profiles[i + 1], + enabled, num_chains, + num_sub_bands); if (ret < 0) break; @@ -794,9 +652,8 @@ out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table); -int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) +int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *data; int i, j, k, ret, tbl_rev; @@ -811,7 +668,7 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) .revisions = BIT(3), .bands = ACPI_GEO_NUM_BANDS_REV2, .profiles = ACPI_NUM_GEO_PROFILES_REV3, - .min_profiles = 3, + .min_profiles = BIOS_GEO_MIN_PROFILE_NUM, }, { .revisions = BIT(2), @@ -897,7 +754,7 @@ int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) read_table: fwrt->geo_rev = tbl_rev; for (i = 0; i < num_profiles; i++) { - for (j = 0; j < ACPI_GEO_NUM_BANDS_REV2; j++) { + for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) { union acpi_object *entry; /* @@ -921,7 +778,7 @@ read_table: entry->integer.value; } - for (k = 0; k < ACPI_GEO_NUM_CHAINS; k++) { + for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) { /* same here as above */ if (j >= num_bands) { fwrt->geo_profiles[i].bands[j].chains[k] = @@ -949,151 +806,26 @@ out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table); - -bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) -{ - /* - * The PER_CHAIN_LIMIT_OFFSET_CMD 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(fwrt->fw->ucode_ver) >= 38 || - (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 && - fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) || - (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 && - ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == - CSR_HW_REV_TYPE_7265D)); -} -IWL_EXPORT_SYMBOL(iwl_sar_geo_support); - -int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt, - struct iwl_per_chain_offset *table, - u32 n_bands, u32 n_profiles) -{ - int i, j; - - if (!fwrt->geo_enabled) - return -ENODATA; - - if (!iwl_sar_geo_support(fwrt)) - return -EOPNOTSUPP; - - for (i = 0; i < n_profiles; i++) { - for (j = 0; j < n_bands; j++) { - struct iwl_per_chain_offset *chain = - &table[i * n_bands + j]; - - chain->max_tx_power = - cpu_to_le16(fwrt->geo_profiles[i].bands[j].max); - chain->chain_a = fwrt->geo_profiles[i].bands[j].chains[0]; - chain->chain_b = fwrt->geo_profiles[i].bands[j].chains[1]; - IWL_DEBUG_RADIO(fwrt, - "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", - i, j, - fwrt->geo_profiles[i].bands[j].chains[0], - fwrt->geo_profiles[i].bands[j].chains[1], - fwrt->geo_profiles[i].bands[j].max); - } - } - - return 0; -} -IWL_EXPORT_SYMBOL(iwl_sar_geo_init); - -__le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) -{ - int ret; - u8 value; - u32 val; - __le32 config_bitmap = 0; - - /* - * Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2'. - * Setting config_bitmap Indonesia bit is valid only for HR/JF. - */ - switch (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)) { - case IWL_CFG_RF_TYPE_HR1: - case IWL_CFG_RF_TYPE_HR2: - case IWL_CFG_RF_TYPE_JF1: - case IWL_CFG_RF_TYPE_JF2: - ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, - DSM_FUNC_ENABLE_INDONESIA_5G2, - &iwl_guid, &value); - - if (!ret && value == DSM_VALUE_INDONESIA_ENABLE) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); - break; - default: - break; - } - - /* - ** Evaluate func 'DSM_FUNC_DISABLE_SRD' - */ - ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, - DSM_FUNC_DISABLE_SRD, - &iwl_guid, &value); - if (!ret) { - if (value == DSM_VALUE_SRD_PASSIVE) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK); - else if (value == DSM_VALUE_SRD_DISABLE) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK); - } - - if (fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT)) { - /* - ** Evaluate func 'DSM_FUNC_REGULATORY_CONFIG' - */ - ret = iwl_acpi_get_dsm_u32(fwrt->dev, 0, - DSM_FUNC_REGULATORY_CONFIG, - &iwl_guid, &val); - /* - * China 2022 enable if the BIOS object does not exist or - * if it is enabled in BIOS. - */ - if (ret < 0 || val & DSM_MASK_CHINA_22_REG) - config_bitmap |= - cpu_to_le32(LARI_CONFIG_ENABLE_CHINA_22_REG_SUPPORT_MSK); - } - - return config_bitmap; -} -IWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap); int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) { union acpi_object *wifi_pkg, *data, *flags; int i, j, ret, tbl_rev, num_sub_bands = 0; int idx = 2; - u8 cmd_ver; - - fwrt->ppag_flags = 0; - fwrt->ppag_table_valid = false; data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - /* try to read ppag table rev 2 or 1 (both have the same data size) */ + /* try to read ppag table rev 3, 2 or 1 (all have the same data size) */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); if (!IS_ERR(wifi_pkg)) { - if (tbl_rev == 1 || tbl_rev == 2) { + if (tbl_rev >= 1 && tbl_rev <= 3) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; IWL_DEBUG_RADIO(fwrt, - "Reading PPAG table v2 (tbl_rev=%d)\n", + "Reading PPAG table (tbl_rev=%d)\n", tbl_rev); goto read_table; } else { @@ -1116,6 +848,9 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto read_table; } + ret = PTR_ERR(wifi_pkg); + goto out_free; + read_table: fwrt->ppag_ver = tbl_rev; flags = &wifi_pkg->package.elements[1]; @@ -1125,19 +860,8 @@ read_table: goto out_free; } - fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK; - cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, - WIDE_ID(PHY_OPS_GROUP, - PER_PLATFORM_ANT_GAIN_CMD), - IWL_FW_CMD_VER_UNKNOWN); - if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) { - ret = -EINVAL; - goto out_free; - } - if (!fwrt->ppag_flags && cmd_ver <= 3) { - ret = 0; - goto out_free; - } + fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value, + fwrt->ppag_ver); /* * read, verify gain values and save them into the PPAG table. @@ -1155,132 +879,15 @@ read_table: } fwrt->ppag_chains[i].subbands[j] = ent->integer.value; - /* from ver 4 the fw deals with out of range values */ - if (cmd_ver >= 4) - continue; - if ((j == 0 && - (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB || - fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) || - (j != 0 && - (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB || - fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) { - ret = -EINVAL; - goto out_free; - } } } - fwrt->ppag_table_valid = true; ret = 0; out_free: kfree(data); return ret; } -IWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table); - -int iwl_read_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; - - /* many firmware images for JF lie about this */ - if (CSR_HW_RFID_TYPE(fwrt->trans->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), - IWL_FW_CMD_VER_UNKNOWN); - if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) { - IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); - return -EINVAL; - } - - /* 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(fwrt->ppag_flags); - - 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); - if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) { - /* in this case FW supports revision 0 */ - IWL_DEBUG_RADIO(fwrt, - "PPAG table rev is %d, send truncated table\n", - fwrt->ppag_ver); - } - } else if (cmd_ver >= 2 && cmd_ver <= 4) { - num_sub_bands = IWL_NUM_SUB_BANDS_V2; - gain = cmd->v2.gain[0]; - *cmd_size = sizeof(cmd->v2); - if (fwrt->ppag_ver == 0) { - /* in this case FW supports revisions 1 or 2 */ - IWL_DEBUG_RADIO(fwrt, - "PPAG table rev is 0, send padded table\n"); - } - } 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", - cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK)); - if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || - (cmd_ver == 2 && fwrt->ppag_ver == 2)) { - 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"); - } - - IWL_DEBUG_RADIO(fwrt, - "PPAG MODE bits going to be sent: %d\n", - cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK)); - - for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { - for (j = 0; j < num_sub_bands; j++) { - 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; -} -IWL_EXPORT_SYMBOL(iwl_read_ppag_table); - -bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) -{ - - if (!dmi_check_system(dmi_ppag_approved_list)) { - IWL_DEBUG_RADIO(fwrt, - "System vendor '%s' is not in the approved list, disabling PPAG.\n", - dmi_get_system_info(DMI_SYS_VENDOR)); - fwrt->ppag_flags = 0; - return false; - } - - return true; -} -IWL_EXPORT_SYMBOL(iwl_acpi_is_ppag_approved); void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, struct iwl_phy_specific_cfg *filters) @@ -1293,7 +900,6 @@ void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, if (IS_ERR(data)) return; - /* try to read wtas table revision 1 or revision 0*/ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_WPFC_WIFI_DATA_SIZE, &tbl_rev); @@ -1303,13 +909,14 @@ void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, if (tbl_rev != 0) goto out_free; - BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) != ACPI_WPFC_WIFI_DATA_SIZE); + BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) != + ACPI_WPFC_WIFI_DATA_SIZE - 1); for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) { - if (wifi_pkg->package.elements[i].type != ACPI_TYPE_INTEGER) - return; + if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER) + goto out_free; tmp.filter_cfg_chains[i] = - cpu_to_le32(wifi_pkg->package.elements[i].integer.value); + cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value); } IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n"); @@ -1318,3 +925,38 @@ out_free: kfree(data); } IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters); + +void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) +{ + union acpi_object *wifi_pkg, *data; + int tbl_rev; + + data = iwl_acpi_get_object(fwrt->dev, ACPI_GLAI_METHOD); + if (IS_ERR(data)) + return; + + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_GLAI_WIFI_DATA_SIZE, + &tbl_rev); + if (IS_ERR(wifi_pkg)) + goto out_free; + + if (tbl_rev != 0) { + IWL_DEBUG_RADIO(fwrt, "Invalid GLAI revision: %d\n", tbl_rev); + goto out_free; + } + + if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER || + wifi_pkg->package.elements[1].integer.value > ACPI_GLAI_MAX_STATUS) + goto out_free; + + fwrt->uefi_tables_lock_status = + wifi_pkg->package.elements[1].integer.value; + + IWL_DEBUG_RADIO(fwrt, + "Loaded UEFI WIFI GUID lock status: %d from ACPI\n", + fwrt->uefi_tables_lock_status); +out_free: + kfree(data); +} +IWL_EXPORT_SYMBOL(iwl_acpi_get_guid_lock_status); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index e9277f6f3582..1d32b82f73db 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -7,6 +7,7 @@ #define __iwl_fw_acpi__ #include <linux/acpi.h> +#include "fw/regulatory.h" #include "fw/api/commands.h" #include "fw/api/power.h" #include "fw/api/phy.h" @@ -25,6 +26,7 @@ #define ACPI_PPAG_METHOD "PPAG" #define ACPI_WTAS_METHOD "WTAS" #define ACPI_WPFC_METHOD "WPFC" +#define ACPI_GLAI_METHOD "GLAI" #define ACPI_WIFI_DOMAIN (0x07) @@ -56,187 +58,90 @@ #define ACPI_EWRD_WIFI_DATA_SIZE_REV2 ((ACPI_SAR_PROFILE_NUM - 1) * \ ACPI_SAR_NUM_CHAINS_REV2 * \ ACPI_SAR_NUM_SUB_BANDS_REV2 + 3) -#define ACPI_WPFC_WIFI_DATA_SIZE 4 /* 4 filter config words */ +#define ACPI_WPFC_WIFI_DATA_SIZE 5 /* domain and 4 filter config words */ /* revision 0 and 1 are identical, except for the semantics in the FW */ #define ACPI_GEO_NUM_BANDS_REV0 2 #define ACPI_GEO_NUM_BANDS_REV2 3 -#define ACPI_GEO_NUM_CHAINS 2 #define ACPI_WRDD_WIFI_DATA_SIZE 2 #define ACPI_SPLC_WIFI_DATA_SIZE 2 #define ACPI_ECKV_WIFI_DATA_SIZE 2 - +/* + * One element for domain type, + * and one for the status + */ +#define ACPI_GLAI_WIFI_DATA_SIZE 2 +#define ACPI_GLAI_MAX_STATUS 2 /* * TAS size: 1 elelment for type, * 1 element for enabled field, * 1 element for block list size, * 16 elements for block list array */ -#define APCI_WTAS_BLACK_LIST_MAX 16 -#define ACPI_WTAS_WIFI_DATA_SIZE (3 + APCI_WTAS_BLACK_LIST_MAX) -#define ACPI_WTAS_ENABLED_MSK 0x1 -#define ACPI_WTAS_OVERRIDE_IEC_MSK 0x2 -#define ACPI_WTAS_ENABLE_IEC_MSK 0x4 -#define ACPI_WTAS_OVERRIDE_IEC_POS 0x1 -#define ACPI_WTAS_ENABLE_IEC_POS 0x2 -#define ACPI_WTAS_USA_UHB_MSK BIT(16) -#define ACPI_WTAS_USA_UHB_POS 16 - +#define ACPI_WTAS_WIFI_DATA_SIZE (3 + IWL_WTAS_BLACK_LIST_MAX) #define ACPI_PPAG_WIFI_DATA_SIZE_V1 ((IWL_NUM_CHAIN_LIMITS * \ IWL_NUM_SUB_BANDS_V1) + 2) #define ACPI_PPAG_WIFI_DATA_SIZE_V2 ((IWL_NUM_CHAIN_LIMITS * \ IWL_NUM_SUB_BANDS_V2) + 2) -/* PPAG gain value bounds in 1/8 dBm */ -#define ACPI_PPAG_MIN_LB -16 -#define ACPI_PPAG_MAX_LB 24 -#define ACPI_PPAG_MIN_HB -16 -#define ACPI_PPAG_MAX_HB 40 -#define ACPI_PPAG_MASK 3 -#define IWL_PPAG_ETSI_MASK BIT(0) - #define IWL_SAR_ENABLE_MSK BIT(0) #define IWL_REDUCE_POWER_FLAGS_POS 1 -/* - * The profile for revision 2 is a superset of revision 1, which is in - * turn a superset of revision 0. So we can store all revisions - * inside revision 2, which is what we represent here. - */ -struct iwl_sar_profile_chain { - u8 subbands[ACPI_SAR_NUM_SUB_BANDS_REV2]; -}; - -struct iwl_sar_profile { - bool enabled; - struct iwl_sar_profile_chain chains[ACPI_SAR_NUM_CHAINS_REV2]; -}; - -/* Same thing as with SAR, all revisions fit in revision 2 */ -struct iwl_geo_profile_band { - u8 max; - u8 chains[ACPI_GEO_NUM_CHAINS]; -}; - -struct iwl_geo_profile { - struct iwl_geo_profile_band bands[ACPI_GEO_NUM_BANDS_REV2]; -}; - -/* Same thing as with SAR, all revisions fit in revision 2 */ -struct iwl_ppag_chain { - s8 subbands[ACPI_SAR_NUM_SUB_BANDS_REV2]; -}; - -enum iwl_dsm_funcs_rev_0 { - DSM_FUNC_QUERY = 0, - DSM_FUNC_DISABLE_SRD = 1, - DSM_FUNC_ENABLE_INDONESIA_5G2 = 2, - DSM_FUNC_ENABLE_6E = 3, - DSM_FUNC_REGULATORY_CONFIG = 4, - DSM_FUNC_11AX_ENABLEMENT = 6, - DSM_FUNC_ENABLE_UNII4_CHAN = 7, - DSM_FUNC_ACTIVATE_CHANNEL = 8, - DSM_FUNC_FORCE_DISABLE_CHANNELS = 9, - DSM_FUNC_ENERGY_DETECTION_THRESHOLD = 10, -}; - -enum iwl_dsm_values_srd { - DSM_VALUE_SRD_ACTIVE, - DSM_VALUE_SRD_PASSIVE, - DSM_VALUE_SRD_DISABLE, - DSM_VALUE_SRD_MAX -}; - -enum iwl_dsm_values_indonesia { - DSM_VALUE_INDONESIA_DISABLE, - DSM_VALUE_INDONESIA_ENABLE, - DSM_VALUE_INDONESIA_RESERVED, - DSM_VALUE_INDONESIA_MAX -}; - -/* DSM RFI uses a different GUID, so need separate definitions */ - -#define DSM_RFI_FUNC_ENABLE 3 - -enum iwl_dsm_values_rfi { - DSM_VALUE_RFI_ENABLE, - DSM_VALUE_RFI_DISABLE, - DSM_VALUE_RFI_MAX -}; - -enum iwl_dsm_masks_reg { - DSM_MASK_CHINA_22_REG = BIT(2) -}; +/* The Inidcator whether UEFI WIFI GUID tables are locked is read from ACPI */ +#define UEFI_WIFI_GUID_UNLOCKED 0 + +#define ACPI_DSM_REV 0 #ifdef CONFIG_ACPI struct iwl_fw_runtime; extern const guid_t iwl_guid; -extern const guid_t iwl_rfi_guid; - -int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, - const guid_t *guid, u8 *value); - -int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, - const guid_t *guid, u32 *value); /** * iwl_acpi_get_mcc - read MCC from ACPI, if available * - * @dev: the struct device + * @fwrt: the fw runtime struct * @mcc: output buffer (3 bytes) that will get the MCC * * This function tries to read the current MCC from ACPI if available. */ -int iwl_acpi_get_mcc(struct device *dev, char *mcc); +int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); -u64 iwl_acpi_get_pwr_limit(struct device *dev); +int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit); /* * iwl_acpi_get_eckv - read external clock validation from ACPI, if available * - * @dev: the struct device + * @fwrt: the fw runtime struct * @extl_clk: output var (2 bytes) that will get the clk indication. * * This function tries to read the external clock indication * from ACPI if available. */ -int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk); - -int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_tables, u32 n_subbands, - int prof_a, int prof_b); +int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk); -int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt); +int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt); -int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt); +int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt); -int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt); +int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt); -bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); - -int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt, - struct iwl_per_chain_offset *table, - u32 n_bands, u32 n_profiles); - -int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver); - -__le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); +int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data); int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt); -int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd, - int *cmd_size); - -bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt); - void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, struct iwl_phy_specific_cfg *filters); +void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt); + +int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs func, u32 *value); + #else /* CONFIG_ACPI */ static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev, @@ -245,92 +150,61 @@ static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev, return ERR_PTR(-ENOENT); } -static inline int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, - const guid_t *guid, u8 *value) -{ - return -ENOENT; -} - -static inline int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, - const guid_t *guid, u32 *value) -{ - return -ENOENT; -} - -static inline int iwl_acpi_get_mcc(struct device *dev, char *mcc) +static inline int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) { return -ENOENT; } -static inline u64 iwl_acpi_get_pwr_limit(struct device *dev) +static inline int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit) { + *dflt_pwr_limit = 0; return 0; } -static inline int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) +static inline int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) { return -ENOENT; } -static inline int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, - __le16 *per_chain, u32 n_tables, u32 n_subbands, - int prof_a, int prof_b) +static inline int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; } -static inline int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) +static inline int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; } -static inline int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) -{ - return -ENOENT; -} - -static inline int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) +static inline int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt) { return 1; } -static inline bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) -{ - return false; -} - -static inline int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, - union iwl_tas_config_cmd *cmd, int fw_ver) +static inline int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data) { return -ENOENT; } -static inline __le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) -{ - return 0; -} - static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) { return -ENOENT; } -static inline int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, - union iwl_ppag_table_cmd *cmd, int *cmd_size) -{ - return -ENOENT; -} +/* macro since the second argument doesn't always exist */ +#define iwl_acpi_get_phy_filters(fwrt, filters) do { } while (0) -static inline bool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) +static inline void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) { - return false; } -static inline void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, - struct iwl_phy_specific_cfg *filters) +static inline int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs func, u32 *value) { + return -ENOENT; } - #endif /* CONFIG_ACPI */ #endif /* __iwl_fw_acpi__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h index 3e81e9369224..bc27e15488f5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/coex.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* + * Copyright (C) 2023 Intel Corporation * Copyright (C) 2013-2014, 2018-2019 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH @@ -170,7 +171,11 @@ enum iwl_bt_ci_compliance { * @bt_activity_grading: the activity of BT &enum iwl_bt_activity_grading * @ttc_status: is TTC enabled - one bit per PHY * @rrc_status: is RRC enabled - one bit per PHY - * @reserved: reserved + * The following fields are only for version 5, and are reserved in version 4: + * @wifi_loss_low_rssi: The predicted lost WiFi rate (% of air time that BT is + * utilizing) when the RSSI is low (<= -65 dBm) + * @wifi_loss_mid_high_rssi: The predicted lost WiFi rate (% of air time that + * BT is utilizing) when the RSSI is mid/high (>= -65 dBm) */ struct iwl_bt_coex_profile_notif { __le32 mbox_msg[4]; @@ -182,7 +187,10 @@ struct iwl_bt_coex_profile_notif { __le32 bt_activity_grading; u8 ttc_status; u8 rrc_status; - __le16 reserved; -} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */ + u8 wifi_loss_low_rssi; + u8 wifi_loss_mid_high_rssi; +} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 + * BT_COEX_PROFILE_NTFY_API_S_VER_5 + */ #endif /* __iwl_fw_api_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index ea99d41040d2..d2a74beed3a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -324,7 +324,7 @@ struct iwl_wowlan_patterns_cmd { u8 n_patterns; /** - * @n_patterns: sta_id + * @sta_id: sta_id */ u8 sta_id; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 751b596ea1a5..0f7903c5a4df 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -101,7 +101,7 @@ enum iwl_data_path_subcmd_ids { RX_NO_DATA_NOTIF = 0xF5, /** - * @THERMAL_DUAL_CHAIN_DISABLE_REQ: firmware request for SMPS mode, + * @THERMAL_DUAL_CHAIN_REQUEST: firmware request for SMPS mode, * &struct iwl_thermal_dual_chain_request */ THERMAL_DUAL_CHAIN_REQUEST = 0xF6, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 394747deb269..47c914de2992 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef __iwl_fw_dbg_tlv_h__ #define __iwl_fw_dbg_tlv_h__ @@ -319,7 +319,7 @@ struct iwl_fw_ini_conf_set_tlv { * @IWL_FW_INI_CONFIG_SET_TYPE_CSR: for CSR configuration * @IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: for DBGC_DRAM_ADDR configuration * @IWL_FW_INI_CONFIG_SET_TYPE_PERIPH_SCRATCH_HWM: for PERIPH SCRATCH HWM configuration - * @IWL_FW_INI_ALLOCATION_NUM: max number of configuration supported + * @IWL_FW_INI_CONFIG_SET_TYPE_MAX_NUM: max number of configuration supported */ enum iwl_fw_ini_config_set_type { @@ -360,6 +360,7 @@ enum iwl_fw_ini_allocation_id { * @IWL_FW_INI_LOCATION_SRAM_PATH: SRAM location * @IWL_FW_INI_LOCATION_DRAM_PATH: DRAM location * @IWL_FW_INI_LOCATION_NPK_PATH: NPK location + * @IWL_FW_INI_LOCATION_NUM: number of valid locations */ enum iwl_fw_ini_buffer_location { IWL_FW_INI_LOCATION_INVALID, @@ -439,6 +440,7 @@ enum iwl_fw_ini_region_device_memory_subtype { * Hard coded time points in which the driver can send hcmd or perform dump * collection * + * @IWL_FW_INI_TIME_POINT_INVALID: invalid timepoint * @IWL_FW_INI_TIME_POINT_EARLY: pre loading the FW * @IWL_FW_INI_TIME_POINT_AFTER_ALIVE: first cmd from host after alive notif * @IWL_FW_INI_TIME_POINT_POST_INIT: last cmd in series of init sequence @@ -553,7 +555,7 @@ enum iwl_fw_ini_dump_policy { * enum iwl_fw_ini_dump_type - Determines dump type based on size defined by FW. * * @IWL_FW_INI_DUMP_BRIEF : only dump the most important regions - * @IWL_FW_INI_DEBUG_MEDIUM: dump more regions than "brief", but not all regions + * @IWL_FW_INI_DUMP_MEDIUM: dump more regions than "brief", but not all regions * @IWL_FW_INI_DUMP_VERBOSE : dump all regions */ enum iwl_fw_ini_dump_type { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index 7b18e098b125..b31ae6889bd0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -60,6 +60,12 @@ enum iwl_debug_cmds { */ FW_DUMP_COMPLETE_CMD = 0xB, /** + * @FW_CLEAR_BUFFER: + * clears the firmware's internal buffer + * no payload + */ + FW_CLEAR_BUFFER = 0xD, + /** * @MFU_ASSERT_DUMP_NTF: * &struct iwl_mfu_assert_dump_notif */ @@ -388,7 +394,7 @@ struct iwl_buf_alloc_cmd { * * @first_word: magic word value * @second_word: magic word value - * @framfrags: DRAM fragmentaion detail + * @dram_frags: DRAM fragmentaion detail */ struct iwl_dram_info { __le32 first_word; @@ -531,7 +537,7 @@ enum iwl_fw_dbg_config_cmd_type { }; /* LDBG_CFG_CMD_TYPE_API_E_VER_1 */ /* this token disables debug asserts in the firmware */ -#define IWL_FW_DBG_CONFIG_TOKEN 0x00011301 +#define IWL_FW_DBG_CONFIG_TOKEN 0x00010001 /** * struct iwl_fw_dbg_config_cmd - configure FW debug diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h index b044990c7b87..25530a29317e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h @@ -630,6 +630,7 @@ enum iwl_location_frame_format { * @IWL_LOCATION_BW_20MHZ: 20MHz * @IWL_LOCATION_BW_40MHZ: 40MHz * @IWL_LOCATION_BW_80MHZ: 80MHz + * @IWL_LOCATION_BW_160MHZ: 160MHz */ enum iwl_location_bw { IWL_LOCATION_BW_20MHZ, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index f15e6d64c298..c6d1f5644638 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -242,9 +242,9 @@ struct iwl_mac_low_latency_cmd { * @esr_transition_timeout: the timeout required by the AP for the * eSR transition. * Available only from version 2 of the command. - * This values comes from the EMLSR transition delay in the EML + * This value comes from the EMLSR transition delay in the EML * Capabilities subfield. - * @medium_sync_delay: the value as it appeasr in P802.11be_D2.2 Figure 9-1002j. + * @medium_sync_delay: the value as it appears in P802.11be_D2.2 Figure 9-1002j. * @assoc_id: unique ID assigned by the AP during association * @reserved1: alignment * @data_policy: see &enum iwl_mac_data_policy @@ -317,7 +317,6 @@ enum iwl_mac_config_filter_flags { * If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0 * len delim to determine if AGG or single. * @client: client mac data - * @go_ibss: mac data for go or ibss * @p2p_dev: mac data for p2p device */ struct iwl_mac_config_cmd { @@ -374,7 +373,7 @@ struct iwl_mac_config_cmd { * iwl_link_ctx_cfg_cmd::bss_color_disable * @LINK_CONTEXT_MODIFY_EHT_PARAMS: covers iwl_link_ctx_cfg_cmd::puncture_mask. * This flag can be set only if the MAC that this link relates to has - * eht_support set to true. + * eht_support set to true. No longer used since _VER_3 of this command. * @LINK_CONTEXT_MODIFY_ALL: set all above flags */ enum iwl_link_ctx_modify_flags { @@ -447,6 +446,7 @@ enum iwl_link_ctx_flags { * @listen_lmac: indicates whether the link should be allocated on the Listen * Lmac or on the Main Lmac. Cannot be changed on an active Link. * Relevant only for eSR. + * @reserved1: in version 2, listen_lmac became reserved * @cck_rates: basic rates available for CCK * @ofdm_rates: basic rates available for OFDM * @cck_short_preamble: 1 for enabling short preamble, 0 otherwise @@ -462,7 +462,7 @@ enum iwl_link_ctx_flags { * @bi: beacon interval in TU, applicable only when associated * @dtim_interval: DTIM interval in TU. * Relevant only for GO, otherwise this is offloaded. - * @puncture_mask: puncture mask for EHT + * @puncture_mask: puncture mask for EHT (removed in VER_3) * @frame_time_rts_th: HE duration RTS threshold, in units of 32us * @flags: a combination from &enum iwl_link_ctx_flags * @flags_mask: what of %flags have changed. Also &enum iwl_link_ctx_flags @@ -472,10 +472,10 @@ enum iwl_link_ctx_flags { * @bssid_index: index of the associated VAP * @bss_color: 11ax AP ID that is used in the HE SIG-A to mark inter BSS frame * @spec_link_id: link_id as the AP knows it - * @reserved: alignment + * @reserved2: alignment * @ibss_bssid_addr: bssid for ibss * @reserved_for_ibss_bssid_addr: reserved - * @reserved1: reserved for future use + * @reserved3: reserved for future use */ struct iwl_link_config_cmd { __le32 action; @@ -486,7 +486,10 @@ struct iwl_link_config_cmd { __le16 reserved_for_local_link_addr; __le32 modify_mask; __le32 active; - __le32 listen_lmac; + union { + __le32 listen_lmac; + __le32 reserved1; + }; __le32 cck_rates; __le32 ofdm_rates; __le32 cck_short_preamble; @@ -502,7 +505,7 @@ struct iwl_link_config_cmd { struct iwl_he_backoff_conf trig_based_txf[AC_NUM]; __le32 bi; __le32 dtim_interval; - __le16 puncture_mask; + __le16 puncture_mask; /* removed in _VER_3 */ __le16 frame_time_rts_th; __le32 flags; __le32 flags_mask; @@ -512,11 +515,11 @@ struct iwl_link_config_cmd { u8 bssid_index; u8 bss_color; u8 spec_link_id; - u8 reserved; + u8 reserved2; u8 ibss_bssid_addr[6]; __le16 reserved_for_ibss_bssid_addr; - __le32 reserved1[8]; -} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1 */ + __le32 reserved3[8]; +} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1, _VER_2, _VER_3 */ /* Currently FW supports link ids in the range 0-3 and can have * at most two active links for each vif. diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 55882190251c..545826973a80 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022, 2024 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_mac_h__ @@ -431,8 +431,8 @@ enum iwl_he_pkt_ext_constellations { }; #define MAX_HE_SUPP_NSS 2 -#define MAX_CHANNEL_BW_INDX_API_D_VER_2 4 -#define MAX_CHANNEL_BW_INDX_API_D_VER_3 5 +#define MAX_CHANNEL_BW_INDX_API_D_VER_1 4 +#define MAX_CHANNEL_BW_INDX_API_D_VER_2 5 /** * struct iwl_he_pkt_ext_v1 - QAM thresholds @@ -455,7 +455,7 @@ enum iwl_he_pkt_ext_constellations { * (0-low_th, 1-high_th) */ struct iwl_he_pkt_ext_v1 { - u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_2][2]; + u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_1][2]; } __packed; /* PKT_EXT_DOT11AX_API_S_VER_1 */ /** @@ -480,7 +480,7 @@ struct iwl_he_pkt_ext_v1 { * (0-low_th, 1-high_th) */ struct iwl_he_pkt_ext_v2 { - u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_3][2]; + u8 pkt_ext_qam_th[MAX_HE_SUPP_NSS][MAX_CHANNEL_BW_INDX_API_D_VER_2][2]; } __packed; /* PKT_EXT_DOT11AX_API_S_VER_2 */ /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h index dfe0bebabc81..58034dfa7e70 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/nvm-reg.h @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_nvm_reg_h__ #define __iwl_fw_api_nvm_reg_h__ +#include "fw/regulatory.h" /** * enum iwl_regulatory_and_nvm_subcmd_ids - regulatory/NVM commands */ @@ -269,6 +270,9 @@ struct iwl_nvm_access_complete_cmd { __le32 reserved; } __packed; /* NVM_ACCESS_COMPLETE_CMD_API_S_VER_1 */ +#define IWL_MCC_US 0x5553 +#define IWL_MCC_CANADA 0x4341 + /** * struct iwl_mcc_update_cmd - Request the device to update geographic * regulatory profile according to the given MCC (Mobile Country Code). @@ -435,36 +439,30 @@ enum iwl_mcc_source { MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, }; -#define IWL_TAS_BLOCK_LIST_MAX 16 /** - * struct iwl_tas_config_cmd_v2 - configures the TAS + * struct iwl_tas_config_cmd_common - configures the TAS. + * This is also the v2 structure. * @block_list_size: size of relevant field in block_list_array * @block_list_array: list of countries where TAS must be disabled */ -struct iwl_tas_config_cmd_v2 { +struct iwl_tas_config_cmd_common { __le32 block_list_size; - __le32 block_list_array[IWL_TAS_BLOCK_LIST_MAX]; + __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; } __packed; /* TAS_CONFIG_CMD_API_S_VER_2 */ /** * struct iwl_tas_config_cmd_v3 - configures the TAS - * @block_list_size: size of relevant field in block_list_array - * @block_list_array: list of countries where TAS must be disabled * @override_tas_iec: indicates whether to override default value of IEC regulatory * @enable_tas_iec: in case override_tas_iec is set - * indicates whether IEC regulatory is enabled or disabled */ struct iwl_tas_config_cmd_v3 { - __le32 block_list_size; - __le32 block_list_array[IWL_TAS_BLOCK_LIST_MAX]; __le16 override_tas_iec; __le16 enable_tas_iec; } __packed; /* TAS_CONFIG_CMD_API_S_VER_3 */ /** - * struct iwl_tas_config_cmd_v3 - configures the TAS - * @block_list_size: size of relevant field in block_list_array - * @block_list_array: list of countries where TAS must be disabled + * struct iwl_tas_config_cmd_v4 - configures the TAS * @override_tas_iec: indicates whether to override default value of IEC regulatory * @enable_tas_iec: in case override_tas_iec is set - * indicates whether IEC regulatory is enabled or disabled @@ -472,19 +470,20 @@ struct iwl_tas_config_cmd_v3 { * @reserved: reserved */ struct iwl_tas_config_cmd_v4 { - __le32 block_list_size; - __le32 block_list_array[IWL_TAS_BLOCK_LIST_MAX]; u8 override_tas_iec; u8 enable_tas_iec; u8 usa_tas_uhb_allowed; u8 reserved; } __packed; /* TAS_CONFIG_CMD_API_S_VER_4 */ -union iwl_tas_config_cmd { - struct iwl_tas_config_cmd_v2 v2; - struct iwl_tas_config_cmd_v3 v3; - struct iwl_tas_config_cmd_v4 v4; +struct iwl_tas_config_cmd { + struct iwl_tas_config_cmd_common common; + union { + struct iwl_tas_config_cmd_v3 v3; + struct iwl_tas_config_cmd_v4 v4; + }; }; + /** * enum iwl_lari_config_masks - bit masks for the various LARI config operations * @LARI_CONFIG_DISABLE_11AC_UKRAINE_MSK: disable 11ac in ukraine diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h index 306ed88de463..08a2c416ce60 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h @@ -142,6 +142,8 @@ struct iwl_phy_context_cmd_v1 { * @lmac_id: the lmac id the phy context belongs to * @ci: channel info * @rxchain_info: ??? + * @sbb_bandwidth: 0 disabled, 1 - 40Mhz ... 4 - 320MHz + * @sbb_ctrl_channel_loc: location of the control channel * @dsp_cfg_flags: set to 0 * @reserved: reserved to align to 64 bit */ @@ -152,9 +154,20 @@ struct iwl_phy_context_cmd { /* PHY_CONTEXT_DATA_API_S_VER_3, PHY_CONTEXT_DATA_API_S_VER_4 */ struct iwl_fw_channel_info ci; __le32 lmac_id; - __le32 rxchain_info; /* reserved in _VER_4 */ + union { + __le32 rxchain_info; /* reserved in _VER_4 */ + struct { /* used for _VER_5/_VER_6 */ + u8 sbb_bandwidth; + u8 sbb_ctrl_channel_loc; + __le16 puncture_mask; /* added in VER_6 */ + }; + }; __le32 dsp_cfg_flags; __le32 reserved; -} __packed; /* PHY_CONTEXT_CMD_API_VER_3, PHY_CONTEXT_CMD_API_VER_4 */ +} __packed; /* PHY_CONTEXT_CMD_API_VER_3, + * PHY_CONTEXT_CMD_API_VER_4, + * PHY_CONTEXT_CMD_API_VER_5, + * PHY_CONTEXT_CMD_API_VER_6 + */ #endif /* __iwl_fw_api_phy_ctxt_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 040d83fa5424..0bf38243f88a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -506,13 +506,40 @@ struct iwl_geo_tx_power_profiles_resp { } __packed; /* PER_CHAIN_LIMIT_OFFSET_RSP */ /** + * enum iwl_ppag_flags - PPAG enable masks + * @IWL_PPAG_ETSI_MASK: enable PPAG in ETSI + * @IWL_PPAG_CHINA_MASK: enable PPAG in China + * @IWL_PPAG_ETSI_LPI_UHB_MASK: enable LPI in ETSI for UHB + * @IWL_PPAG_ETSI_VLP_UHB_MASK: enable VLP in ETSI for UHB + * @IWL_PPAG_ETSI_SP_UHB_MASK: enable SP in ETSI for UHB + * @IWL_PPAG_USA_LPI_UHB_MASK: enable LPI in USA for UHB + * @IWL_PPAG_USA_VLP_UHB_MASK: enable VLP in USA for UHB + * @IWL_PPAG_USA_SP_UHB_MASK: enable SP in USA for UHB + * @IWL_PPAG_CANADA_LPI_UHB_MASK: enable LPI in CANADA for UHB + * @IWL_PPAG_CANADA_VLP_UHB_MASK: enable VLP in CANADA for UHB + * @IWL_PPAG_CANADA_SP_UHB_MASK: enable SP in CANADA for UHB + */ +enum iwl_ppag_flags { + IWL_PPAG_ETSI_MASK = BIT(0), + IWL_PPAG_CHINA_MASK = BIT(1), + IWL_PPAG_ETSI_LPI_UHB_MASK = BIT(2), + IWL_PPAG_ETSI_VLP_UHB_MASK = BIT(3), + IWL_PPAG_ETSI_SP_UHB_MASK = BIT(4), + IWL_PPAG_USA_LPI_UHB_MASK = BIT(5), + IWL_PPAG_USA_VLP_UHB_MASK = BIT(6), + IWL_PPAG_USA_SP_UHB_MASK = BIT(7), + IWL_PPAG_CANADA_LPI_UHB_MASK = BIT(8), + IWL_PPAG_CANADA_VLP_UHB_MASK = BIT(9), + IWL_PPAG_CANADA_SP_UHB_MASK = BIT(10), +}; + +/** * union iwl_ppag_table_cmd - union for all versions of PPAG command * @v1: version 1 * @v2: version 2 - * - * @flags: bit 0 - indicates enablement of PPAG for ETSI - * bit 1 - indicates enablement of PPAG for CHINA BIOS - * bit 1 can be used only in v3 (identical to v2) + * version 3, 4 and 5 are the same structure as v2, + * but has a different format of the flags bitmap + * @flags: values from &enum iwl_ppag_flags * @gain: table of antenna gain values per chain and sub-band * @reserved: reserved */ @@ -529,6 +556,11 @@ union iwl_ppag_table_cmd { } v2; } __packed; +#define IWL_PPAG_CMD_V4_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK) +#define IWL_PPAG_CMD_V5_MASK (IWL_PPAG_CMD_V4_MASK | \ + IWL_PPAG_ETSI_LPI_UHB_MASK | \ + IWL_PPAG_USA_LPI_UHB_MASK) + #define MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE 26 #define MCC_TO_SAR_OFFSET_TABLE_COL_SIZE 13 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h index d62fed543276..d7f8a276b683 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2021, 2023 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -109,6 +109,7 @@ enum iwl_sta_flags { * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from * station info array (1 - n 1X mode) + * @STA_KEY_FLG_AMSDU_SPP: SPP (signaling and payload protected) A-MSDU * @STA_KEY_FLG_KEYID_MSK: the index of the key * @STA_KEY_FLG_KEYID_POS: key index bit position * @STA_KEY_NOT_VALID: key is invalid @@ -129,6 +130,7 @@ enum iwl_sta_key_flag { STA_KEY_FLG_EN_MSK = (7 << 0), STA_KEY_FLG_WEP_KEY_MAP = BIT(3), + STA_KEY_FLG_AMSDU_SPP = BIT(7), STA_KEY_FLG_KEYID_POS = 8, STA_KEY_FLG_KEYID_MSK = (3 << STA_KEY_FLG_KEYID_POS), STA_KEY_NOT_VALID = BIT(11), diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 842360b1e995..d9e4c75403b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -76,6 +76,8 @@ enum iwl_tx_flags { * to a secured STA * @IWL_TX_FLAGS_HIGH_PRI: high priority frame (like EAPOL) - can affect rate * selection, retry limits and BT kill + * @IWL_TX_FLAGS_RTS: firmware used an RTS + * @IWL_TX_FLAGS_CTS: firmware used CTS-to-self */ enum iwl_tx_cmd_flags { IWL_TX_FLAGS_CMD_RATE = BIT(0), @@ -884,6 +886,7 @@ struct iwl_tx_path_flush_cmd { /** * struct iwl_flush_queue_info - virtual flush queue info + * @tid: the tid to flush * @queue_num: virtual queue id * @read_before_flush: read pointer before flush * @read_after_flush: read pointer after flush @@ -897,6 +900,7 @@ struct iwl_flush_queue_info { /** * struct iwl_tx_path_flush_cmd_rsp -- queue/FIFO flush command response + * @sta_id: the station for which the queue was flushed * @num_flushed_queues: number of queues in queues array * @queues: all flushed queues */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h index 9c69d3674384..e6c0f928a6bb 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/txq.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2019-2021, 2023 Intel Corporation + * Copyright (C) 2005-2014, 2019-2021, 2023-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -66,6 +66,16 @@ enum iwl_gen2_tx_fifo { IWL_GEN2_TRIG_TX_FIFO_VO, }; +enum iwl_bz_tx_fifo { + IWL_BZ_EDCA_TX_FIFO_BK, + IWL_BZ_EDCA_TX_FIFO_BE, + IWL_BZ_EDCA_TX_FIFO_VI, + IWL_BZ_EDCA_TX_FIFO_VO, + IWL_BZ_TRIG_TX_FIFO_BK, + IWL_BZ_TRIG_TX_FIFO_BE, + IWL_BZ_TRIG_TX_FIFO_VI, + IWL_BZ_TRIG_TX_FIFO_VO, +}; /** * enum iwl_tx_queue_cfg_actions - TXQ config options * @TX_QUEUE_CFG_ENABLE_QUEUE: enable a queue diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 3975a53a9f20..db6d7013df66 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -19,7 +19,6 @@ * @fwrt_ptr: pointer to the buffer coming from fwrt * @trans_ptr: pointer to struct %iwl_trans_dump_data which contains the * transport's data. - * @trans_len: length of the valid data in trans_ptr * @fwrt_len: length of the valid data in fwrt_ptr */ struct iwl_fw_dump_ptrs { @@ -880,10 +879,10 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, cpu_to_le32(fwrt->trans->hw_rev_step); memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable, sizeof(dump_info->fw_human_readable)); - strncpy(dump_info->dev_human_readable, fwrt->trans->name, - sizeof(dump_info->dev_human_readable) - 1); - strncpy(dump_info->bus_human_readable, fwrt->dev->bus->name, - sizeof(dump_info->bus_human_readable) - 1); + strscpy_pad(dump_info->dev_human_readable, fwrt->trans->name, + sizeof(dump_info->dev_human_readable)); + strscpy_pad(dump_info->bus_human_readable, fwrt->dev->bus->name, + sizeof(dump_info->bus_human_readable)); dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs; dump_info->lmac_err_id[0] = cpu_to_le32(fwrt->dump.lmac_err_id[0]); @@ -1728,10 +1727,12 @@ iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, /** * mask_apply_and_normalize - applies mask on val and normalize the result * - * The normalization is based on the first set bit in the mask - * * @val: value * @mask: mask to apply and to normalize with + * + * The normalization is based on the first set bit in the mask + * + * Returns: the extracted value */ static u32 mask_apply_and_normalize(u32 val, u32 mask) { @@ -2200,15 +2201,16 @@ struct iwl_dump_ini_mem_ops { }; /** - * iwl_dump_ini_mem - * - * Creates a dump tlv and copy a memory region into it. - * Returns the size of the current dump tlv or 0 if failed + * iwl_dump_ini_mem - dump memory region * * @fwrt: fw runtime struct * @list: list to add the dump tlv to * @reg_data: memory region * @ops: memory dump operations + * + * Creates a dump tlv and copy a memory region into it. + * + * Returns: the size of the current dump tlv or 0 if failed */ static u32 iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, struct list_head *list, struct iwl_dump_ini_region_data *reg_data, @@ -2427,9 +2429,12 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_debug_info_tlv *debug_info = (void *)node->tlv.data; + BUILD_BUG_ON(sizeof(cfg_name->cfg_name) != + sizeof(debug_info->debug_cfg_name)); + cfg_name->image_type = debug_info->image_type; cfg_name->cfg_name_len = - cpu_to_le32(IWL_FW_INI_MAX_CFG_NAME); + cpu_to_le32(sizeof(cfg_name->cfg_name)); memcpy(cfg_name->cfg_name, debug_info->debug_cfg_name, sizeof(cfg_name->cfg_name)); cfg_name++; @@ -2873,7 +2878,8 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n", le32_to_cpu(desc->trig_desc.type)); - schedule_delayed_work(&wk_data->wk, usecs_to_jiffies(delay)); + queue_delayed_work(system_unbound_wq, &wk_data->wk, + usecs_to_jiffies(delay)); return 0; } @@ -3175,7 +3181,9 @@ int iwl_fw_dbg_ini_collect(struct iwl_fw_runtime *fwrt, if (sync) iwl_fw_dbg_collect_sync(fwrt, idx); else - schedule_delayed_work(&fwrt->dump.wks[idx].wk, usecs_to_jiffies(delay)); + queue_delayed_work(system_unbound_wq, + &fwrt->dump.wks[idx].wk, + usecs_to_jiffies(delay)); return 0; } @@ -3395,3 +3403,22 @@ void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt) iwl_trans_send_cmd(fwrt->trans, &hcmd); } IWL_EXPORT_SYMBOL(iwl_fw_disable_dbg_asserts); + +void iwl_fw_dbg_clear_monitor_buf(struct iwl_fw_runtime *fwrt) +{ + struct iwl_fw_dbg_params params = {0}; + + iwl_fw_dbg_stop_sync(fwrt); + + if (fw_has_api(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_API_INT_DBG_BUF_CLEAR)) { + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DEBUG_GROUP, FW_CLEAR_BUFFER), + }; + iwl_trans_send_cmd(fwrt->trans, &hcmd); + } + + iwl_dbg_tlv_init_cfg(fwrt); + iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, false); +} +IWL_EXPORT_SYMBOL(iwl_fw_dbg_clear_monitor_buf); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 66b233250c7c..98d56e778d99 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -306,8 +306,6 @@ static inline void iwl_fw_error_collect(struct iwl_fw_runtime *fwrt, bool sync) _iwl_dbg_tlv_time_point(fwrt, tp_id, NULL, sync); } -void iwl_fw_error_print_fseq_regs(struct iwl_fw_runtime *fwrt); - static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, struct iwl_lmac_alive *lmac, struct iwl_umac_alive *umac) @@ -330,6 +328,7 @@ void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, u32 timepoint, u32 timepoint_data); void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt); +void iwl_fw_dbg_clear_monitor_buf(struct iwl_fw_runtime *fwrt); #define IWL_FW_CHECK_FAILED(_obj, _fmt, ...) \ IWL_ERR_LIMIT(_obj, _fmt, __VA_ARGS__) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index 06d6f7f66430..5c76e3b94968 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2014, 2018-2022 Intel Corporation + * Copyright (C) 2014, 2018-2024 Intel Corporation * Copyright (C) 2014-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -16,7 +16,7 @@ /** * enum iwl_fw_error_dump_type - types of data in the dump file * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0 - * @IWL_FW_ERROR_DUMP_RXF: + * @IWL_FW_ERROR_DUMP_RXF: RX FIFO contents * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as * &struct iwl_fw_error_dump_txcmd packets * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info @@ -24,21 +24,24 @@ * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several * sections like this in a single file. + * @IWL_FW_ERROR_DUMP_TXF: TX FIFO contents * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers * @IWL_FW_ERROR_DUMP_MEM: chunk of memory * @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump. * Structured as &struct iwl_fw_error_dump_trigger_desc. * @IWL_FW_ERROR_DUMP_RB: the content of an RB structured as * &struct iwl_fw_error_dump_rb - * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were + * @IWL_FW_ERROR_DUMP_PAGING: UMAC's image memory segments which were * paged to the DRAM. * @IWL_FW_ERROR_DUMP_RADIO_REG: Dump the radio registers. + * @IWL_FW_ERROR_DUMP_INTERNAL_TXF: internal TX FIFO data * @IWL_FW_ERROR_DUMP_EXTERNAL: used only by external code utilities, and * for that reason is not in use in any other place in the Linux Wi-Fi * stack. * @IWL_FW_ERROR_DUMP_MEM_CFG: the addresses and sizes of fifos in the smem, * which we get from the fw after ALIVE. The content is structured as * &struct iwl_fw_error_dump_smem_cfg. + * @IWL_FW_ERROR_DUMP_D3_DEBUG_DATA: D3 debug data */ enum iwl_fw_error_dump_type { /* 0 is deprecated */ @@ -59,8 +62,6 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_EXTERNAL = 15, /* Do not move */ IWL_FW_ERROR_DUMP_MEM_CFG = 16, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA = 17, - - IWL_FW_ERROR_DUMP_MAX, }; /** @@ -442,7 +443,7 @@ struct iwl_fw_ini_err_table_dump { * struct iwl_fw_error_dump_rb - content of an Receive Buffer * @index: the index of the Receive Buffer in the Rx queue * @rxq: the RB's Rx queue - * @reserved: + * @reserved: reserved * @data: the content of the Receive Buffer */ struct iwl_fw_error_dump_rb { @@ -488,7 +489,7 @@ struct iwl_fw_ini_special_device_memory { * struct iwl_fw_error_dump_paging - content of the UMAC's image page * block on DRAM * @index: the index of the page block - * @reserved: + * @reserved: reserved * @data: the content of the page block */ struct iwl_fw_error_dump_paging { @@ -511,6 +512,7 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) /** * enum iwl_fw_dbg_trigger - triggers available * + * @FW_DBG_TRIGGER_INVALID: invalid trigger value * @FW_DBG_TRIGGER_USER: trigger log collection by user * This should not be defined as a trigger to the driver, but a value the * driver should set to indicate that the trigger was initiated by the @@ -530,14 +532,15 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) * @FW_DBG_TRIGGER_TIME_EVENT: trigger log collection upon time events related * events. * @FW_DBG_TRIGGER_BA: trigger log collection upon BlockAck related events. - * @FW_DBG_TX_LATENCY: trigger log collection when the tx latency goes above a - * threshold. - * @FW_DBG_TDLS: trigger log collection upon TDLS related events. + * @FW_DBG_TRIGGER_TX_LATENCY: trigger log collection when the tx latency + * goes above a threshold. + * @FW_DBG_TRIGGER_TDLS: trigger log collection upon TDLS related events. * @FW_DBG_TRIGGER_TX_STATUS: trigger log collection upon tx status when * the firmware sends a tx reply. * @FW_DBG_TRIGGER_ALIVE_TIMEOUT: trigger log collection if alive flow timeouts * @FW_DBG_TRIGGER_DRIVER: trigger log collection upon a flow failure * in the driver. + * @FW_DBG_TRIGGER_MAX: beyond triggers, number for sizing arrays etc. */ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_INVALID = 0, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 03f6e520145f..f69d29e531c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2008-2014, 2018-2023 Intel Corporation + * Copyright (C) 2008-2014, 2018-2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -20,7 +20,7 @@ struct iwl_ucode_header { __le32 init_size; /* bytes of init code */ __le32 init_data_size; /* bytes of init data */ __le32 boot_size; /* bytes of bootstrap code */ - u8 data[0]; /* in same order as sizes */ + u8 data[]; /* in same order as sizes */ } v1; struct { __le32 build; /* build number */ @@ -29,7 +29,7 @@ struct iwl_ucode_header { __le32 init_size; /* bytes of init code */ __le32 init_data_size; /* bytes of init data */ __le32 boot_size; /* bytes of bootstrap code */ - u8 data[0]; /* in same order as sizes */ + u8 data[]; /* in same order as sizes */ } v2; } u; }; @@ -216,6 +216,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * ADD_MODIFY_STA_KEY_API_S_VER_2. * @IWL_UCODE_TLV_API_STA_TYPE: This ucode supports station type assignement. * @IWL_UCODE_TLV_API_NAN2_VER2: This ucode supports NAN API version 2 + * @IWL_UCODE_TLV_API_ADAPTIVE_DWELL: support for adaptive dwell in scanning * @IWL_UCODE_TLV_API_NEW_RX_STATS: should new RX STATISTICS API be used * @IWL_UCODE_TLV_API_QUOTA_LOW_LATENCY: Quota command includes a field * indicating low latency direction. @@ -239,10 +240,21 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * SCAN_OFFLOAD_PROFILES_QUERY_RSP_S. * @IWL_UCODE_TLV_API_MBSSID_HE: This ucode supports v2 of * STA_CONTEXT_DOT11AX_API_S + * @IWL_UCODE_TLV_API_FTM_RTT_ACCURACY: version 7 of the range response API + * is supported by FW, this indicates the RTT confidence value * @IWL_UCODE_TLV_API_SAR_TABLE_VER: This ucode supports different sar * version tables. * @IWL_UCODE_TLV_API_REDUCED_SCAN_CONFIG: This ucode supports v3 of - * SCAN_CONFIG_DB_CMD_API_S. + * SCAN_CONFIG_DB_CMD_API_S. + * @IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP: support for setting adaptive dwell + * number of APs in the 5 GHz band + * @IWL_UCODE_TLV_API_BAND_IN_RX_DATA: FW reports band number in RX notification + * @IWL_UCODE_TLV_API_NO_HOST_DISABLE_TX: Firmware offloaded the station disable tx + * logic. + * @IWL_UCODE_TLV_API_INT_DBG_BUF_CLEAR: Firmware supports clearing the debug + * internal buffer + * @IWL_UCODE_TLV_API_SMART_FIFO_OFFLOAD: Firmware doesn't need the host to + * configure the smart fifo * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -280,6 +292,10 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_ADWELL_HB_DEF_N_AP = (__force iwl_ucode_tlv_api_t)57, IWL_UCODE_TLV_API_SCAN_EXT_CHAN_VER = (__force iwl_ucode_tlv_api_t)58, IWL_UCODE_TLV_API_BAND_IN_RX_DATA = (__force iwl_ucode_tlv_api_t)59, + /* API Set 2 */ + IWL_UCODE_TLV_API_NO_HOST_DISABLE_TX = (__force iwl_ucode_tlv_api_t)66, + IWL_UCODE_TLV_API_INT_DBG_BUF_CLEAR = (__force iwl_ucode_tlv_api_t)67, + IWL_UCODE_TLV_API_SMART_FIFO_OFFLOAD = (__force iwl_ucode_tlv_api_t)68, NUM_IWL_UCODE_TLV_API /* @@ -376,6 +392,9 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * channels even when these are not enabled. * @IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT: Support for indicating dump collection * complete to FW. + * @IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT: Support SPP (signaling and payload + * protected) A-MSDU. + * @IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT: Support secure LTF measurement. * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -461,6 +480,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_PSC_CHAN_SUPPORT = (__force iwl_ucode_tlv_capa_t)98, IWL_UCODE_TLV_CAPA_BIGTK_SUPPORT = (__force iwl_ucode_tlv_capa_t)100, + IWL_UCODE_TLV_CAPA_SPP_AMSDU_SUPPORT = (__force iwl_ucode_tlv_capa_t)103, IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT = (__force iwl_ucode_tlv_capa_t)104, IWL_UCODE_TLV_CAPA_DUMP_COMPLETE_SUPPORT = (__force iwl_ucode_tlv_capa_t)105, IWL_UCODE_TLV_CAPA_SYNCED_TIME = (__force iwl_ucode_tlv_capa_t)106, @@ -473,7 +493,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_STA_EXP_MFP_SUPPORT = (__force iwl_ucode_tlv_capa_t)114, IWL_UCODE_TLV_CAPA_SNIFF_VALIDATE_SUPPORT = (__force iwl_ucode_tlv_capa_t)116, IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT = (__force iwl_ucode_tlv_capa_t)117, - + IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT = (__force iwl_ucode_tlv_capa_t)121, NUM_IWL_UCODE_TLV_CAPA /* * This construction make both sparse (which cannot increment the previous @@ -559,6 +579,7 @@ enum iwl_fw_dbg_reg_operator { * struct iwl_fw_dbg_reg_op - an operation on a register * * @op: &enum iwl_fw_dbg_reg_operator + * @reserved: reserved * @addr: offset of the register * @val: value */ @@ -605,6 +626,7 @@ struct iwl_fw_dbg_mem_seg_tlv { * @version: version of the TLV - currently 0 * @monitor_mode: &enum iwl_fw_dbg_monitor_mode * @size_power: buffer size will be 2^(size_power + 11) + * @reserved: reserved * @base_reg: addr of the base addr register (PRPH) * @end_reg: addr of the end addr register (PRPH) * @write_ptr_reg: the addr of the reg of the write pointer @@ -715,6 +737,8 @@ enum iwl_fw_dbg_trigger_vif_type { * @trig_dis_ms: the time, in milliseconds, after an occurrence of this * trigger in which another occurrence should be ignored. * @flags: &enum iwl_fw_dbg_trigger_flags + * @reserved: reserved (for alignment) + * @data: trigger data */ struct iwl_fw_dbg_trigger_tlv { __le32 id; @@ -755,7 +779,7 @@ struct iwl_fw_dbg_trigger_missed_bcon { /** * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW. - * cmds: the list of commands to trigger the collection on + * @cmds: the list of commands to trigger the collection on */ struct iwl_fw_dbg_trigger_cmd { struct cmd { @@ -765,7 +789,7 @@ struct iwl_fw_dbg_trigger_cmd { } __packed; /** - * iwl_fw_dbg_trigger_stats - configures trigger for statistics + * struct iwl_fw_dbg_trigger_stats - configures trigger for statistics * @stop_offset: the offset of the value to be monitored * @stop_threshold: the threshold above which to collect * @start_offset: the offset of the value to be monitored diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 650e4bde9c17..1195e708caa9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2020-2023 Intel Corporation + * Copyright(c) 2020-2024 Intel Corporation */ #include "iwl-drv.h" @@ -12,6 +12,8 @@ #include "fw/api/alive.h" #include "fw/uefi.h" +#define IWL_PNVM_REDUCED_CAP_BIT BIT(25) + struct iwl_pnvm_section { __le32 offset; const u8 data[]; @@ -173,6 +175,7 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, while (len >= sizeof(*tlv)) { u32 tlv_len, tlv_type; + u32 rf_type; len -= sizeof(*tlv); tlv = (const void *)data; @@ -201,6 +204,16 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, data += sizeof(*tlv) + ALIGN(tlv_len, 4); len -= ALIGN(tlv_len, 4); + trans->reduced_cap_sku = false; + rf_type = CSR_HW_RFID_TYPE(trans->hw_rf_id); + if ((trans->sku_id[0] & IWL_PNVM_REDUCED_CAP_BIT) && + rf_type == IWL_CFG_RF_TYPE_FM) + trans->reduced_cap_sku = true; + + IWL_DEBUG_FW(trans, + "Reduced SKU device %d\n", + trans->reduced_cap_sku); + if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) && trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) && trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) { @@ -239,7 +252,7 @@ static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len) } new_len = pnvm->size; - *data = kmemdup(pnvm->data, pnvm->size, GFP_KERNEL); + *data = kvmemdup(pnvm->data, pnvm->size, GFP_KERNEL); release_firmware(pnvm); if (!*data) @@ -255,21 +268,27 @@ static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len) struct pnvm_sku_package *package; u8 *image = NULL; - /* First attempt to get the PNVM from BIOS */ - package = iwl_uefi_get_pnvm(trans_p, len); - if (!IS_ERR_OR_NULL(package)) { - if (*len >= sizeof(*package)) { - /* we need only the data */ - *len -= sizeof(*package); - image = kmemdup(package->data, *len, GFP_KERNEL); + /* Get PNVM from BIOS for non-Intel SKU */ + if (trans_p->sku_id[2]) { + package = iwl_uefi_get_pnvm(trans_p, len); + if (!IS_ERR_OR_NULL(package)) { + if (*len >= sizeof(*package)) { + /* we need only the data */ + *len -= sizeof(*package); + image = kvmemdup(package->data, + *len, GFP_KERNEL); + } + /* + * free package regardless of whether kmemdup + * succeeded + */ + kfree(package); + if (image) + return image; } - /* free package regardless of whether kmemdup succeeded */ - kfree(package); - if (image) - return image; } - /* If it's not available, try from the filesystem */ + /* If it's not available, or for Intel SKU, try from the filesystem */ if (iwl_pnvm_get_from_fs(trans_p, &image, len)) return NULL; return image; @@ -314,7 +333,7 @@ static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, set: iwl_trans_set_pnvm(trans, capa); free: - kfree(data); + kvfree(data); kfree(pnvm_data); } diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c new file mode 100644 index 000000000000..36d506463e0e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -0,0 +1,500 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2023 Intel Corporation + */ +#include <linux/dmi.h> +#include "iwl-drv.h" +#include "iwl-debug.h" +#include "regulatory.h" +#include "fw/runtime.h" +#include "fw/uefi.h" + +#define GET_BIOS_TABLE(__name, ...) \ +do { \ + int ret = -ENOENT; \ + if (fwrt->uefi_tables_lock_status > UEFI_WIFI_GUID_UNLOCKED) \ + ret = iwl_uefi_get_ ## __name(__VA_ARGS__); \ + if (ret < 0) \ + ret = iwl_acpi_get_ ## __name(__VA_ARGS__); \ + return ret; \ +} while (0) + +#define IWL_BIOS_TABLE_LOADER(__name) \ +int iwl_bios_get_ ## __name(struct iwl_fw_runtime *fwrt) \ +{GET_BIOS_TABLE(__name, fwrt); } \ +IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name) + +#define IWL_BIOS_TABLE_LOADER_DATA(__name, data_type) \ +int iwl_bios_get_ ## __name(struct iwl_fw_runtime *fwrt, \ + data_type * data) \ +{GET_BIOS_TABLE(__name, fwrt, data); } \ +IWL_EXPORT_SYMBOL(iwl_bios_get_ ## __name) + +IWL_BIOS_TABLE_LOADER(wrds_table); +IWL_BIOS_TABLE_LOADER(ewrd_table); +IWL_BIOS_TABLE_LOADER(wgds_table); +IWL_BIOS_TABLE_LOADER(ppag_table); +IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data); +IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64); +IWL_BIOS_TABLE_LOADER_DATA(mcc, char); +IWL_BIOS_TABLE_LOADER_DATA(eckv, u32); + + +static const struct dmi_system_id dmi_ppag_approved_list[] = { + { .ident = "HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + }, + }, + { .ident = "SAMSUNG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), + }, + }, + { .ident = "MSFT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + }, + }, + { .ident = "ASUS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + }, + }, + { .ident = "GOOGLE-HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + }, + }, + { .ident = "GOOGLE-ASUS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."), + }, + }, + { .ident = "GOOGLE-SAMSUNG", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), + }, + }, + { .ident = "DELL", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + }, + }, + { .ident = "DELL", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + }, + }, + { .ident = "RAZER", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Razer"), + }, + }, + { .ident = "Honor", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + }, + }, + {} +}; + +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."), + }, + }, + { .ident = "MSFT", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + }, + }, + { .ident = "Acer", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + }, + }, + { .ident = "ASUS", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + }, + }, + { .ident = "GOOGLE-HP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_BOARD_VENDOR, "HP"), + }, + }, + { .ident = "MSI", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + }, + }, + { .ident = "Honor", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HONOR"), + }, + }, + /* keep last */ + {} +}; + +bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) +{ + /* + * The PER_CHAIN_LIMIT_OFFSET_CMD 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(fwrt->fw->ucode_ver) >= 38 || + (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 && + fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) || + (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 && + ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == + CSR_HW_REV_TYPE_7265D)); +} +IWL_EXPORT_SYMBOL(iwl_sar_geo_support); + +int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt, + struct iwl_per_chain_offset *table, + u32 n_bands, u32 n_profiles) +{ + int i, j; + + if (!fwrt->geo_enabled) + return -ENODATA; + + if (!iwl_sar_geo_support(fwrt)) + return -EOPNOTSUPP; + + for (i = 0; i < n_profiles; i++) { + for (j = 0; j < n_bands; j++) { + struct iwl_per_chain_offset *chain = + &table[i * n_bands + j]; + + chain->max_tx_power = + cpu_to_le16(fwrt->geo_profiles[i].bands[j].max); + chain->chain_a = + fwrt->geo_profiles[i].bands[j].chains[0]; + chain->chain_b = + fwrt->geo_profiles[i].bands[j].chains[1]; + IWL_DEBUG_RADIO(fwrt, + "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", + i, j, + fwrt->geo_profiles[i].bands[j].chains[0], + fwrt->geo_profiles[i].bands[j].chains[1], + fwrt->geo_profiles[i].bands[j].max); + } + } + + return 0; +} +IWL_EXPORT_SYMBOL(iwl_sar_geo_fill_table); + +static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt, + __le16 *per_chain, u32 n_subbands, + int prof_a, int prof_b) +{ + int profs[BIOS_SAR_NUM_CHAINS] = { prof_a, prof_b }; + int i, j; + + for (i = 0; i < BIOS_SAR_NUM_CHAINS; i++) { + struct iwl_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 BIOS_SAR_MAX_PROFILE_NUM */ + if (profs[i] > BIOS_SAR_MAX_PROFILE_NUM) + return -EINVAL; + + /* profiles go from 1 to 4, so decrement to access the array */ + prof = &fwrt->sar_profiles[profs[i] - 1]; + + /* if the profile is disabled, do nothing */ + if (!prof->enabled) { + IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n", + profs[i]); + /* + * if one of the profiles is disabled, we + * ignore all of them and return 1 to + * differentiate disabled from other failures. + */ + return 1; + } + + IWL_DEBUG_INFO(fwrt, + "SAR EWRD: chain %d profile index %d\n", + i, profs[i]); + IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i); + for (j = 0; j < n_subbands; j++) { + per_chain[i * n_subbands + j] = + cpu_to_le16(prof->chains[i].subbands[j]); + IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n", + j, prof->chains[i].subbands[j]); + } + } + + return 0; +} + +int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, + __le16 *per_chain, u32 n_tables, u32 n_subbands, + int prof_a, int prof_b) +{ + int i, ret = 0; + + for (i = 0; i < n_tables; i++) { + ret = iwl_sar_fill_table(fwrt, + &per_chain[i * n_subbands * BIOS_SAR_NUM_CHAINS], + n_subbands, prof_a, prof_b); + if (ret) + break; + } + + return ret; +} +IWL_EXPORT_SYMBOL(iwl_sar_fill_profile); + +static bool iwl_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; +} + +int iwl_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->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; + } + + /* 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(fwrt->ppag_flags); + + 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); + if (fwrt->ppag_ver >= 1) { + /* in this case FW supports revision 0 */ + IWL_DEBUG_RADIO(fwrt, + "PPAG table rev is %d, send truncated table\n", + fwrt->ppag_ver); + } + } else if (cmd_ver >= 2 && cmd_ver <= 5) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v2.gain[0]; + *cmd_size = sizeof(cmd->v2); + if (fwrt->ppag_ver == 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 { + 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", + le32_to_cpu(cmd->v1.flags)); + + if (cmd_ver == 5) + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V5_MASK); + else if (cmd_ver < 5) + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V4_MASK); + + if ((cmd_ver == 1 && + !fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || + (cmd_ver == 2 && fwrt->ppag_ver >= 2)) { + 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"); + } + + IWL_DEBUG_RADIO(fwrt, + "PPAG MODE bits going to be sent: %d\n", + le32_to_cpu(cmd->v1.flags)); + + for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { + for (j = 0; j < num_sub_bands; j++) { + if (!send_ppag_always && + !iwl_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; +} +IWL_EXPORT_SYMBOL(iwl_fill_ppag_table); + +bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt) +{ + if (!dmi_check_system(dmi_ppag_approved_list)) { + IWL_DEBUG_RADIO(fwrt, + "System vendor '%s' is not in the approved list, disabling PPAG.\n", + dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); + fwrt->ppag_flags = 0; + return false; + } + + return true; +} +IWL_EXPORT_SYMBOL(iwl_is_ppag_approved); + +bool iwl_is_tas_approved(void) +{ + return dmi_check_system(dmi_tas_approved_list); +} +IWL_EXPORT_SYMBOL(iwl_is_tas_approved); + +int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *tas_data, + const u32 tas_selection) +{ + u8 override_iec = u32_get_bits(tas_selection, + IWL_WTAS_OVERRIDE_IEC_MSK); + u8 enabled_iec = u32_get_bits(tas_selection, IWL_WTAS_ENABLE_IEC_MSK); + u8 usa_tas_uhb = u32_get_bits(tas_selection, IWL_WTAS_USA_UHB_MSK); + int enabled = tas_selection & IWL_WTAS_ENABLED_MSK; + + IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n", + tas_selection); + + tas_data->usa_tas_uhb_allowed = usa_tas_uhb; + tas_data->override_tas_iec = override_iec; + tas_data->enable_tas_iec = enabled_iec; + + return enabled; +} + +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) +{ + int ret; + u32 val; + __le32 config_bitmap = 0; + + switch (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)) { + case IWL_CFG_RF_TYPE_HR1: + case IWL_CFG_RF_TYPE_HR2: + case IWL_CFG_RF_TYPE_JF1: + case IWL_CFG_RF_TYPE_JF2: + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_INDONESIA_5G2, + &val); + + if (!ret && val == DSM_VALUE_INDONESIA_ENABLE) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); + break; + default: + break; + } + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_DISABLE_SRD, &val); + if (!ret) { + if (val == DSM_VALUE_SRD_PASSIVE) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK); + else if (val == DSM_VALUE_SRD_DISABLE) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK); + } + + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CHINA_22_REG_SUPPORT)) { + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_REGULATORY_CONFIG, + &val); + /* + * China 2022 enable if the BIOS object does not exist or + * if it is enabled in BIOS. + */ + if (ret < 0 || val & DSM_MASK_CHINA_22_REG) + config_bitmap |= + cpu_to_le32(LARI_CONFIG_ENABLE_CHINA_22_REG_SUPPORT_MSK); + } + + return config_bitmap; +} +IWL_EXPORT_SYMBOL(iwl_get_lari_config_bitmap); + +int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value) +{ + GET_BIOS_TABLE(dsm, fwrt, func, value); +} +IWL_EXPORT_SYMBOL(iwl_bios_get_dsm); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h new file mode 100644 index 000000000000..28e774766847 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2023 Intel Corporation + */ + +#ifndef __fw_regulatory_h__ +#define __fw_regulatory_h__ + +#include "fw/img.h" +#include "fw/api/commands.h" +#include "fw/api/power.h" +#include "fw/api/phy.h" +#include "fw/api/config.h" +#include "fw/img.h" +#include "iwl-trans.h" + +#define BIOS_SAR_MAX_PROFILE_NUM 4 +/* + * Each SAR profile has (up to, depends on the table revision) 4 chains: + * chain A, chain B, chain A when in CDB, chain B when in CDB + */ +#define BIOS_SAR_MAX_CHAINS_PER_PROFILE 4 +#define BIOS_SAR_NUM_CHAINS 2 +#define BIOS_SAR_MAX_SUB_BANDS_NUM 11 + +#define BIOS_GEO_NUM_CHAINS 2 +#define BIOS_GEO_MAX_NUM_BANDS 3 +#define BIOS_GEO_MAX_PROFILE_NUM 8 +#define BIOS_GEO_MIN_PROFILE_NUM 3 + +#define IWL_SAR_ENABLE_MSK BIT(0) + +/* PPAG gain value bounds in 1/8 dBm */ +#define IWL_PPAG_MIN_LB -16 +#define IWL_PPAG_MAX_LB 24 +#define IWL_PPAG_MIN_HB -16 +#define IWL_PPAG_MAX_HB 40 + +#define IWL_PPAG_ETSI_CHINA_MASK 3 +#define IWL_PPAG_REV3_MASK 0x7FF + +#define IWL_WTAS_BLACK_LIST_MAX 16 +#define IWL_WTAS_ENABLED_MSK 0x1 +#define IWL_WTAS_OVERRIDE_IEC_MSK 0x2 +#define IWL_WTAS_ENABLE_IEC_MSK 0x4 +#define IWL_WTAS_USA_UHB_MSK BIT(16) + +/* + * The profile for revision 2 is a superset of revision 1, which is in + * turn a superset of revision 0. So we can store all revisions + * inside revision 2, which is what we represent here. + */ + +/* + * struct iwl_sar_profile_chain - per-chain values of a SAR profile + * @subbands: the SAR value for each subband + */ +struct iwl_sar_profile_chain { + u8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM]; +}; + +/* + * struct iwl_sar_profile - SAR profile from SAR tables + * @enabled: whether the profile is enabled or not + * @chains: per-chain SAR values + */ +struct iwl_sar_profile { + bool enabled; + struct iwl_sar_profile_chain chains[BIOS_SAR_MAX_CHAINS_PER_PROFILE]; +}; + +/* Same thing as with SAR, all revisions fit in revision 2 */ + +/* + * struct iwl_geo_profile_band - per-band geo SAR offsets + * @max: the max tx power allowed for the band + * @chains: SAR offsets values for each chain + */ +struct iwl_geo_profile_band { + u8 max; + u8 chains[BIOS_GEO_NUM_CHAINS]; +}; + +/* + * struct iwl_geo_profile - geo profile + * @bands: per-band table of the SAR offsets + */ +struct iwl_geo_profile { + struct iwl_geo_profile_band bands[BIOS_GEO_MAX_NUM_BANDS]; +}; + +/* Same thing as with SAR, all revisions fit in revision 2 */ +struct iwl_ppag_chain { + s8 subbands[BIOS_SAR_MAX_SUB_BANDS_NUM]; +}; + +struct iwl_tas_data { + __le32 block_list_size; + __le32 block_list_array[IWL_WTAS_BLACK_LIST_MAX]; + u8 override_tas_iec; + u8 enable_tas_iec; + u8 usa_tas_uhb_allowed; +}; + +/* For DSM revision 0 and 4 */ +enum iwl_dsm_funcs { + DSM_FUNC_QUERY = 0, + DSM_FUNC_DISABLE_SRD = 1, + DSM_FUNC_ENABLE_INDONESIA_5G2 = 2, + DSM_FUNC_ENABLE_6E = 3, + DSM_FUNC_REGULATORY_CONFIG = 4, + DSM_FUNC_11AX_ENABLEMENT = 6, + DSM_FUNC_ENABLE_UNII4_CHAN = 7, + DSM_FUNC_ACTIVATE_CHANNEL = 8, + DSM_FUNC_FORCE_DISABLE_CHANNELS = 9, + DSM_FUNC_ENERGY_DETECTION_THRESHOLD = 10, + DSM_FUNC_RFI_CONFIG = 11, + DSM_FUNC_NUM_FUNCS = 12, +}; + +enum iwl_dsm_values_srd { + DSM_VALUE_SRD_ACTIVE, + DSM_VALUE_SRD_PASSIVE, + DSM_VALUE_SRD_DISABLE, + DSM_VALUE_SRD_MAX +}; + +enum iwl_dsm_values_indonesia { + DSM_VALUE_INDONESIA_DISABLE, + DSM_VALUE_INDONESIA_ENABLE, + DSM_VALUE_INDONESIA_RESERVED, + DSM_VALUE_INDONESIA_MAX +}; + +enum iwl_dsm_values_rfi { + DSM_VALUE_RFI_DLVR_DISABLE = BIT(0), + DSM_VALUE_RFI_DDR_DISABLE = BIT(1), +}; + +#define DSM_VALUE_RFI_DISABLE (DSM_VALUE_RFI_DLVR_DISABLE |\ + DSM_VALUE_RFI_DDR_DISABLE) + +enum iwl_dsm_masks_reg { + DSM_MASK_CHINA_22_REG = BIT(2) +}; + +struct iwl_fw_runtime; + +bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt); + +int iwl_sar_geo_fill_table(struct iwl_fw_runtime *fwrt, + struct iwl_per_chain_offset *table, + u32 n_bands, u32 n_profiles); + +int iwl_sar_fill_profile(struct iwl_fw_runtime *fwrt, + __le16 *per_chain, u32 n_tables, u32 n_subbands, + int prof_a, int prof_b); + +int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, + union iwl_ppag_table_cmd *cmd, + int *cmd_size); + +bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt); + +bool iwl_is_tas_approved(void); + +int iwl_parse_tas_selection(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *tas_data, + const u32 tas_selection); + +int iwl_bios_get_wrds_table(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_ewrd_table(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_wgds_table(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_ppag_table(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data); + +int iwl_bios_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit); + +int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); +int iwl_bios_get_eckv(struct iwl_fw_runtime *fwrt, u32 *ext_clk); + +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); + +int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value); + +static inline u32 iwl_bios_get_ppag_flags(const u32 ppag_modes, + const u8 ppag_ver) +{ + return ppag_modes & (ppag_ver < 3 ? IWL_PPAG_ETSI_CHINA_MASK : + IWL_PPAG_REV3_MASK); +} +#endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 357727774db9..b2bc4fd37abf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2024 Intel Corporation */ #ifndef __iwl_fw_runtime_h__ #define __iwl_fw_runtime_h__ @@ -14,6 +14,7 @@ #include "fw/api/power.h" #include "iwl-eeprom-parse.h" #include "fw/acpi.h" +#include "fw/regulatory.h" struct iwl_fw_runtime_ops { void (*dump_start)(void *ctx); @@ -100,6 +101,11 @@ struct iwl_txf_iter_data { * @dump: debug dump data * @uats_enabled: VLP or AFC AP is enabled * @uats_table: AP type table + * @uefi_tables_lock_status: The status of the WIFI GUID UEFI variables lock: + * 0: Unlocked, 1 and 2: Locked. + * Only read the UEFI variables if locked. + * @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables + * @geo_profiles: geographic profiles as read from WGDS BIOS table */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -158,24 +164,22 @@ struct iwl_fw_runtime { #ifdef CONFIG_IWLWIFI_DEBUGFS bool tpc_enabled; #endif /* CONFIG_IWLWIFI_DEBUGFS */ -#ifdef CONFIG_ACPI - struct iwl_sar_profile sar_profiles[ACPI_SAR_PROFILE_NUM]; + struct iwl_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM]; u8 sar_chain_a_profile; u8 sar_chain_b_profile; - struct iwl_geo_profile geo_profiles[ACPI_NUM_GEO_PROFILES_REV3]; + u8 reduced_power_flags; + struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; u32 geo_rev; u32 geo_num_profiles; bool geo_enabled; struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; u32 ppag_flags; - u32 ppag_ver; - bool ppag_table_valid; + u8 ppag_ver; struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; - u8 reduced_power_flags; - bool uats_enabled; struct iwl_uats_table_cmd uats_table; -#endif + u8 uefi_tables_lock_status; + bool uats_enabled; }; void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 2964c5fb11e9..e81fc0129b9d 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2021-2023 Intel Corporation + * Copyright(c) 2021-2024 Intel Corporation */ #include "iwl-drv.h" @@ -76,6 +76,42 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) return data; } +static +void *iwl_uefi_get_verified_variable(struct iwl_trans *trans, + efi_char16_t *uefi_var_name, + char *var_name, + unsigned int expected_size, + unsigned long *size) +{ + void *var; + unsigned long var_size; + + var = iwl_uefi_get_variable(uefi_var_name, &IWL_EFI_VAR_GUID, + &var_size); + + if (IS_ERR(var)) { + IWL_DEBUG_RADIO(trans, + "%s UEFI variable not found 0x%lx\n", var_name, + PTR_ERR(var)); + return var; + } + + if (var_size < expected_size) { + IWL_DEBUG_RADIO(trans, + "Invalid %s UEFI variable len (%lu)\n", + var_name, var_size); + kfree(var); + return ERR_PTR(-EINVAL); + } + + IWL_DEBUG_RADIO(trans, "%s from UEFI with size %lu\n", var_name, + var_size); + + if (size) + *size = var_size; + return var; +} + int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, u32 tlv_len, struct iwl_pnvm_image *pnvm_data) { @@ -230,26 +266,13 @@ u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) unsigned long package_size; u8 *data; - package = iwl_uefi_get_variable(IWL_UEFI_REDUCED_POWER_NAME, - &IWL_EFI_VAR_GUID, &package_size); - - if (IS_ERR(package)) { - IWL_DEBUG_FW(trans, - "Reduced Power UEFI variable not found 0x%lx (len %lu)\n", - PTR_ERR(package), package_size); + package = iwl_uefi_get_verified_variable(trans, + IWL_UEFI_REDUCED_POWER_NAME, + "Reduced Power", + sizeof(*package), + &package_size); + if (IS_ERR(package)) return ERR_CAST(package); - } - - if (package_size < sizeof(*package)) { - IWL_DEBUG_FW(trans, - "Invalid Reduced Power UEFI variable len (%lu)\n", - package_size); - kfree(package); - return ERR_PTR(-EINVAL); - } - - IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n", - package_size); IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n", package->rev, package->total_size, package->n_skus); @@ -283,32 +306,15 @@ static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_dat void iwl_uefi_get_step_table(struct iwl_trans *trans) { struct uefi_cnv_common_step_data *data; - unsigned long package_size; int ret; if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; - data = iwl_uefi_get_variable(IWL_UEFI_STEP_NAME, &IWL_EFI_VAR_GUID, - &package_size); - - if (IS_ERR(data)) { - IWL_DEBUG_FW(trans, - "STEP UEFI variable not found 0x%lx\n", - PTR_ERR(data)); + data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_STEP_NAME, + "STEP", sizeof(*data), NULL); + if (IS_ERR(data)) return; - } - - if (package_size < sizeof(*data)) { - IWL_DEBUG_FW(trans, - "Invalid STEP table UEFI variable len (%lu)\n", - package_size); - kfree(data); - return; - } - - IWL_DEBUG_FW(trans, "Read STEP from UEFI with size %lu\n", - package_size); ret = iwl_uefi_step_parse(data, trans); if (ret < 0) @@ -318,7 +324,6 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans) } IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table); -#ifdef CONFIG_ACPI static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data, struct iwl_fw_runtime *fwrt) { @@ -355,31 +360,15 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { struct uefi_cnv_wlan_sgom_data *data; - unsigned long package_size; int ret; if (!fwrt->geo_enabled) return; - data = iwl_uefi_get_variable(IWL_UEFI_SGOM_NAME, &IWL_EFI_VAR_GUID, - &package_size); - if (IS_ERR(data)) { - IWL_DEBUG_FW(trans, - "SGOM UEFI variable not found 0x%lx\n", - PTR_ERR(data)); - return; - } - - if (package_size < sizeof(*data)) { - IWL_DEBUG_FW(trans, - "Invalid SGOM table UEFI variable len (%lu)\n", - package_size); - kfree(data); + data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME, + "SGOM", sizeof(*data), NULL); + if (IS_ERR(data)) return; - } - - IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n", - package_size); ret = iwl_uefi_sgom_parse(data, fwrt); if (ret < 0) @@ -404,28 +393,12 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { struct uefi_cnv_wlan_uats_data *data; - unsigned long package_size; int ret; - data = iwl_uefi_get_variable(IWL_UEFI_UATS_NAME, &IWL_EFI_VAR_GUID, - &package_size); - if (IS_ERR(data)) { - IWL_DEBUG_FW(trans, - "UATS UEFI variable not found 0x%lx\n", - PTR_ERR(data)); + data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME, + "UATS", sizeof(*data), NULL); + if (IS_ERR(data)) return -EINVAL; - } - - if (package_size < sizeof(*data)) { - IWL_DEBUG_FW(trans, - "Invalid UATS table UEFI variable len (%lu)\n", - package_size); - kfree(data); - return -EINVAL; - } - - IWL_DEBUG_FW(trans, "Read UATS from UEFI with size %lu\n", - package_size); ret = iwl_uefi_uats_parse(data, fwrt); if (ret < 0) { @@ -438,4 +411,298 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, return 0; } IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table); -#endif /* CONFIG_ACPI */ + +static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt, + struct uefi_sar_profile *uefi_sar_prof, + u8 prof_index, bool enabled) +{ + memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof, + sizeof(struct uefi_sar_profile)); + + fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK; +} + +int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_wrds *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME, + "WRDS", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_WRDS_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n", + data->revision); + goto out; + } + + /* The profile from WRDS is officially profile 1, but goes + * into sar_profiles[0] (because we don't have a profile 0). + */ + iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode); +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_ewrd *data; + int i, ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME, + "EWRD", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_EWRD_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n", + data->revision); + goto out; + } + + if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) { + ret = -EINVAL; + goto out; + } + + for (i = 0; i < data->num_profiles; i++) + /* 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. + */ + iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1, + data->mode); + +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_wgds *data; + int i, ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME, + "WGDS", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_WGDS_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n", + data->revision); + goto out; + } + + if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM || + data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Invalid number of profiles in WGDS: %d\n", + data->num_profiles); + goto out; + } + + fwrt->geo_rev = data->revision; + for (i = 0; i < data->num_profiles; i++) + memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i], + sizeof(struct iwl_geo_profile)); + + fwrt->geo_num_profiles = data->num_profiles; + fwrt->geo_enabled = true; +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_var_ppag *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME, + "PPAG", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision < IWL_UEFI_MIN_PPAG_REV || + data->revision > IWL_UEFI_MAX_PPAG_REV) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n", + data->revision); + goto out; + } + + fwrt->ppag_ver = data->revision; + fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes, + fwrt->ppag_ver); + + BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains)); + memcpy(&fwrt->ppag_chains, &data->ppag_chains, + sizeof(data->ppag_chains)); +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *tas_data) +{ + struct uefi_cnv_var_wtas *uefi_tas; + int ret = 0, enabled, i; + + uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME, + "WTAS", sizeof(*uefi_tas), NULL); + if (IS_ERR(uefi_tas)) + return -EINVAL; + + if (uefi_tas->revision != IWL_UEFI_WTAS_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n", + uefi_tas->revision); + goto out; + } + + enabled = iwl_parse_tas_selection(fwrt, tas_data, + uefi_tas->tas_selection); + if (!enabled) { + IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); + ret = 0; + goto out; + } + + IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", + uefi_tas->revision); + if (uefi_tas->black_list_size > IWL_WTAS_BLACK_LIST_MAX) { + IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %d\n", + uefi_tas->black_list_size); + ret = -EINVAL; + goto out; + } + tas_data->block_list_size = cpu_to_le32(uefi_tas->black_list_size); + IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size); + + for (i = 0; i < uefi_tas->black_list_size; i++) { + tas_data->block_list_array[i] = + cpu_to_le32(uefi_tas->black_list[i]); + IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", + uefi_tas->black_list[i]); + } +out: + kfree(uefi_tas); + return ret; +} + +int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit) +{ + struct uefi_cnv_var_splc *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME, + "SPLC", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_SPLC_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI SPLC revision:%d\n", + data->revision); + goto out; + } + *dflt_pwr_limit = data->default_pwr_limit; +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) +{ + struct uefi_cnv_var_wrdd *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME, + "WRDD", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_WRDD_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", + data->revision); + goto out; + } + + if (data->mcc != UEFI_MCC_CHINA) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n"); + goto out; + } + + mcc[0] = (data->mcc >> 8) & 0xff; + mcc[1] = data->mcc & 0xff; + mcc[2] = '\0'; +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) +{ + struct uefi_cnv_var_eckv *data; + int ret = 0; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME, + "ECKV", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_ECKV_REVISION) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", + data->revision); + goto out; + } + *extl_clk = data->ext_clock_valid; +out: + kfree(data); + return ret; +} + +int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value) +{ + struct uefi_cnv_var_general_cfg *data; + int ret = -EINVAL; + + /* Not supported function index */ + if (func >= DSM_FUNC_NUM_FUNCS || func == 5) + return -EOPNOTSUPP; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME, + "DSM", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != IWL_UEFI_DSM_REVISION) { + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSM revision:%d\n", + data->revision); + goto out; + } + + if (ARRAY_SIZE(data->functions) != UEFI_MAX_DSM_FUNCS) { + IWL_DEBUG_RADIO(fwrt, "Invalid size of DSM functions array\n"); + goto out; + } + + *value = data->functions[func]; + ret = 0; +out: + kfree(data); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index bf61a8df1225..303cc299d1bc 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -5,15 +5,38 @@ #ifndef __iwl_fw_uefi__ #define __iwl_fw_uefi__ +#include "fw/regulatory.h" + #define IWL_UEFI_OEM_PNVM_NAME L"UefiCnvWlanOemSignedPnvm" #define IWL_UEFI_REDUCED_POWER_NAME L"UefiCnvWlanReducedPower" #define IWL_UEFI_SGOM_NAME L"UefiCnvWlanSarGeoOffsetMapping" #define IWL_UEFI_STEP_NAME L"UefiCnvCommonSTEP" #define IWL_UEFI_UATS_NAME L"CnvUefiWlanUATS" +#define IWL_UEFI_WRDS_NAME L"UefiCnvWlanWRDS" +#define IWL_UEFI_EWRD_NAME L"UefiCnvWlanEWRD" +#define IWL_UEFI_WGDS_NAME L"UefiCnvWlanWGDS" +#define IWL_UEFI_PPAG_NAME L"UefiCnvWlanPPAG" +#define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS" +#define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC" +#define IWL_UEFI_WRDD_NAME L"UefiCnvWlanWRDD" +#define IWL_UEFI_ECKV_NAME L"UefiCnvWlanECKV" +#define IWL_UEFI_DSM_NAME L"UefiCnvWlanGeneralCfg" + #define IWL_SGOM_MAP_SIZE 339 #define IWL_UATS_MAP_SIZE 339 +#define IWL_UEFI_WRDS_REVISION 2 +#define IWL_UEFI_EWRD_REVISION 2 +#define IWL_UEFI_WGDS_REVISION 3 +#define IWL_UEFI_MIN_PPAG_REV 1 +#define IWL_UEFI_MAX_PPAG_REV 3 +#define IWL_UEFI_WTAS_REVISION 1 +#define IWL_UEFI_SPLC_REVISION 0 +#define IWL_UEFI_WRDD_REVISION 0 +#define IWL_UEFI_ECKV_REVISION 0 +#define IWL_UEFI_DSM_REVISION 4 + struct pnvm_sku_package { u8 rev; u32 total_size; @@ -42,6 +65,120 @@ struct uefi_cnv_common_step_data { } __packed; /* + * struct uefi_sar_profile - a SAR profile as defined in UEFI + * + * @chains: a per-chain table of SAR values + */ +struct uefi_sar_profile { + struct iwl_sar_profile_chain chains[BIOS_SAR_MAX_CHAINS_PER_PROFILE]; +} __packed; + +/* + * struct uefi_cnv_var_wrds - WRDS table as defined in UEFI + * + * @revision: the revision of the table + * @mode: is WRDS enbaled/disabled + * @sar_profile: sar profile #1 + */ +struct uefi_cnv_var_wrds { + u8 revision; + u32 mode; + struct uefi_sar_profile sar_profile; +} __packed; + +/* + * struct uefi_cnv_var_ewrd - EWRD table as defined in UEFI + * @revision: the revision of the table + * @mode: is WRDS enbaled/disabled + * @num_profiles: how many additional profiles we have in this table (0-3) + * @sar_profiles: the additional SAR profiles (#2-#4) + */ +struct uefi_cnv_var_ewrd { + u8 revision; + u32 mode; + u32 num_profiles; + struct uefi_sar_profile sar_profiles[BIOS_SAR_MAX_PROFILE_NUM - 1]; +} __packed; + +/* + * struct uefi_cnv_var_wgds - WGDS table as defined in UEFI + * @revision: the revision of the table + * @num_profiles: the number of geo profiles we have in the table. + * The first 3 are mandatory, and can have up to 8. + * @geo_profiles: a per-profile table of the offsets to add to SAR values. + */ +struct uefi_cnv_var_wgds { + u8 revision; + u8 num_profiles; + struct iwl_geo_profile geo_profiles[BIOS_GEO_MAX_PROFILE_NUM]; +} __packed; + +/* + * struct uefi_cnv_var_ppag - PPAG table as defined in UEFI + * @revision: the revision of the table + * @ppag_modes: values from &enum iwl_ppag_flags + * @ppag_chains: the PPAG values per chain and band + */ +struct uefi_cnv_var_ppag { + u8 revision; + u32 ppag_modes; + struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; +} __packed; + +/* struct uefi_cnv_var_wtas - WTAS tabled as defined in UEFI + * @revision: the revision of the table + * @tas_selection: different options of TAS enablement. + * @black_list_size: the number of defined entried in the black list + * @black_list: a list of countries that are not allowed to use the TAS feature + */ +struct uefi_cnv_var_wtas { + u8 revision; + u32 tas_selection; + u8 black_list_size; + u16 black_list[IWL_WTAS_BLACK_LIST_MAX]; +} __packed; + +/* struct uefi_cnv_var_splc - SPLC tabled as defined in UEFI + * @revision: the revision of the table + * @default_pwr_limit: The default maximum power per device + */ +struct uefi_cnv_var_splc { + u8 revision; + u32 default_pwr_limit; +} __packed; + +#define UEFI_MCC_CHINA 0x434e + +/* struct uefi_cnv_var_wrdd - WRDD table as defined in UEFI + * @revision: the revision of the table + * @mcc: country identifier as defined in ISO/IEC 3166-1 Alpha 2 code + */ +struct uefi_cnv_var_wrdd { + u8 revision; + u32 mcc; +} __packed; + +/* struct uefi_cnv_var_eckv - ECKV table as defined in UEFI + * @revision: the revision of the table + * @ext_clock_valid: indicates if external 32KHz clock is valid + */ +struct uefi_cnv_var_eckv { + u8 revision; + u32 ext_clock_valid; +} __packed; + +#define UEFI_MAX_DSM_FUNCS 32 + +/* struct uefi_cnv_var_general_cfg - DSM-like table as defined in UEFI + * @revision: the revision of the table + * @functions: payload of the different DSM functions + */ +struct uefi_cnv_var_general_cfg { + u8 revision; + u32 functions[UEFI_MAX_DSM_FUNCS]; +} __packed; + +/* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply * disable the feature in old kernels. @@ -55,6 +192,21 @@ int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, void iwl_uefi_get_step_table(struct iwl_trans *trans); int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, u32 tlv_len, struct iwl_pnvm_image *pnvm_data); +int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data); +int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit); +int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); +int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk); +int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, + u32 *value); +void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); +int iwl_uefi_get_uats_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -85,13 +237,56 @@ iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, { return 0; } -#endif /* CONFIG_EFI */ -#if defined(CONFIG_EFI) && defined(CONFIG_ACPI) -void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); -int iwl_uefi_get_uats_table(struct iwl_trans *trans, - struct iwl_fw_runtime *fwrt); -#else +static inline int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, + struct iwl_tas_data *data) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, + u64 *dflt_pwr_limit) +{ + *dflt_pwr_limit = 0; + return 0; +} + +static inline int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) +{ + return -ENOENT; +} + +static inline int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, + enum iwl_dsm_funcs func, u32 *value) +{ + return -ENOENT; +} + static inline void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { @@ -103,6 +298,5 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, { return 0; } - -#endif +#endif /* CONFIG_EFI */ #endif /* __iwl_fw_uefi__ */ |