Path: blob/main/sys/contrib/dev/iwlwifi/fw/acpi.c
108019 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2017 Intel Deutschland GmbH3* Copyright (C) 2019-2025 Intel Corporation4*/5#include <linux/uuid.h>6#include "iwl-drv.h"7#include "iwl-debug.h"8#include "acpi.h"9#include "fw/runtime.h"1011const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,120xA5, 0xB3, 0x1F, 0x73,130x8E, 0x28, 0x5A, 0xDE);1415static const size_t acpi_dsm_size[DSM_FUNC_NUM_FUNCS] = {16[DSM_FUNC_QUERY] = sizeof(u32),17[DSM_FUNC_DISABLE_SRD] = sizeof(u8),18[DSM_FUNC_ENABLE_INDONESIA_5G2] = sizeof(u8),19[DSM_FUNC_ENABLE_6E] = sizeof(u32),20[DSM_FUNC_REGULATORY_CONFIG] = sizeof(u32),21/* Not supported in driver */22[5] = (size_t)0,23[DSM_FUNC_11AX_ENABLEMENT] = sizeof(u32),24[DSM_FUNC_ENABLE_UNII4_CHAN] = sizeof(u32),25[DSM_FUNC_ACTIVATE_CHANNEL] = sizeof(u32),26[DSM_FUNC_FORCE_DISABLE_CHANNELS] = sizeof(u32),27[DSM_FUNC_ENERGY_DETECTION_THRESHOLD] = sizeof(u32),28[DSM_FUNC_RFI_CONFIG] = sizeof(u32),29[DSM_FUNC_ENABLE_11BE] = sizeof(u32),30};3132static int iwl_acpi_get_handle(struct device *dev, acpi_string method,33acpi_handle *ret_handle)34{35acpi_handle root_handle;36acpi_status status;3738root_handle = ACPI_HANDLE(dev);39if (!root_handle) {40IWL_DEBUG_DEV_RADIO(dev,41"ACPI: Could not retrieve root port handle\n");42return -ENOENT;43}4445status = acpi_get_handle(root_handle, method, ret_handle);46if (ACPI_FAILURE(status)) {47IWL_DEBUG_DEV_RADIO(dev,48"ACPI: %s method not found\n", method);49return -ENOENT;50}51return 0;52}5354static void *iwl_acpi_get_object(struct device *dev, acpi_string method)55{56struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};57acpi_handle handle;58acpi_status status;59int ret;6061ret = iwl_acpi_get_handle(dev, method, &handle);62if (ret)63return ERR_PTR(-ENOENT);6465/* Call the method with no arguments */66status = acpi_evaluate_object(handle, NULL, NULL, &buf);67if (ACPI_FAILURE(status)) {68IWL_DEBUG_DEV_RADIO(dev,69"ACPI: %s method invocation failed (status: 0x%x)\n",70method, status);71return ERR_PTR(-ENOENT);72}73return buf.pointer;74}7576/*77* Generic function for evaluating a method defined in the device specific78* method (DSM) interface. The returned acpi object must be freed by calling79* function.80*/81union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev,82int func, union acpi_object *args,83const guid_t *guid)84{85union acpi_object *obj;8687obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), guid, rev, func,88args);89if (!obj) {90IWL_DEBUG_DEV_RADIO(dev,91"ACPI: DSM method invocation failed (rev: %d, func:%d)\n",92rev, func);93return ERR_PTR(-ENOENT);94}95return obj;96}9798/*99* Generic function to evaluate a DSM with no arguments100* and an integer return value,101* (as an integer object or inside a buffer object),102* verify and assign the value in the "value" parameter.103* return 0 in success and the appropriate errno otherwise.104*/105static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func,106const guid_t *guid, u64 *value,107size_t expected_size)108{109union acpi_object *obj;110int ret;111112obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid);113if (IS_ERR(obj)) {114IWL_DEBUG_DEV_RADIO(dev,115"Failed to get DSM object. func= %d\n",116func);117return -ENOENT;118}119120if (obj->type == ACPI_TYPE_INTEGER) {121*value = obj->integer.value;122} else if (obj->type == ACPI_TYPE_BUFFER) {123__le64 le_value = 0;124125if (WARN_ON_ONCE(expected_size > sizeof(le_value))) {126ret = -EINVAL;127goto out;128}129130/* if the buffer size doesn't match the expected size */131if (obj->buffer.length != expected_size)132IWL_DEBUG_DEV_RADIO(dev,133"ACPI: DSM invalid buffer size, padding or truncating (%d)\n",134obj->buffer.length);135136/* assuming LE from Intel BIOS spec */137memcpy(&le_value, obj->buffer.pointer,138min_t(size_t, expected_size, (size_t)obj->buffer.length));139*value = le64_to_cpu(le_value);140} else {141IWL_DEBUG_DEV_RADIO(dev,142"ACPI: DSM method did not return a valid object, type=%d\n",143obj->type);144ret = -EINVAL;145goto out;146}147148IWL_DEBUG_DEV_RADIO(dev,149"ACPI: DSM method evaluated: func=%d, value=%lld\n",150func, *value);151ret = 0;152out:153ACPI_FREE(obj);154return ret;155}156157/*158* This function receives a DSM function number, calculates its expected size159* according to Intel BIOS spec, and fills in the value in a 32-bit field.160* In case the expected size is smaller than 32-bit, padding will be added.161*/162int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt,163enum iwl_dsm_funcs func, u32 *value)164{165size_t expected_size;166u64 tmp;167int ret;168169BUILD_BUG_ON(ARRAY_SIZE(acpi_dsm_size) != DSM_FUNC_NUM_FUNCS);170171if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size) || !func))172return -EINVAL;173174expected_size = acpi_dsm_size[func];175176/* Currently all ACPI DSMs are either 8-bit or 32-bit */177if (expected_size != sizeof(u8) && expected_size != sizeof(u32))178return -EOPNOTSUPP;179180if (!fwrt->acpi_dsm_funcs_valid) {181ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV,182DSM_FUNC_QUERY,183&iwl_guid, &tmp,184acpi_dsm_size[DSM_FUNC_QUERY]);185if (ret) {186/* always indicate BIT(0) to avoid re-reading */187fwrt->acpi_dsm_funcs_valid = BIT(0);188return ret;189}190191IWL_DEBUG_RADIO(fwrt, "ACPI DSM validity bitmap 0x%x\n",192(u32)tmp);193/* always indicate BIT(0) to avoid re-reading */194fwrt->acpi_dsm_funcs_valid = tmp | BIT(0);195}196197if (!(fwrt->acpi_dsm_funcs_valid & BIT(func))) {198IWL_DEBUG_RADIO(fwrt, "ACPI DSM %d not indicated as valid\n",199func);200return -ENODATA;201}202203ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func,204&iwl_guid, &tmp, expected_size);205if (ret)206return ret;207208if ((expected_size == sizeof(u8) && tmp != (u8)tmp) ||209(expected_size == sizeof(u32) && tmp != (u32)tmp))210IWL_DEBUG_RADIO(fwrt,211"DSM value overflows the expected size, truncating\n");212*value = (u32)tmp;213214return 0;215}216217static union acpi_object *218iwl_acpi_get_wifi_pkg_range(struct device *dev,219union acpi_object *data,220int min_data_size,221int max_data_size,222int *tbl_rev)223{224int i;225union acpi_object *wifi_pkg;226227/*228* We need at least one entry in the wifi package that229* describes the domain, and one more entry, otherwise there's230* no point in reading it.231*/232if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))233return ERR_PTR(-EINVAL);234235/*236* We need at least two packages, one for the revision and one237* for the data itself. Also check that the revision is valid238* (i.e. it is an integer (each caller has to check by itself239* if the returned revision is supported)).240*/241if (data->type != ACPI_TYPE_PACKAGE ||242data->package.count < 2 ||243data->package.elements[0].type != ACPI_TYPE_INTEGER) {244IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");245return ERR_PTR(-EINVAL);246}247248*tbl_rev = data->package.elements[0].integer.value;249250/* loop through all the packages to find the one for WiFi */251for (i = 1; i < data->package.count; i++) {252union acpi_object *domain;253254wifi_pkg = &data->package.elements[i];255256/* skip entries that are not a package with the right size */257if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||258wifi_pkg->package.count < min_data_size ||259wifi_pkg->package.count > max_data_size)260continue;261262domain = &wifi_pkg->package.elements[0];263if (domain->type == ACPI_TYPE_INTEGER &&264domain->integer.value == ACPI_WIFI_DOMAIN)265goto found;266}267268return ERR_PTR(-ENOENT);269270found:271return wifi_pkg;272}273274static union acpi_object *275iwl_acpi_get_wifi_pkg(struct device *dev,276union acpi_object *data,277int data_size, int *tbl_rev)278{279return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size,280tbl_rev);281}282283int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt,284struct iwl_tas_data *tas_data)285{286union acpi_object *wifi_pkg, *data;287int ret, tbl_rev, block_list_size, enabled;288u32 tas_selection;289290data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);291if (IS_ERR(data))292return PTR_ERR(data);293294/* try to read wtas table */295wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,296ACPI_WTAS_WIFI_DATA_SIZE,297&tbl_rev);298if (IS_ERR(wifi_pkg)) {299ret = PTR_ERR(wifi_pkg);300goto out_free;301}302303if (tbl_rev < 0 || tbl_rev > 2 ||304wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {305ret = -EINVAL;306goto out_free;307}308309tas_selection = (u32)wifi_pkg->package.elements[1].integer.value;310enabled = tas_selection & IWL_WTAS_ENABLED_MSK;311312IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n",313tas_selection);314tas_data->table_source = BIOS_SOURCE_ACPI;315tas_data->table_revision = tbl_rev;316tas_data->tas_selection = tas_selection;317318IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n",319enabled ? "is" : "not");320321IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);322if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||323wifi_pkg->package.elements[2].integer.value >324IWL_WTAS_BLACK_LIST_MAX) {325IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",326wifi_pkg->package.elements[2].integer.value);327ret = -EINVAL;328goto out_free;329}330331block_list_size = wifi_pkg->package.elements[2].integer.value;332tas_data->block_list_size = block_list_size;333334IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);335336for (int i = 0; i < block_list_size; i++) {337u16 country;338339if (wifi_pkg->package.elements[3 + i].type !=340ACPI_TYPE_INTEGER) {341IWL_DEBUG_RADIO(fwrt,342"TAS invalid array elem %d\n", 3 + i);343ret = -EINVAL;344goto out_free;345}346347country = wifi_pkg->package.elements[3 + i].integer.value;348tas_data->block_list_array[i] = country;349IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);350}351352ret = enabled;353out_free:354kfree(data);355return ret;356}357358int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)359{360union acpi_object *wifi_pkg, *data;361u32 mcc_val;362int ret, tbl_rev;363364data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDD_METHOD);365if (IS_ERR(data))366return PTR_ERR(data);367368wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,369ACPI_WRDD_WIFI_DATA_SIZE,370&tbl_rev);371if (IS_ERR(wifi_pkg)) {372ret = PTR_ERR(wifi_pkg);373goto out_free;374}375376if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||377tbl_rev != 0) {378ret = -EINVAL;379goto out_free;380}381382mcc_val = wifi_pkg->package.elements[1].integer.value;383if (mcc_val != BIOS_MCC_CHINA) {384ret = -EINVAL;385IWL_DEBUG_RADIO(fwrt, "ACPI WRDD is supported only for CN\n");386goto out_free;387}388389mcc[0] = (mcc_val >> 8) & 0xff;390mcc[1] = mcc_val & 0xff;391mcc[2] = '\0';392393ret = 0;394out_free:395kfree(data);396return ret;397}398399int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit)400{401union acpi_object *data, *wifi_pkg;402int tbl_rev, ret = -EINVAL;403404*dflt_pwr_limit = 0;405data = iwl_acpi_get_object(fwrt->dev, ACPI_SPLC_METHOD);406if (IS_ERR(data))407goto out;408409wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,410ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);411if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||412wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER)413goto out_free;414415*dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;416ret = 0;417out_free:418kfree(data);419out:420return ret;421}422423int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)424{425union acpi_object *wifi_pkg, *data;426int ret, tbl_rev;427428data = iwl_acpi_get_object(fwrt->dev, ACPI_ECKV_METHOD);429if (IS_ERR(data))430return PTR_ERR(data);431432wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,433ACPI_ECKV_WIFI_DATA_SIZE,434&tbl_rev);435if (IS_ERR(wifi_pkg)) {436ret = PTR_ERR(wifi_pkg);437goto out_free;438}439440if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||441tbl_rev != 0) {442ret = -EINVAL;443goto out_free;444}445446*extl_clk = wifi_pkg->package.elements[1].integer.value;447448ret = 0;449450out_free:451kfree(data);452return ret;453}454455static int456iwl_acpi_parse_chains_table(union acpi_object *table,457struct iwl_sar_profile_chain *chains,458u8 num_chains, u8 num_sub_bands)459{460for (u8 chain = 0; chain < num_chains; chain++) {461for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM;462subband++) {463/* if we don't have the values, use the default */464if (subband >= num_sub_bands) {465chains[chain].subbands[subband] = 0;466} else if (table->type != ACPI_TYPE_INTEGER ||467table->integer.value > U8_MAX) {468return -EINVAL;469} else {470chains[chain].subbands[subband] =471table->integer.value;472table++;473}474}475}476477return 0;478}479480int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)481{482union acpi_object *wifi_pkg, *table, *data;483int ret, tbl_rev;484u32 flags;485u8 num_chains, num_sub_bands;486487data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);488if (IS_ERR(data))489return PTR_ERR(data);490491/* start by trying to read revision 2 */492wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,493ACPI_WRDS_WIFI_DATA_SIZE_REV2,494&tbl_rev);495if (!IS_ERR(wifi_pkg)) {496if (tbl_rev != 2) {497ret = -EINVAL;498goto out_free;499}500501num_chains = ACPI_SAR_NUM_CHAINS_REV2;502num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;503504goto read_table;505}506507/* then try revision 1 */508wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,509ACPI_WRDS_WIFI_DATA_SIZE_REV1,510&tbl_rev);511if (!IS_ERR(wifi_pkg)) {512if (tbl_rev != 1) {513ret = -EINVAL;514goto out_free;515}516517num_chains = ACPI_SAR_NUM_CHAINS_REV1;518num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;519520goto read_table;521}522523/* then finally revision 0 */524wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,525ACPI_WRDS_WIFI_DATA_SIZE_REV0,526&tbl_rev);527if (!IS_ERR(wifi_pkg)) {528if (tbl_rev != 0) {529ret = -EINVAL;530goto out_free;531}532533num_chains = ACPI_SAR_NUM_CHAINS_REV0;534num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;535536goto read_table;537}538539ret = PTR_ERR(wifi_pkg);540goto out_free;541542read_table:543if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {544ret = -EINVAL;545goto out_free;546}547548IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);549550flags = wifi_pkg->package.elements[1].integer.value;551fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;552553/* position of the actual table */554table = &wifi_pkg->package.elements[2];555556/* The profile from WRDS is officially profile 1, but goes557* into sar_profiles[0] (because we don't have a profile 0).558*/559ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains,560num_chains, num_sub_bands);561if (!ret && flags & IWL_SAR_ENABLE_MSK)562fwrt->sar_profiles[0].enabled = true;563564out_free:565kfree(data);566return ret;567}568569int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)570{571union acpi_object *wifi_pkg, *data;572bool enabled;573int i, n_profiles, tbl_rev, pos;574int ret = 0;575u8 num_sub_bands;576577data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);578if (IS_ERR(data))579return PTR_ERR(data);580581/* start by trying to read revision 2 */582wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,583ACPI_EWRD_WIFI_DATA_SIZE_REV2,584&tbl_rev);585if (!IS_ERR(wifi_pkg)) {586if (tbl_rev != 2) {587ret = -EINVAL;588goto out_free;589}590591num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;592593goto read_table;594}595596/* then try revision 1 */597wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,598ACPI_EWRD_WIFI_DATA_SIZE_REV1,599&tbl_rev);600if (!IS_ERR(wifi_pkg)) {601if (tbl_rev != 1) {602ret = -EINVAL;603goto out_free;604}605606num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;607608goto read_table;609}610611/* then finally revision 0 */612wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,613ACPI_EWRD_WIFI_DATA_SIZE_REV0,614&tbl_rev);615if (!IS_ERR(wifi_pkg)) {616if (tbl_rev != 0) {617ret = -EINVAL;618goto out_free;619}620621num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;622623goto read_table;624}625626ret = PTR_ERR(wifi_pkg);627goto out_free;628629read_table:630if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||631wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {632ret = -EINVAL;633goto out_free;634}635636enabled = !!(wifi_pkg->package.elements[1].integer.value);637n_profiles = wifi_pkg->package.elements[2].integer.value;638639/*640* Check the validity of n_profiles. The EWRD profiles start641* from index 1, so the maximum value allowed here is642* ACPI_SAR_PROFILES_NUM - 1.643*/644if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {645ret = -EINVAL;646goto out_free;647}648649/* the tables start at element 3 */650pos = 3;651652BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1);653BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0);654655/* parse non-cdb chains for all profiles */656for (i = 0; i < n_profiles; i++) {657union acpi_object *table = &wifi_pkg->package.elements[pos];658659/* The EWRD profiles officially go from 2 to 4, but we660* save them in sar_profiles[1-3] (because we don't661* have profile 0). So in the array we start from 1.662*/663ret = iwl_acpi_parse_chains_table(table,664fwrt->sar_profiles[i + 1].chains,665ACPI_SAR_NUM_CHAINS_REV0,666num_sub_bands);667if (ret < 0)668goto out_free;669670/* go to the next table */671pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;672}673674/* non-cdb table revisions */675if (tbl_rev < 2)676goto set_enabled;677678/* parse cdb chains for all profiles */679for (i = 0; i < n_profiles; i++) {680struct iwl_sar_profile_chain *chains;681union acpi_object *table;682683table = &wifi_pkg->package.elements[pos];684chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0];685ret = iwl_acpi_parse_chains_table(table,686chains,687ACPI_SAR_NUM_CHAINS_REV0,688num_sub_bands);689if (ret < 0)690goto out_free;691692/* go to the next table */693pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;694}695696set_enabled:697for (i = 0; i < n_profiles; i++)698fwrt->sar_profiles[i + 1].enabled = enabled;699700out_free:701kfree(data);702return ret;703}704705int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)706{707union acpi_object *wifi_pkg, *data;708int i, j, k, ret, tbl_rev;709u8 num_bands, num_profiles;710static const struct {711u8 revisions;712u8 bands;713u8 profiles;714u8 min_profiles;715} rev_data[] = {716{717.revisions = BIT(3),718.bands = ACPI_GEO_NUM_BANDS_REV2,719.profiles = ACPI_NUM_GEO_PROFILES_REV3,720.min_profiles = BIOS_GEO_MIN_PROFILE_NUM,721},722{723.revisions = BIT(2),724.bands = ACPI_GEO_NUM_BANDS_REV2,725.profiles = ACPI_NUM_GEO_PROFILES,726},727{728.revisions = BIT(0) | BIT(1),729.bands = ACPI_GEO_NUM_BANDS_REV0,730.profiles = ACPI_NUM_GEO_PROFILES,731},732};733int idx;734/* start from one to skip the domain */735int entry_idx = 1;736737BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);738BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);739740data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);741if (IS_ERR(data))742return PTR_ERR(data);743744/* read the highest revision we understand first */745for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {746/* min_profiles != 0 requires num_profiles header */747u32 hdr_size = 1 + !!rev_data[idx].min_profiles;748u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *749rev_data[idx].bands;750u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;751u32 min_size;752753if (!rev_data[idx].min_profiles)754min_size = max_size;755else756min_size = hdr_size +757profile_size * rev_data[idx].min_profiles;758759wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,760min_size, max_size,761&tbl_rev);762if (!IS_ERR(wifi_pkg)) {763if (!(BIT(tbl_rev) & rev_data[idx].revisions))764continue;765766num_bands = rev_data[idx].bands;767num_profiles = rev_data[idx].profiles;768769if (rev_data[idx].min_profiles) {770/* read header that says # of profiles */771union acpi_object *entry;772773entry = &wifi_pkg->package.elements[entry_idx];774entry_idx++;775if (entry->type != ACPI_TYPE_INTEGER ||776entry->integer.value > num_profiles ||777entry->integer.value <778rev_data[idx].min_profiles) {779ret = -EINVAL;780goto out_free;781}782783/*784* Check to see if we received package count785* same as max # of profiles786*/787if (wifi_pkg->package.count !=788hdr_size + profile_size * num_profiles) {789ret = -EINVAL;790goto out_free;791}792793/* Number of valid profiles */794num_profiles = entry->integer.value;795}796goto read_table;797}798}799800if (idx < ARRAY_SIZE(rev_data))801ret = PTR_ERR(wifi_pkg);802else803ret = -ENOENT;804goto out_free;805806read_table:807fwrt->geo_rev = tbl_rev;808for (i = 0; i < num_profiles; i++) {809for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) {810union acpi_object *entry;811812/*813* num_bands is either 2 or 3, if it's only 2 then814* fill the third band (6 GHz) with the values from815* 5 GHz (second band)816*/817if (j >= num_bands) {818fwrt->geo_profiles[i].bands[j].max =819fwrt->geo_profiles[i].bands[1].max;820} else {821entry = &wifi_pkg->package.elements[entry_idx];822entry_idx++;823if (entry->type != ACPI_TYPE_INTEGER ||824entry->integer.value > U8_MAX) {825ret = -EINVAL;826goto out_free;827}828829fwrt->geo_profiles[i].bands[j].max =830entry->integer.value;831}832833for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) {834/* same here as above */835if (j >= num_bands) {836fwrt->geo_profiles[i].bands[j].chains[k] =837fwrt->geo_profiles[i].bands[1].chains[k];838} else {839entry = &wifi_pkg->package.elements[entry_idx];840entry_idx++;841if (entry->type != ACPI_TYPE_INTEGER ||842entry->integer.value > U8_MAX) {843ret = -EINVAL;844goto out_free;845}846847fwrt->geo_profiles[i].bands[j].chains[k] =848entry->integer.value;849}850}851}852}853854fwrt->geo_num_profiles = num_profiles;855fwrt->geo_enabled = true;856ret = 0;857out_free:858kfree(data);859return ret;860}861862int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)863{864union acpi_object *wifi_pkg, *data, *flags;865int i, j, ret, tbl_rev, num_sub_bands = 0;866int idx = 2;867868data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);869if (IS_ERR(data))870return PTR_ERR(data);871872/* try to read ppag table rev 1 to 4 (all have the same data size) */873wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,874ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);875876if (!IS_ERR(wifi_pkg)) {877if (tbl_rev >= 1 && tbl_rev <= 4) {878num_sub_bands = IWL_NUM_SUB_BANDS_V2;879IWL_DEBUG_RADIO(fwrt,880"Reading PPAG table (tbl_rev=%d)\n",881tbl_rev);882goto read_table;883} else {884ret = -EINVAL;885goto out_free;886}887}888889/* try to read ppag table revision 0 */890wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,891ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);892893if (!IS_ERR(wifi_pkg)) {894if (tbl_rev != 0) {895ret = -EINVAL;896goto out_free;897}898num_sub_bands = IWL_NUM_SUB_BANDS_V1;899IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");900goto read_table;901}902903ret = PTR_ERR(wifi_pkg);904goto out_free;905906read_table:907fwrt->ppag_bios_rev = tbl_rev;908flags = &wifi_pkg->package.elements[1];909910if (flags->type != ACPI_TYPE_INTEGER) {911ret = -EINVAL;912goto out_free;913}914915fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value,916fwrt->ppag_bios_rev);917918/*919* read, verify gain values and save them into the PPAG table.920* first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the921* following sub-bands to High-Band (5GHz).922*/923for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {924for (j = 0; j < num_sub_bands; j++) {925union acpi_object *ent;926927ent = &wifi_pkg->package.elements[idx++];928if (ent->type != ACPI_TYPE_INTEGER) {929ret = -EINVAL;930goto out_free;931}932933fwrt->ppag_chains[i].subbands[j] = ent->integer.value;934}935}936937fwrt->ppag_bios_source = BIOS_SOURCE_ACPI;938ret = 0;939940out_free:941kfree(data);942return ret;943}944945int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt)946{947struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters;948struct iwl_phy_specific_cfg tmp = {};949union acpi_object *wifi_pkg, *data __free(kfree);950int tbl_rev, i;951952data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);953if (IS_ERR(data))954return PTR_ERR(data);955956wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,957ACPI_WPFC_WIFI_DATA_SIZE,958&tbl_rev);959if (IS_ERR(wifi_pkg))960return PTR_ERR(wifi_pkg);961962if (tbl_rev != 0)963return -EINVAL;964965BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=966ACPI_WPFC_WIFI_DATA_SIZE - 1);967968for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {969if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)970return -EINVAL;971tmp.filter_cfg_chains[i] =972cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);973}974975IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");976*filters = tmp;977return 0;978}979IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);980981void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt)982{983union acpi_object *wifi_pkg, *data;984int tbl_rev;985986data = iwl_acpi_get_object(fwrt->dev, ACPI_GLAI_METHOD);987if (IS_ERR(data))988return;989990wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,991ACPI_GLAI_WIFI_DATA_SIZE,992&tbl_rev);993if (IS_ERR(wifi_pkg))994goto out_free;995996if (tbl_rev != 0) {997IWL_DEBUG_RADIO(fwrt, "Invalid GLAI revision: %d\n", tbl_rev);998goto out_free;999}10001001if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||1002wifi_pkg->package.elements[1].integer.value > ACPI_GLAI_MAX_STATUS)1003goto out_free;10041005fwrt->uefi_tables_lock_status =1006wifi_pkg->package.elements[1].integer.value;10071008IWL_DEBUG_RADIO(fwrt,1009"Loaded UEFI WIFI GUID lock status: %d from ACPI\n",1010fwrt->uefi_tables_lock_status);1011out_free:1012kfree(data);1013}1014IWL_EXPORT_SYMBOL(iwl_acpi_get_guid_lock_status);10151016int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)1017{1018union acpi_object *wifi_pkg, *data;1019int ret = -ENOENT;1020int tbl_rev;10211022data = iwl_acpi_get_object(fwrt->dev, ACPI_WBEM_METHOD);1023if (IS_ERR(data))1024return ret;10251026wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,1027ACPI_WBEM_WIFI_DATA_SIZE,1028&tbl_rev);1029if (IS_ERR(wifi_pkg))1030goto out_free;10311032if (tbl_rev != IWL_ACPI_WBEM_REVISION) {1033IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI WBEM revision:%d\n",1034tbl_rev);1035goto out_free;1036}10371038if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)1039goto out_free;10401041*value = wifi_pkg->package.elements[1].integer.value &1042IWL_ACPI_WBEM_REV0_MASK;1043IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from ACPI\n");1044ret = 0;1045out_free:1046kfree(data);1047return ret;1048}10491050int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value)1051{1052union acpi_object *wifi_pkg, *data;1053int ret = -ENOENT;1054int tbl_rev;10551056data = iwl_acpi_get_object(fwrt->dev, ACPI_DSBR_METHOD);1057if (IS_ERR(data))1058return ret;10591060wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,1061ACPI_DSBR_WIFI_DATA_SIZE,1062&tbl_rev);1063if (IS_ERR(wifi_pkg))1064goto out_free;10651066if (tbl_rev != ACPI_DSBR_WIFI_DATA_REV) {1067IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI DSBR revision:%d\n",1068tbl_rev);1069goto out_free;1070}10711072if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)1073goto out_free;10741075*value = wifi_pkg->package.elements[1].integer.value;1076IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from ACPI value: 0x%x\n",1077*value);1078ret = 0;1079out_free:1080kfree(data);1081return ret;1082}108310841085