diff options
Diffstat (limited to 'sys/contrib/dev/iwlwifi/fw/acpi.c')
| -rw-r--r-- | sys/contrib/dev/iwlwifi/fw/acpi.c | 253 |
1 files changed, 168 insertions, 85 deletions
diff --git a/sys/contrib/dev/iwlwifi/fw/acpi.c b/sys/contrib/dev/iwlwifi/fw/acpi.c index 8c8880b44827..7ec22738b5d6 100644 --- a/sys/contrib/dev/iwlwifi/fw/acpi.c +++ b/sys/contrib/dev/iwlwifi/fw/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2019-2024 Intel Corporation + * Copyright (C) 2019-2025 Intel Corporation */ #include <linux/uuid.h> #include "iwl-drv.h" @@ -79,9 +79,9 @@ static void *iwl_acpi_get_object(struct device *dev, acpi_string method) * method (DSM) interface. The returned acpi object must be freed by calling * function. */ -static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func, - union acpi_object *args, - const guid_t *guid) +union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev, + int func, union acpi_object *args, + const guid_t *guid) { union acpi_object *obj; @@ -108,7 +108,7 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, size_t expected_size) { union acpi_object *obj; - int ret = 0; + int ret; obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid); if (IS_ERR(obj)) { @@ -123,8 +123,10 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, } else if (obj->type == ACPI_TYPE_BUFFER) { __le64 le_value = 0; - if (WARN_ON_ONCE(expected_size > sizeof(le_value))) - return -EINVAL; + if (WARN_ON_ONCE(expected_size > sizeof(le_value))) { + ret = -EINVAL; + goto out; + } /* if the buffer size doesn't match the expected size */ if (obj->buffer.length != expected_size) @@ -145,8 +147,9 @@ static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, } IWL_DEBUG_DEV_RADIO(dev, - "ACPI: DSM method evaluated: func=%d, ret=%d\n", - func, ret); + "ACPI: DSM method evaluated: func=%d, value=%lld\n", + func, *value); + ret = 0; out: ACPI_FREE(obj); return ret; @@ -166,7 +169,7 @@ int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, BUILD_BUG_ON(ARRAY_SIZE(acpi_dsm_size) != DSM_FUNC_NUM_FUNCS); - if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size))) + if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size) || !func)) return -EINVAL; expected_size = acpi_dsm_size[func]; @@ -175,6 +178,29 @@ int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt, if (expected_size != sizeof(u8) && expected_size != sizeof(u32)) return -EOPNOTSUPP; + if (!fwrt->acpi_dsm_funcs_valid) { + ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, + DSM_FUNC_QUERY, + &iwl_guid, &tmp, + acpi_dsm_size[DSM_FUNC_QUERY]); + if (ret) { + /* always indicate BIT(0) to avoid re-reading */ + fwrt->acpi_dsm_funcs_valid = BIT(0); + return ret; + } + + IWL_DEBUG_RADIO(fwrt, "ACPI DSM validity bitmap 0x%x\n", + (u32)tmp); + /* always indicate BIT(0) to avoid re-reading */ + fwrt->acpi_dsm_funcs_valid = tmp | BIT(0); + } + + if (!(fwrt->acpi_dsm_funcs_valid & BIT(func))) { + IWL_DEBUG_RADIO(fwrt, "ACPI DSM %d not indicated as valid\n", + func); + return -ENODATA; + } + ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func, &iwl_guid, &tmp, expected_size); if (ret) @@ -259,13 +285,14 @@ 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; + int ret, tbl_rev, block_list_size, enabled; + u32 tas_selection; data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD); if (IS_ERR(data)) return PTR_ERR(data); - /* try to read wtas table revision 1 or revision 0*/ + /* try to read wtas table */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_WTAS_WIFI_DATA_SIZE, &tbl_rev); @@ -274,27 +301,23 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, goto out_free; } - if (tbl_rev == 1 && wifi_pkg->package.elements[1].type == - ACPI_TYPE_INTEGER) { - u32 tas_selection = - (u32)wifi_pkg->package.elements[1].integer.value; - - enabled = iwl_parse_tas_selection(fwrt, tas_data, - tas_selection); - - } else if (tbl_rev == 0 && - wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) { - enabled = !!wifi_pkg->package.elements[1].integer.value; - } else { + if (tbl_rev < 0 || tbl_rev > 2 || + wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { ret = -EINVAL; goto out_free; } - if (!enabled) { - IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); - ret = 0; - goto out_free; - } + tas_selection = (u32)wifi_pkg->package.elements[1].integer.value; + enabled = tas_selection & IWL_WTAS_ENABLED_MSK; + + IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n", + tas_selection); + tas_data->table_source = BIOS_SOURCE_ACPI; + tas_data->table_revision = tbl_rev; + tas_data->tas_selection = tas_selection; + + IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n", + enabled ? "is" : "not"); IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev); if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER || @@ -305,13 +328,14 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, ret = -EINVAL; goto out_free; } + block_list_size = wifi_pkg->package.elements[2].integer.value; - tas_data->block_list_size = cpu_to_le32(block_list_size); + tas_data->block_list_size = block_list_size; IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size); - for (i = 0; i < block_list_size; i++) { - u32 country; + for (int i = 0; i < block_list_size; i++) { + u16 country; if (wifi_pkg->package.elements[3 + i].type != ACPI_TYPE_INTEGER) { @@ -322,11 +346,11 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, } country = wifi_pkg->package.elements[3 + i].integer.value; - tas_data->block_list_array[i] = cpu_to_le32(country); + tas_data->block_list_array[i] = country; IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country); } - ret = 1; + ret = enabled; out_free: kfree(data); return ret; @@ -357,6 +381,11 @@ int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) } mcc_val = wifi_pkg->package.elements[1].integer.value; + if (mcc_val != BIOS_MCC_CHINA) { + ret = -EINVAL; + IWL_DEBUG_RADIO(fwrt, "ACPI WRDD is supported only for CN\n"); + goto out_free; + } mcc[0] = (mcc_val >> 8) & 0xff; mcc[1] = mcc_val & 0xff; @@ -424,38 +453,28 @@ out_free: return ret; } -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) +static int +iwl_acpi_parse_chains_table(union acpi_object *table, + struct iwl_sar_profile_chain *chains, + u8 num_chains, u8 num_sub_bands) { - int i, j, idx = 0; - - /* - * The table from ACPI is flat, but we store it in a - * structured array. - */ - for (i = 0; i < BIOS_SAR_MAX_CHAINS_PER_PROFILE; i++) { - for (j = 0; j < BIOS_SAR_MAX_SUB_BANDS_NUM; j++) { + for (u8 chain = 0; chain < num_chains; chain++) { + for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM; + subband++) { /* if we don't have the values, use the default */ - if (i >= num_chains || j >= num_sub_bands) { - profile->chains[i].subbands[j] = 0; + if (subband >= num_sub_bands) { + chains[chain].subbands[subband] = 0; + } else if (table->type != ACPI_TYPE_INTEGER || + table->integer.value > U8_MAX) { + return -EINVAL; } else { - if (table[idx].type != ACPI_TYPE_INTEGER || - table[idx].integer.value > U8_MAX) - return -EINVAL; - - profile->chains[i].subbands[j] = - table[idx].integer.value; - - idx++; + chains[chain].subbands[subband] = + table->integer.value; + table++; } } } - /* Only if all values were valid can the profile be enabled */ - profile->enabled = enabled; - return 0; } @@ -538,9 +557,11 @@ 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_acpi_sar_set_profile(table, &fwrt->sar_profiles[0], - flags & IWL_SAR_ENABLE_MSK, - num_chains, num_sub_bands); + ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains, + num_chains, num_sub_bands); + if (!ret && flags & IWL_SAR_ENABLE_MSK) + fwrt->sar_profiles[0].enabled = true; + out_free: kfree(data); return ret; @@ -552,7 +573,7 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) bool enabled; int i, n_profiles, tbl_rev, pos; int ret = 0; - u8 num_chains, num_sub_bands; + u8 num_sub_bands; data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD); if (IS_ERR(data)) @@ -568,7 +589,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV2; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2; goto read_table; @@ -584,7 +604,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV1; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1; goto read_table; @@ -600,7 +619,6 @@ int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt) goto out_free; } - num_chains = ACPI_SAR_NUM_CHAINS_REV0; num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0; goto read_table; @@ -632,23 +650,54 @@ read_table: /* the tables start at element 3 */ pos = 3; + BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1); + BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0); + + /* parse non-cdb chains for all profiles */ 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_acpi_sar_set_profile(table, - &fwrt->sar_profiles[i + 1], - enabled, num_chains, - num_sub_bands); + ret = iwl_acpi_parse_chains_table(table, + fwrt->sar_profiles[i + 1].chains, + ACPI_SAR_NUM_CHAINS_REV0, + num_sub_bands); if (ret < 0) - break; + goto out_free; /* go to the next table */ - pos += num_chains * num_sub_bands; + pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands; } + /* non-cdb table revisions */ + if (tbl_rev < 2) + goto set_enabled; + + /* parse cdb chains for all profiles */ + for (i = 0; i < n_profiles; i++) { + struct iwl_sar_profile_chain *chains; + union acpi_object *table; + + table = &wifi_pkg->package.elements[pos]; + chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0]; + ret = iwl_acpi_parse_chains_table(table, + chains, + ACPI_SAR_NUM_CHAINS_REV0, + num_sub_bands); + if (ret < 0) + goto out_free; + + /* go to the next table */ + pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands; + } + +set_enabled: + for (i = 0; i < n_profiles; i++) + fwrt->sar_profiles[i + 1].enabled = enabled; + out_free: kfree(data); return ret; @@ -821,12 +870,12 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) if (IS_ERR(data)) return PTR_ERR(data); - /* try to read ppag table rev 3, 2 or 1 (all have the same data size) */ + /* try to read ppag table rev 1 to 4 (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 <= 3) { + if (tbl_rev >= 1 && tbl_rev <= 4) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; IWL_DEBUG_RADIO(fwrt, "Reading PPAG table (tbl_rev=%d)\n", @@ -856,7 +905,7 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto out_free; read_table: - fwrt->ppag_ver = tbl_rev; + fwrt->ppag_bios_rev = tbl_rev; flags = &wifi_pkg->package.elements[1]; if (flags->type != ACPI_TYPE_INTEGER) { @@ -865,7 +914,7 @@ read_table: } fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value, - fwrt->ppag_ver); + fwrt->ppag_bios_rev); /* * read, verify gain values and save them into the PPAG table. @@ -886,6 +935,7 @@ read_table: } } + fwrt->ppag_bios_source = BIOS_SOURCE_ACPI; ret = 0; out_free: @@ -893,40 +943,39 @@ out_free: return ret; } -void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, - struct iwl_phy_specific_cfg *filters) +int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt) { + struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters; struct iwl_phy_specific_cfg tmp = {}; - union acpi_object *wifi_pkg, *data; + union acpi_object *wifi_pkg, *data __free(kfree); int tbl_rev, i; data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD); if (IS_ERR(data)) - return; + return PTR_ERR(data); wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_WPFC_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg)) - goto out_free; + return PTR_ERR(wifi_pkg); if (tbl_rev != 0) - goto out_free; + return -EINVAL; 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 + 1].type != ACPI_TYPE_INTEGER) - goto out_free; + return -EINVAL; tmp.filter_cfg_chains[i] = cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value); } IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n"); *filters = tmp; -out_free: - kfree(data); + return 0; } IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters); @@ -998,3 +1047,37 @@ out_free: kfree(data); return ret; } + +int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value) +{ + union acpi_object *wifi_pkg, *data; + int ret = -ENOENT; + int tbl_rev; + + data = iwl_acpi_get_object(fwrt->dev, ACPI_DSBR_METHOD); + if (IS_ERR(data)) + return ret; + + wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, + ACPI_DSBR_WIFI_DATA_SIZE, + &tbl_rev); + if (IS_ERR(wifi_pkg)) + goto out_free; + + if (tbl_rev != ACPI_DSBR_WIFI_DATA_REV) { + IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI DSBR revision:%d\n", + tbl_rev); + goto out_free; + } + + if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) + goto out_free; + + *value = wifi_pkg->package.elements[1].integer.value; + IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from ACPI value: 0x%x\n", + *value); + ret = 0; +out_free: + kfree(data); + return ret; +} |
