162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Intel Deutschland GmbH 462306a36Sopenharmony_ci * Copyright (C) 2019-2023 Intel Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/uuid.h> 762306a36Sopenharmony_ci#include <linux/dmi.h> 862306a36Sopenharmony_ci#include "iwl-drv.h" 962306a36Sopenharmony_ci#include "iwl-debug.h" 1062306a36Sopenharmony_ci#include "acpi.h" 1162306a36Sopenharmony_ci#include "fw/runtime.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciconst guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6, 1462306a36Sopenharmony_ci 0xA5, 0xB3, 0x1F, 0x73, 1562306a36Sopenharmony_ci 0x8E, 0x28, 0x5A, 0xDE); 1662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_guid); 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ciconst guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29, 1962306a36Sopenharmony_ci 0x81, 0x4F, 0x75, 0xE4, 2062306a36Sopenharmony_ci 0xDD, 0x26, 0xB5, 0xFD); 2162306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rfi_guid); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const struct dmi_system_id dmi_ppag_approved_list[] = { 2462306a36Sopenharmony_ci { .ident = "HP", 2562306a36Sopenharmony_ci .matches = { 2662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "HP"), 2762306a36Sopenharmony_ci }, 2862306a36Sopenharmony_ci }, 2962306a36Sopenharmony_ci { .ident = "SAMSUNG", 3062306a36Sopenharmony_ci .matches = { 3162306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), 3262306a36Sopenharmony_ci }, 3362306a36Sopenharmony_ci }, 3462306a36Sopenharmony_ci { .ident = "MSFT", 3562306a36Sopenharmony_ci .matches = { 3662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), 3762306a36Sopenharmony_ci }, 3862306a36Sopenharmony_ci }, 3962306a36Sopenharmony_ci { .ident = "ASUS", 4062306a36Sopenharmony_ci .matches = { 4162306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), 4262306a36Sopenharmony_ci }, 4362306a36Sopenharmony_ci }, 4462306a36Sopenharmony_ci { .ident = "GOOGLE-HP", 4562306a36Sopenharmony_ci .matches = { 4662306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Google"), 4762306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "HP"), 4862306a36Sopenharmony_ci }, 4962306a36Sopenharmony_ci }, 5062306a36Sopenharmony_ci { .ident = "GOOGLE-ASUS", 5162306a36Sopenharmony_ci .matches = { 5262306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Google"), 5362306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."), 5462306a36Sopenharmony_ci }, 5562306a36Sopenharmony_ci }, 5662306a36Sopenharmony_ci { .ident = "GOOGLE-SAMSUNG", 5762306a36Sopenharmony_ci .matches = { 5862306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Google"), 5962306a36Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"), 6062306a36Sopenharmony_ci }, 6162306a36Sopenharmony_ci }, 6262306a36Sopenharmony_ci { .ident = "DELL", 6362306a36Sopenharmony_ci .matches = { 6462306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 6562306a36Sopenharmony_ci }, 6662306a36Sopenharmony_ci }, 6762306a36Sopenharmony_ci { .ident = "DELL", 6862306a36Sopenharmony_ci .matches = { 6962306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), 7062306a36Sopenharmony_ci }, 7162306a36Sopenharmony_ci }, 7262306a36Sopenharmony_ci { .ident = "RAZER", 7362306a36Sopenharmony_ci .matches = { 7462306a36Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Razer"), 7562306a36Sopenharmony_ci }, 7662306a36Sopenharmony_ci }, 7762306a36Sopenharmony_ci {} 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int iwl_acpi_get_handle(struct device *dev, acpi_string method, 8162306a36Sopenharmony_ci acpi_handle *ret_handle) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci acpi_handle root_handle; 8462306a36Sopenharmony_ci acpi_status status; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci root_handle = ACPI_HANDLE(dev); 8762306a36Sopenharmony_ci if (!root_handle) { 8862306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 8962306a36Sopenharmony_ci "ACPI: Could not retrieve root port handle\n"); 9062306a36Sopenharmony_ci return -ENOENT; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci status = acpi_get_handle(root_handle, method, ret_handle); 9462306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 9562306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 9662306a36Sopenharmony_ci "ACPI: %s method not found\n", method); 9762306a36Sopenharmony_ci return -ENOENT; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void *iwl_acpi_get_object(struct device *dev, acpi_string method) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; 10562306a36Sopenharmony_ci acpi_handle handle; 10662306a36Sopenharmony_ci acpi_status status; 10762306a36Sopenharmony_ci int ret; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci ret = iwl_acpi_get_handle(dev, method, &handle); 11062306a36Sopenharmony_ci if (ret) 11162306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Call the method with no arguments */ 11462306a36Sopenharmony_ci status = acpi_evaluate_object(handle, NULL, NULL, &buf); 11562306a36Sopenharmony_ci if (ACPI_FAILURE(status)) { 11662306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 11762306a36Sopenharmony_ci "ACPI: %s method invocation failed (status: 0x%x)\n", 11862306a36Sopenharmony_ci method, status); 11962306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci return buf.pointer; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Generic function for evaluating a method defined in the device specific 12662306a36Sopenharmony_ci * method (DSM) interface. The returned acpi object must be freed by calling 12762306a36Sopenharmony_ci * function. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func, 13062306a36Sopenharmony_ci union acpi_object *args, 13162306a36Sopenharmony_ci const guid_t *guid) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci union acpi_object *obj; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), guid, rev, func, 13662306a36Sopenharmony_ci args); 13762306a36Sopenharmony_ci if (!obj) { 13862306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 13962306a36Sopenharmony_ci "ACPI: DSM method invocation failed (rev: %d, func:%d)\n", 14062306a36Sopenharmony_ci rev, func); 14162306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci return obj; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* 14762306a36Sopenharmony_ci * Generic function to evaluate a DSM with no arguments 14862306a36Sopenharmony_ci * and an integer return value, 14962306a36Sopenharmony_ci * (as an integer object or inside a buffer object), 15062306a36Sopenharmony_ci * verify and assign the value in the "value" parameter. 15162306a36Sopenharmony_ci * return 0 in success and the appropriate errno otherwise. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_cistatic int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, 15462306a36Sopenharmony_ci const guid_t *guid, u64 *value, 15562306a36Sopenharmony_ci size_t expected_size) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci union acpi_object *obj; 15862306a36Sopenharmony_ci int ret = 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid); 16162306a36Sopenharmony_ci if (IS_ERR(obj)) { 16262306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 16362306a36Sopenharmony_ci "Failed to get DSM object. func= %d\n", 16462306a36Sopenharmony_ci func); 16562306a36Sopenharmony_ci return -ENOENT; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (obj->type == ACPI_TYPE_INTEGER) { 16962306a36Sopenharmony_ci *value = obj->integer.value; 17062306a36Sopenharmony_ci } else if (obj->type == ACPI_TYPE_BUFFER) { 17162306a36Sopenharmony_ci __le64 le_value = 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (WARN_ON_ONCE(expected_size > sizeof(le_value))) 17462306a36Sopenharmony_ci return -EINVAL; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* if the buffer size doesn't match the expected size */ 17762306a36Sopenharmony_ci if (obj->buffer.length != expected_size) 17862306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 17962306a36Sopenharmony_ci "ACPI: DSM invalid buffer size, padding or truncating (%d)\n", 18062306a36Sopenharmony_ci obj->buffer.length); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* assuming LE from Intel BIOS spec */ 18362306a36Sopenharmony_ci memcpy(&le_value, obj->buffer.pointer, 18462306a36Sopenharmony_ci min_t(size_t, expected_size, (size_t)obj->buffer.length)); 18562306a36Sopenharmony_ci *value = le64_to_cpu(le_value); 18662306a36Sopenharmony_ci } else { 18762306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 18862306a36Sopenharmony_ci "ACPI: DSM method did not return a valid object, type=%d\n", 18962306a36Sopenharmony_ci obj->type); 19062306a36Sopenharmony_ci ret = -EINVAL; 19162306a36Sopenharmony_ci goto out; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, 19562306a36Sopenharmony_ci "ACPI: DSM method evaluated: func=%d, ret=%d\n", 19662306a36Sopenharmony_ci func, ret); 19762306a36Sopenharmony_ciout: 19862306a36Sopenharmony_ci ACPI_FREE(obj); 19962306a36Sopenharmony_ci return ret; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * Evaluate a DSM with no arguments and a u8 return value, 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ciint iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, 20662306a36Sopenharmony_ci const guid_t *guid, u8 *value) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci int ret; 20962306a36Sopenharmony_ci u64 val; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = iwl_acpi_get_dsm_integer(dev, rev, func, 21262306a36Sopenharmony_ci guid, &val, sizeof(u8)); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (ret < 0) 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* cast val (u64) to be u8 */ 21862306a36Sopenharmony_ci *value = (u8)val; 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/* 22462306a36Sopenharmony_ci * Evaluate a DSM with no arguments and a u32 return value, 22562306a36Sopenharmony_ci */ 22662306a36Sopenharmony_ciint iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func, 22762306a36Sopenharmony_ci const guid_t *guid, u32 *value) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int ret; 23062306a36Sopenharmony_ci u64 val; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = iwl_acpi_get_dsm_integer(dev, rev, func, 23362306a36Sopenharmony_ci guid, &val, sizeof(u32)); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (ret < 0) 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* cast val (u64) to be u32 */ 23962306a36Sopenharmony_ci *value = (u32)val; 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u32); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic union acpi_object * 24562306a36Sopenharmony_ciiwl_acpi_get_wifi_pkg_range(struct device *dev, 24662306a36Sopenharmony_ci union acpi_object *data, 24762306a36Sopenharmony_ci int min_data_size, 24862306a36Sopenharmony_ci int max_data_size, 24962306a36Sopenharmony_ci int *tbl_rev) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci int i; 25262306a36Sopenharmony_ci union acpi_object *wifi_pkg; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * We need at least one entry in the wifi package that 25662306a36Sopenharmony_ci * describes the domain, and one more entry, otherwise there's 25762306a36Sopenharmony_ci * no point in reading it. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size)) 26062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* 26362306a36Sopenharmony_ci * We need at least two packages, one for the revision and one 26462306a36Sopenharmony_ci * for the data itself. Also check that the revision is valid 26562306a36Sopenharmony_ci * (i.e. it is an integer (each caller has to check by itself 26662306a36Sopenharmony_ci * if the returned revision is supported)). 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (data->type != ACPI_TYPE_PACKAGE || 26962306a36Sopenharmony_ci data->package.count < 2 || 27062306a36Sopenharmony_ci data->package.elements[0].type != ACPI_TYPE_INTEGER) { 27162306a36Sopenharmony_ci IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n"); 27262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci *tbl_rev = data->package.elements[0].integer.value; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* loop through all the packages to find the one for WiFi */ 27862306a36Sopenharmony_ci for (i = 1; i < data->package.count; i++) { 27962306a36Sopenharmony_ci union acpi_object *domain; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci wifi_pkg = &data->package.elements[i]; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* skip entries that are not a package with the right size */ 28462306a36Sopenharmony_ci if (wifi_pkg->type != ACPI_TYPE_PACKAGE || 28562306a36Sopenharmony_ci wifi_pkg->package.count < min_data_size || 28662306a36Sopenharmony_ci wifi_pkg->package.count > max_data_size) 28762306a36Sopenharmony_ci continue; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci domain = &wifi_pkg->package.elements[0]; 29062306a36Sopenharmony_ci if (domain->type == ACPI_TYPE_INTEGER && 29162306a36Sopenharmony_ci domain->integer.value == ACPI_WIFI_DOMAIN) 29262306a36Sopenharmony_ci goto found; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cifound: 29862306a36Sopenharmony_ci return wifi_pkg; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_cistatic union acpi_object * 30262306a36Sopenharmony_ciiwl_acpi_get_wifi_pkg(struct device *dev, 30362306a36Sopenharmony_ci union acpi_object *data, 30462306a36Sopenharmony_ci int data_size, int *tbl_rev) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size, 30762306a36Sopenharmony_ci tbl_rev); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciint iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, 31262306a36Sopenharmony_ci union iwl_tas_config_cmd *cmd, int fw_ver) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci union acpi_object *wifi_pkg, *data; 31562306a36Sopenharmony_ci int ret, tbl_rev, i, block_list_size, enabled; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD); 31862306a36Sopenharmony_ci if (IS_ERR(data)) 31962306a36Sopenharmony_ci return PTR_ERR(data); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* try to read wtas table revision 1 or revision 0*/ 32262306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 32362306a36Sopenharmony_ci ACPI_WTAS_WIFI_DATA_SIZE, 32462306a36Sopenharmony_ci &tbl_rev); 32562306a36Sopenharmony_ci if (IS_ERR(wifi_pkg)) { 32662306a36Sopenharmony_ci ret = PTR_ERR(wifi_pkg); 32762306a36Sopenharmony_ci goto out_free; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (tbl_rev == 1 && wifi_pkg->package.elements[1].type == 33162306a36Sopenharmony_ci ACPI_TYPE_INTEGER) { 33262306a36Sopenharmony_ci u32 tas_selection = 33362306a36Sopenharmony_ci (u32)wifi_pkg->package.elements[1].integer.value; 33462306a36Sopenharmony_ci u16 override_iec = 33562306a36Sopenharmony_ci (tas_selection & ACPI_WTAS_OVERRIDE_IEC_MSK) >> ACPI_WTAS_OVERRIDE_IEC_POS; 33662306a36Sopenharmony_ci u16 enabled_iec = (tas_selection & ACPI_WTAS_ENABLE_IEC_MSK) >> 33762306a36Sopenharmony_ci ACPI_WTAS_ENABLE_IEC_POS; 33862306a36Sopenharmony_ci u8 usa_tas_uhb = (tas_selection & ACPI_WTAS_USA_UHB_MSK) >> ACPI_WTAS_USA_UHB_POS; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci enabled = tas_selection & ACPI_WTAS_ENABLED_MSK; 34262306a36Sopenharmony_ci if (fw_ver <= 3) { 34362306a36Sopenharmony_ci cmd->v3.override_tas_iec = cpu_to_le16(override_iec); 34462306a36Sopenharmony_ci cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec); 34562306a36Sopenharmony_ci } else { 34662306a36Sopenharmony_ci cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb; 34762306a36Sopenharmony_ci cmd->v4.override_tas_iec = (u8)override_iec; 34862306a36Sopenharmony_ci cmd->v4.enable_tas_iec = (u8)enabled_iec; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci } else if (tbl_rev == 0 && 35262306a36Sopenharmony_ci wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) { 35362306a36Sopenharmony_ci enabled = !!wifi_pkg->package.elements[1].integer.value; 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci ret = -EINVAL; 35662306a36Sopenharmony_ci goto out_free; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!enabled) { 36062306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); 36162306a36Sopenharmony_ci ret = 0; 36262306a36Sopenharmony_ci goto out_free; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev); 36662306a36Sopenharmony_ci if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER || 36762306a36Sopenharmony_ci wifi_pkg->package.elements[2].integer.value > 36862306a36Sopenharmony_ci APCI_WTAS_BLACK_LIST_MAX) { 36962306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n", 37062306a36Sopenharmony_ci wifi_pkg->package.elements[2].integer.value); 37162306a36Sopenharmony_ci ret = -EINVAL; 37262306a36Sopenharmony_ci goto out_free; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci block_list_size = wifi_pkg->package.elements[2].integer.value; 37562306a36Sopenharmony_ci cmd->v4.block_list_size = cpu_to_le32(block_list_size); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size); 37862306a36Sopenharmony_ci if (block_list_size > APCI_WTAS_BLACK_LIST_MAX) { 37962306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n", 38062306a36Sopenharmony_ci block_list_size); 38162306a36Sopenharmony_ci ret = -EINVAL; 38262306a36Sopenharmony_ci goto out_free; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci for (i = 0; i < block_list_size; i++) { 38662306a36Sopenharmony_ci u32 country; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (wifi_pkg->package.elements[3 + i].type != 38962306a36Sopenharmony_ci ACPI_TYPE_INTEGER) { 39062306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 39162306a36Sopenharmony_ci "TAS invalid array elem %d\n", 3 + i); 39262306a36Sopenharmony_ci ret = -EINVAL; 39362306a36Sopenharmony_ci goto out_free; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci country = wifi_pkg->package.elements[3 + i].integer.value; 39762306a36Sopenharmony_ci cmd->v4.block_list_array[i] = cpu_to_le32(country); 39862306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci ret = 1; 40262306a36Sopenharmony_ciout_free: 40362306a36Sopenharmony_ci kfree(data); 40462306a36Sopenharmony_ci return ret; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_tas); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ciint iwl_acpi_get_mcc(struct device *dev, char *mcc) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci union acpi_object *wifi_pkg, *data; 41162306a36Sopenharmony_ci u32 mcc_val; 41262306a36Sopenharmony_ci int ret, tbl_rev; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD); 41562306a36Sopenharmony_ci if (IS_ERR(data)) 41662306a36Sopenharmony_ci return PTR_ERR(data); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE, 41962306a36Sopenharmony_ci &tbl_rev); 42062306a36Sopenharmony_ci if (IS_ERR(wifi_pkg)) { 42162306a36Sopenharmony_ci ret = PTR_ERR(wifi_pkg); 42262306a36Sopenharmony_ci goto out_free; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER || 42662306a36Sopenharmony_ci tbl_rev != 0) { 42762306a36Sopenharmony_ci ret = -EINVAL; 42862306a36Sopenharmony_ci goto out_free; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci mcc_val = wifi_pkg->package.elements[1].integer.value; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci mcc[0] = (mcc_val >> 8) & 0xff; 43462306a36Sopenharmony_ci mcc[1] = mcc_val & 0xff; 43562306a36Sopenharmony_ci mcc[2] = '\0'; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ret = 0; 43862306a36Sopenharmony_ciout_free: 43962306a36Sopenharmony_ci kfree(data); 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_mcc); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ciu64 iwl_acpi_get_pwr_limit(struct device *dev) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci union acpi_object *data, *wifi_pkg; 44762306a36Sopenharmony_ci u64 dflt_pwr_limit; 44862306a36Sopenharmony_ci int tbl_rev; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD); 45162306a36Sopenharmony_ci if (IS_ERR(data)) { 45262306a36Sopenharmony_ci dflt_pwr_limit = 0; 45362306a36Sopenharmony_ci goto out; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, 45762306a36Sopenharmony_ci ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev); 45862306a36Sopenharmony_ci if (IS_ERR(wifi_pkg) || tbl_rev != 0 || 45962306a36Sopenharmony_ci wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) { 46062306a36Sopenharmony_ci dflt_pwr_limit = 0; 46162306a36Sopenharmony_ci goto out_free; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value; 46562306a36Sopenharmony_ciout_free: 46662306a36Sopenharmony_ci kfree(data); 46762306a36Sopenharmony_ciout: 46862306a36Sopenharmony_ci return dflt_pwr_limit; 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ciint iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci union acpi_object *wifi_pkg, *data; 47562306a36Sopenharmony_ci int ret, tbl_rev; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD); 47862306a36Sopenharmony_ci if (IS_ERR(data)) 47962306a36Sopenharmony_ci return PTR_ERR(data); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE, 48262306a36Sopenharmony_ci &tbl_rev); 48362306a36Sopenharmony_ci if (IS_ERR(wifi_pkg)) { 48462306a36Sopenharmony_ci ret = PTR_ERR(wifi_pkg); 48562306a36Sopenharmony_ci goto out_free; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER || 48962306a36Sopenharmony_ci tbl_rev != 0) { 49062306a36Sopenharmony_ci ret = -EINVAL; 49162306a36Sopenharmony_ci goto out_free; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci *extl_clk = wifi_pkg->package.elements[1].integer.value; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ciout_free: 49962306a36Sopenharmony_ci kfree(data); 50062306a36Sopenharmony_ci return ret; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_eckv); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cistatic int iwl_sar_set_profile(union acpi_object *table, 50562306a36Sopenharmony_ci struct iwl_sar_profile *profile, 50662306a36Sopenharmony_ci bool enabled, u8 num_chains, u8 num_sub_bands) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci int i, j, idx = 0; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* 51162306a36Sopenharmony_ci * The table from ACPI is flat, but we store it in a 51262306a36Sopenharmony_ci * structured array. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ci for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV2; i++) { 51562306a36Sopenharmony_ci for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS_REV2; j++) { 51662306a36Sopenharmony_ci /* if we don't have the values, use the default */ 51762306a36Sopenharmony_ci if (i >= num_chains || j >= num_sub_bands) { 51862306a36Sopenharmony_ci profile->chains[i].subbands[j] = 0; 51962306a36Sopenharmony_ci } else { 52062306a36Sopenharmony_ci if (table[idx].type != ACPI_TYPE_INTEGER || 52162306a36Sopenharmony_ci table[idx].integer.value > U8_MAX) 52262306a36Sopenharmony_ci return -EINVAL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci profile->chains[i].subbands[j] = 52562306a36Sopenharmony_ci table[idx].integer.value; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci idx++; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Only if all values were valid can the profile be enabled */ 53362306a36Sopenharmony_ci profile->enabled = enabled; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt, 53962306a36Sopenharmony_ci __le16 *per_chain, u32 n_subbands, 54062306a36Sopenharmony_ci int prof_a, int prof_b) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci int profs[ACPI_SAR_NUM_CHAINS_REV0] = { prof_a, prof_b }; 54362306a36Sopenharmony_ci int i, j; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV0; i++) { 54662306a36Sopenharmony_ci struct iwl_sar_profile *prof; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* don't allow SAR to be disabled (profile 0 means disable) */ 54962306a36Sopenharmony_ci if (profs[i] == 0) 55062306a36Sopenharmony_ci return -EPERM; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */ 55362306a36Sopenharmony_ci if (profs[i] > ACPI_SAR_PROFILE_NUM) 55462306a36Sopenharmony_ci return -EINVAL; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* profiles go from 1 to 4, so decrement to access the array */ 55762306a36Sopenharmony_ci prof = &fwrt->sar_profiles[profs[i] - 1]; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* if the profile is disabled, do nothing */ 56062306a36Sopenharmony_ci if (!prof->enabled) { 56162306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n", 56262306a36Sopenharmony_ci profs[i]); 56362306a36Sopenharmony_ci /* 56462306a36Sopenharmony_ci * if one of the profiles is disabled, we 56562306a36Sopenharmony_ci * ignore all of them and return 1 to 56662306a36Sopenharmony_ci * differentiate disabled from other failures. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci return 1; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci IWL_DEBUG_INFO(fwrt, 57262306a36Sopenharmony_ci "SAR EWRD: chain %d profile index %d\n", 57362306a36Sopenharmony_ci i, profs[i]); 57462306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i); 57562306a36Sopenharmony_ci for (j = 0; j < n_subbands; j++) { 57662306a36Sopenharmony_ci per_chain[i * n_subbands + j] = 57762306a36Sopenharmony_ci cpu_to_le16(prof->chains[i].subbands[j]); 57862306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n", 57962306a36Sopenharmony_ci j, prof->chains[i].subbands[j]); 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return 0; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ciint iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, 58762306a36Sopenharmony_ci __le16 *per_chain, u32 n_tables, u32 n_subbands, 58862306a36Sopenharmony_ci int prof_a, int prof_b) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci int i, ret = 0; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci for (i = 0; i < n_tables; i++) { 59362306a36Sopenharmony_ci ret = iwl_sar_fill_table(fwrt, 59462306a36Sopenharmony_ci &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAINS_REV0], 59562306a36Sopenharmony_ci n_subbands, prof_a, prof_b); 59662306a36Sopenharmony_ci if (ret) 59762306a36Sopenharmony_ci break; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci return ret; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_select_profile); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ciint iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci union acpi_object *wifi_pkg, *table, *data; 60762306a36Sopenharmony_ci int ret, tbl_rev; 60862306a36Sopenharmony_ci u32 flags; 60962306a36Sopenharmony_ci u8 num_chains, num_sub_bands; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD); 61262306a36Sopenharmony_ci if (IS_ERR(data)) 61362306a36Sopenharmony_ci return PTR_ERR(data); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* start by trying to read revision 2 */ 61662306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 61762306a36Sopenharmony_ci ACPI_WRDS_WIFI_DATA_SIZE_REV2, 61862306a36Sopenharmony_ci &tbl_rev); 61962306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 62062306a36Sopenharmony_ci if (tbl_rev != 2) { 62162306a36Sopenharmony_ci ret = -EINVAL; 62262306a36Sopenharmony_ci goto out_free; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci num_chains = ACPI_SAR_NUM_CHAINS_REV2; 62662306a36Sopenharmony_ci num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci goto read_table; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* then try revision 1 */ 63262306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 63362306a36Sopenharmony_ci ACPI_WRDS_WIFI_DATA_SIZE_REV1, 63462306a36Sopenharmony_ci &tbl_rev); 63562306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 63662306a36Sopenharmony_ci if (tbl_rev != 1) { 63762306a36Sopenharmony_ci ret = -EINVAL; 63862306a36Sopenharmony_ci goto out_free; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci num_chains = ACPI_SAR_NUM_CHAINS_REV1; 64262306a36Sopenharmony_ci num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci goto read_table; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci /* then finally revision 0 */ 64862306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 64962306a36Sopenharmony_ci ACPI_WRDS_WIFI_DATA_SIZE_REV0, 65062306a36Sopenharmony_ci &tbl_rev); 65162306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 65262306a36Sopenharmony_ci if (tbl_rev != 0) { 65362306a36Sopenharmony_ci ret = -EINVAL; 65462306a36Sopenharmony_ci goto out_free; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci num_chains = ACPI_SAR_NUM_CHAINS_REV0; 65862306a36Sopenharmony_ci num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci goto read_table; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci ret = PTR_ERR(wifi_pkg); 66462306a36Sopenharmony_ci goto out_free; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ciread_table: 66762306a36Sopenharmony_ci if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { 66862306a36Sopenharmony_ci ret = -EINVAL; 66962306a36Sopenharmony_ci goto out_free; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci flags = wifi_pkg->package.elements[1].integer.value; 67562306a36Sopenharmony_ci fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* position of the actual table */ 67862306a36Sopenharmony_ci table = &wifi_pkg->package.elements[2]; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* The profile from WRDS is officially profile 1, but goes 68162306a36Sopenharmony_ci * into sar_profiles[0] (because we don't have a profile 0). 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_ci ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], 68462306a36Sopenharmony_ci flags & IWL_SAR_ENABLE_MSK, 68562306a36Sopenharmony_ci num_chains, num_sub_bands); 68662306a36Sopenharmony_ciout_free: 68762306a36Sopenharmony_ci kfree(data); 68862306a36Sopenharmony_ci return ret; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ciint iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci union acpi_object *wifi_pkg, *data; 69562306a36Sopenharmony_ci bool enabled; 69662306a36Sopenharmony_ci int i, n_profiles, tbl_rev, pos; 69762306a36Sopenharmony_ci int ret = 0; 69862306a36Sopenharmony_ci u8 num_chains, num_sub_bands; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD); 70162306a36Sopenharmony_ci if (IS_ERR(data)) 70262306a36Sopenharmony_ci return PTR_ERR(data); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci /* start by trying to read revision 2 */ 70562306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 70662306a36Sopenharmony_ci ACPI_EWRD_WIFI_DATA_SIZE_REV2, 70762306a36Sopenharmony_ci &tbl_rev); 70862306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 70962306a36Sopenharmony_ci if (tbl_rev != 2) { 71062306a36Sopenharmony_ci ret = -EINVAL; 71162306a36Sopenharmony_ci goto out_free; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci num_chains = ACPI_SAR_NUM_CHAINS_REV2; 71562306a36Sopenharmony_ci num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci goto read_table; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* then try revision 1 */ 72162306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 72262306a36Sopenharmony_ci ACPI_EWRD_WIFI_DATA_SIZE_REV1, 72362306a36Sopenharmony_ci &tbl_rev); 72462306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 72562306a36Sopenharmony_ci if (tbl_rev != 1) { 72662306a36Sopenharmony_ci ret = -EINVAL; 72762306a36Sopenharmony_ci goto out_free; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci num_chains = ACPI_SAR_NUM_CHAINS_REV1; 73162306a36Sopenharmony_ci num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci goto read_table; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* then finally revision 0 */ 73762306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 73862306a36Sopenharmony_ci ACPI_EWRD_WIFI_DATA_SIZE_REV0, 73962306a36Sopenharmony_ci &tbl_rev); 74062306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 74162306a36Sopenharmony_ci if (tbl_rev != 0) { 74262306a36Sopenharmony_ci ret = -EINVAL; 74362306a36Sopenharmony_ci goto out_free; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci num_chains = ACPI_SAR_NUM_CHAINS_REV0; 74762306a36Sopenharmony_ci num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci goto read_table; 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ret = PTR_ERR(wifi_pkg); 75362306a36Sopenharmony_ci goto out_free; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ciread_table: 75662306a36Sopenharmony_ci if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER || 75762306a36Sopenharmony_ci wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) { 75862306a36Sopenharmony_ci ret = -EINVAL; 75962306a36Sopenharmony_ci goto out_free; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci enabled = !!(wifi_pkg->package.elements[1].integer.value); 76362306a36Sopenharmony_ci n_profiles = wifi_pkg->package.elements[2].integer.value; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci /* 76662306a36Sopenharmony_ci * Check the validity of n_profiles. The EWRD profiles start 76762306a36Sopenharmony_ci * from index 1, so the maximum value allowed here is 76862306a36Sopenharmony_ci * ACPI_SAR_PROFILES_NUM - 1. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_ci if (n_profiles >= ACPI_SAR_PROFILE_NUM) { 77162306a36Sopenharmony_ci ret = -EINVAL; 77262306a36Sopenharmony_ci goto out_free; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* the tables start at element 3 */ 77662306a36Sopenharmony_ci pos = 3; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci for (i = 0; i < n_profiles; i++) { 77962306a36Sopenharmony_ci /* The EWRD profiles officially go from 2 to 4, but we 78062306a36Sopenharmony_ci * save them in sar_profiles[1-3] (because we don't 78162306a36Sopenharmony_ci * have profile 0). So in the array we start from 1. 78262306a36Sopenharmony_ci */ 78362306a36Sopenharmony_ci ret = iwl_sar_set_profile(&wifi_pkg->package.elements[pos], 78462306a36Sopenharmony_ci &fwrt->sar_profiles[i + 1], enabled, 78562306a36Sopenharmony_ci num_chains, num_sub_bands); 78662306a36Sopenharmony_ci if (ret < 0) 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci /* go to the next table */ 79062306a36Sopenharmony_ci pos += num_chains * num_sub_bands; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ciout_free: 79462306a36Sopenharmony_ci kfree(data); 79562306a36Sopenharmony_ci return ret; 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ciint iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci union acpi_object *wifi_pkg, *data; 80262306a36Sopenharmony_ci int i, j, k, ret, tbl_rev; 80362306a36Sopenharmony_ci u8 num_bands, num_profiles; 80462306a36Sopenharmony_ci static const struct { 80562306a36Sopenharmony_ci u8 revisions; 80662306a36Sopenharmony_ci u8 bands; 80762306a36Sopenharmony_ci u8 profiles; 80862306a36Sopenharmony_ci u8 min_profiles; 80962306a36Sopenharmony_ci } rev_data[] = { 81062306a36Sopenharmony_ci { 81162306a36Sopenharmony_ci .revisions = BIT(3), 81262306a36Sopenharmony_ci .bands = ACPI_GEO_NUM_BANDS_REV2, 81362306a36Sopenharmony_ci .profiles = ACPI_NUM_GEO_PROFILES_REV3, 81462306a36Sopenharmony_ci .min_profiles = 3, 81562306a36Sopenharmony_ci }, 81662306a36Sopenharmony_ci { 81762306a36Sopenharmony_ci .revisions = BIT(2), 81862306a36Sopenharmony_ci .bands = ACPI_GEO_NUM_BANDS_REV2, 81962306a36Sopenharmony_ci .profiles = ACPI_NUM_GEO_PROFILES, 82062306a36Sopenharmony_ci }, 82162306a36Sopenharmony_ci { 82262306a36Sopenharmony_ci .revisions = BIT(0) | BIT(1), 82362306a36Sopenharmony_ci .bands = ACPI_GEO_NUM_BANDS_REV0, 82462306a36Sopenharmony_ci .profiles = ACPI_NUM_GEO_PROFILES, 82562306a36Sopenharmony_ci }, 82662306a36Sopenharmony_ci }; 82762306a36Sopenharmony_ci int idx; 82862306a36Sopenharmony_ci /* start from one to skip the domain */ 82962306a36Sopenharmony_ci int entry_idx = 1; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3); 83262306a36Sopenharmony_ci BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD); 83562306a36Sopenharmony_ci if (IS_ERR(data)) 83662306a36Sopenharmony_ci return PTR_ERR(data); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* read the highest revision we understand first */ 83962306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) { 84062306a36Sopenharmony_ci /* min_profiles != 0 requires num_profiles header */ 84162306a36Sopenharmony_ci u32 hdr_size = 1 + !!rev_data[idx].min_profiles; 84262306a36Sopenharmony_ci u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE * 84362306a36Sopenharmony_ci rev_data[idx].bands; 84462306a36Sopenharmony_ci u32 max_size = hdr_size + profile_size * rev_data[idx].profiles; 84562306a36Sopenharmony_ci u32 min_size; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (!rev_data[idx].min_profiles) 84862306a36Sopenharmony_ci min_size = max_size; 84962306a36Sopenharmony_ci else 85062306a36Sopenharmony_ci min_size = hdr_size + 85162306a36Sopenharmony_ci profile_size * rev_data[idx].min_profiles; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data, 85462306a36Sopenharmony_ci min_size, max_size, 85562306a36Sopenharmony_ci &tbl_rev); 85662306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 85762306a36Sopenharmony_ci if (!(BIT(tbl_rev) & rev_data[idx].revisions)) 85862306a36Sopenharmony_ci continue; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci num_bands = rev_data[idx].bands; 86162306a36Sopenharmony_ci num_profiles = rev_data[idx].profiles; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if (rev_data[idx].min_profiles) { 86462306a36Sopenharmony_ci /* read header that says # of profiles */ 86562306a36Sopenharmony_ci union acpi_object *entry; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci entry = &wifi_pkg->package.elements[entry_idx]; 86862306a36Sopenharmony_ci entry_idx++; 86962306a36Sopenharmony_ci if (entry->type != ACPI_TYPE_INTEGER || 87062306a36Sopenharmony_ci entry->integer.value > num_profiles) { 87162306a36Sopenharmony_ci ret = -EINVAL; 87262306a36Sopenharmony_ci goto out_free; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci num_profiles = entry->integer.value; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* 87762306a36Sopenharmony_ci * this also validates >= min_profiles since we 87862306a36Sopenharmony_ci * otherwise wouldn't have gotten the data when 87962306a36Sopenharmony_ci * looking up in ACPI 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_ci if (wifi_pkg->package.count != 88262306a36Sopenharmony_ci hdr_size + profile_size * num_profiles) { 88362306a36Sopenharmony_ci ret = -EINVAL; 88462306a36Sopenharmony_ci goto out_free; 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci } 88762306a36Sopenharmony_ci goto read_table; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (idx < ARRAY_SIZE(rev_data)) 89262306a36Sopenharmony_ci ret = PTR_ERR(wifi_pkg); 89362306a36Sopenharmony_ci else 89462306a36Sopenharmony_ci ret = -ENOENT; 89562306a36Sopenharmony_ci goto out_free; 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ciread_table: 89862306a36Sopenharmony_ci fwrt->geo_rev = tbl_rev; 89962306a36Sopenharmony_ci for (i = 0; i < num_profiles; i++) { 90062306a36Sopenharmony_ci for (j = 0; j < ACPI_GEO_NUM_BANDS_REV2; j++) { 90162306a36Sopenharmony_ci union acpi_object *entry; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* 90462306a36Sopenharmony_ci * num_bands is either 2 or 3, if it's only 2 then 90562306a36Sopenharmony_ci * fill the third band (6 GHz) with the values from 90662306a36Sopenharmony_ci * 5 GHz (second band) 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ci if (j >= num_bands) { 90962306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[j].max = 91062306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[1].max; 91162306a36Sopenharmony_ci } else { 91262306a36Sopenharmony_ci entry = &wifi_pkg->package.elements[entry_idx]; 91362306a36Sopenharmony_ci entry_idx++; 91462306a36Sopenharmony_ci if (entry->type != ACPI_TYPE_INTEGER || 91562306a36Sopenharmony_ci entry->integer.value > U8_MAX) { 91662306a36Sopenharmony_ci ret = -EINVAL; 91762306a36Sopenharmony_ci goto out_free; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[j].max = 92162306a36Sopenharmony_ci entry->integer.value; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci for (k = 0; k < ACPI_GEO_NUM_CHAINS; k++) { 92562306a36Sopenharmony_ci /* same here as above */ 92662306a36Sopenharmony_ci if (j >= num_bands) { 92762306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[j].chains[k] = 92862306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[1].chains[k]; 92962306a36Sopenharmony_ci } else { 93062306a36Sopenharmony_ci entry = &wifi_pkg->package.elements[entry_idx]; 93162306a36Sopenharmony_ci entry_idx++; 93262306a36Sopenharmony_ci if (entry->type != ACPI_TYPE_INTEGER || 93362306a36Sopenharmony_ci entry->integer.value > U8_MAX) { 93462306a36Sopenharmony_ci ret = -EINVAL; 93562306a36Sopenharmony_ci goto out_free; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[j].chains[k] = 93962306a36Sopenharmony_ci entry->integer.value; 94062306a36Sopenharmony_ci } 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci } 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci fwrt->geo_num_profiles = num_profiles; 94662306a36Sopenharmony_ci fwrt->geo_enabled = true; 94762306a36Sopenharmony_ci ret = 0; 94862306a36Sopenharmony_ciout_free: 94962306a36Sopenharmony_ci kfree(data); 95062306a36Sopenharmony_ci return ret; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cibool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * The PER_CHAIN_LIMIT_OFFSET_CMD command is not supported on 95862306a36Sopenharmony_ci * earlier firmware versions. Unfortunately, we don't have a 95962306a36Sopenharmony_ci * TLV API flag to rely on, so rely on the major version which 96062306a36Sopenharmony_ci * is in the first byte of ucode_ver. This was implemented 96162306a36Sopenharmony_ci * initially on version 38 and then backported to 17. It was 96262306a36Sopenharmony_ci * also backported to 29, but only for 7265D devices. The 96362306a36Sopenharmony_ci * intention was to have it in 36 as well, but not all 8000 96462306a36Sopenharmony_ci * family got this feature enabled. The 8000 family is the 96562306a36Sopenharmony_ci * only one using version 36, so skip this version entirely. 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_ci return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 || 96862306a36Sopenharmony_ci (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 && 96962306a36Sopenharmony_ci fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) || 97062306a36Sopenharmony_ci (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 && 97162306a36Sopenharmony_ci ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == 97262306a36Sopenharmony_ci CSR_HW_REV_TYPE_7265D)); 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_geo_support); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ciint iwl_sar_geo_init(struct iwl_fw_runtime *fwrt, 97762306a36Sopenharmony_ci struct iwl_per_chain_offset *table, 97862306a36Sopenharmony_ci u32 n_bands, u32 n_profiles) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci int i, j; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (!fwrt->geo_enabled) 98362306a36Sopenharmony_ci return -ENODATA; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (!iwl_sar_geo_support(fwrt)) 98662306a36Sopenharmony_ci return -EOPNOTSUPP; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci for (i = 0; i < n_profiles; i++) { 98962306a36Sopenharmony_ci for (j = 0; j < n_bands; j++) { 99062306a36Sopenharmony_ci struct iwl_per_chain_offset *chain = 99162306a36Sopenharmony_ci &table[i * n_bands + j]; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci chain->max_tx_power = 99462306a36Sopenharmony_ci cpu_to_le16(fwrt->geo_profiles[i].bands[j].max); 99562306a36Sopenharmony_ci chain->chain_a = fwrt->geo_profiles[i].bands[j].chains[0]; 99662306a36Sopenharmony_ci chain->chain_b = fwrt->geo_profiles[i].bands[j].chains[1]; 99762306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 99862306a36Sopenharmony_ci "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", 99962306a36Sopenharmony_ci i, j, 100062306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[j].chains[0], 100162306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[j].chains[1], 100262306a36Sopenharmony_ci fwrt->geo_profiles[i].bands[j].max); 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci return 0; 100762306a36Sopenharmony_ci} 100862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_geo_init); 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci__le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci int ret; 101362306a36Sopenharmony_ci u8 value; 101462306a36Sopenharmony_ci __le32 config_bitmap = 0; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci /* 101762306a36Sopenharmony_ci ** Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2' 101862306a36Sopenharmony_ci */ 101962306a36Sopenharmony_ci ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, 102062306a36Sopenharmony_ci DSM_FUNC_ENABLE_INDONESIA_5G2, 102162306a36Sopenharmony_ci &iwl_guid, &value); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (!ret && value == DSM_VALUE_INDONESIA_ENABLE) 102462306a36Sopenharmony_ci config_bitmap |= 102562306a36Sopenharmony_ci cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci /* 102862306a36Sopenharmony_ci ** Evaluate func 'DSM_FUNC_DISABLE_SRD' 102962306a36Sopenharmony_ci */ 103062306a36Sopenharmony_ci ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0, 103162306a36Sopenharmony_ci DSM_FUNC_DISABLE_SRD, 103262306a36Sopenharmony_ci &iwl_guid, &value); 103362306a36Sopenharmony_ci if (!ret) { 103462306a36Sopenharmony_ci if (value == DSM_VALUE_SRD_PASSIVE) 103562306a36Sopenharmony_ci config_bitmap |= 103662306a36Sopenharmony_ci cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK); 103762306a36Sopenharmony_ci else if (value == DSM_VALUE_SRD_DISABLE) 103862306a36Sopenharmony_ci config_bitmap |= 103962306a36Sopenharmony_ci cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK); 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return config_bitmap; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ciint iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci union acpi_object *wifi_pkg, *data, *flags; 104962306a36Sopenharmony_ci int i, j, ret, tbl_rev, num_sub_bands = 0; 105062306a36Sopenharmony_ci int idx = 2; 105162306a36Sopenharmony_ci u8 cmd_ver; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci fwrt->ppag_flags = 0; 105462306a36Sopenharmony_ci fwrt->ppag_table_valid = false; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD); 105762306a36Sopenharmony_ci if (IS_ERR(data)) 105862306a36Sopenharmony_ci return PTR_ERR(data); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* try to read ppag table rev 2 or 1 (both have the same data size) */ 106162306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 106262306a36Sopenharmony_ci ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 106562306a36Sopenharmony_ci if (tbl_rev == 1 || tbl_rev == 2) { 106662306a36Sopenharmony_ci num_sub_bands = IWL_NUM_SUB_BANDS_V2; 106762306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 106862306a36Sopenharmony_ci "Reading PPAG table v2 (tbl_rev=%d)\n", 106962306a36Sopenharmony_ci tbl_rev); 107062306a36Sopenharmony_ci goto read_table; 107162306a36Sopenharmony_ci } else { 107262306a36Sopenharmony_ci ret = -EINVAL; 107362306a36Sopenharmony_ci goto out_free; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* try to read ppag table revision 0 */ 107862306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 107962306a36Sopenharmony_ci ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (!IS_ERR(wifi_pkg)) { 108262306a36Sopenharmony_ci if (tbl_rev != 0) { 108362306a36Sopenharmony_ci ret = -EINVAL; 108462306a36Sopenharmony_ci goto out_free; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci num_sub_bands = IWL_NUM_SUB_BANDS_V1; 108762306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n"); 108862306a36Sopenharmony_ci goto read_table; 108962306a36Sopenharmony_ci } 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci ret = PTR_ERR(wifi_pkg); 109262306a36Sopenharmony_ci goto out_free; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ciread_table: 109562306a36Sopenharmony_ci fwrt->ppag_ver = tbl_rev; 109662306a36Sopenharmony_ci flags = &wifi_pkg->package.elements[1]; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (flags->type != ACPI_TYPE_INTEGER) { 109962306a36Sopenharmony_ci ret = -EINVAL; 110062306a36Sopenharmony_ci goto out_free; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK; 110462306a36Sopenharmony_ci cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, 110562306a36Sopenharmony_ci WIDE_ID(PHY_OPS_GROUP, 110662306a36Sopenharmony_ci PER_PLATFORM_ANT_GAIN_CMD), 110762306a36Sopenharmony_ci IWL_FW_CMD_VER_UNKNOWN); 110862306a36Sopenharmony_ci if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) { 110962306a36Sopenharmony_ci ret = -EINVAL; 111062306a36Sopenharmony_ci goto out_free; 111162306a36Sopenharmony_ci } 111262306a36Sopenharmony_ci if (!fwrt->ppag_flags && cmd_ver <= 3) { 111362306a36Sopenharmony_ci ret = 0; 111462306a36Sopenharmony_ci goto out_free; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci /* 111862306a36Sopenharmony_ci * read, verify gain values and save them into the PPAG table. 111962306a36Sopenharmony_ci * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the 112062306a36Sopenharmony_ci * following sub-bands to High-Band (5GHz). 112162306a36Sopenharmony_ci */ 112262306a36Sopenharmony_ci for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { 112362306a36Sopenharmony_ci for (j = 0; j < num_sub_bands; j++) { 112462306a36Sopenharmony_ci union acpi_object *ent; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci ent = &wifi_pkg->package.elements[idx++]; 112762306a36Sopenharmony_ci if (ent->type != ACPI_TYPE_INTEGER) { 112862306a36Sopenharmony_ci ret = -EINVAL; 112962306a36Sopenharmony_ci goto out_free; 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci fwrt->ppag_chains[i].subbands[j] = ent->integer.value; 113362306a36Sopenharmony_ci /* from ver 4 the fw deals with out of range values */ 113462306a36Sopenharmony_ci if (cmd_ver >= 4) 113562306a36Sopenharmony_ci continue; 113662306a36Sopenharmony_ci if ((j == 0 && 113762306a36Sopenharmony_ci (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB || 113862306a36Sopenharmony_ci fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) || 113962306a36Sopenharmony_ci (j != 0 && 114062306a36Sopenharmony_ci (fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB || 114162306a36Sopenharmony_ci fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) { 114262306a36Sopenharmony_ci ret = -EINVAL; 114362306a36Sopenharmony_ci goto out_free; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci } 114662306a36Sopenharmony_ci } 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci fwrt->ppag_table_valid = true; 114962306a36Sopenharmony_ci ret = 0; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ciout_free: 115262306a36Sopenharmony_ci kfree(data); 115362306a36Sopenharmony_ci return ret; 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ciint iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd, 115862306a36Sopenharmony_ci int *cmd_size) 115962306a36Sopenharmony_ci{ 116062306a36Sopenharmony_ci u8 cmd_ver; 116162306a36Sopenharmony_ci int i, j, num_sub_bands; 116262306a36Sopenharmony_ci s8 *gain; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci /* many firmware images for JF lie about this */ 116562306a36Sopenharmony_ci if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) == 116662306a36Sopenharmony_ci CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF)) 116762306a36Sopenharmony_ci return -EOPNOTSUPP; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) { 117062306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 117162306a36Sopenharmony_ci "PPAG capability not supported by FW, command not sent.\n"); 117262306a36Sopenharmony_ci return -EINVAL; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw, 117662306a36Sopenharmony_ci WIDE_ID(PHY_OPS_GROUP, 117762306a36Sopenharmony_ci PER_PLATFORM_ANT_GAIN_CMD), 117862306a36Sopenharmony_ci IWL_FW_CMD_VER_UNKNOWN); 117962306a36Sopenharmony_ci if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) { 118062306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n"); 118162306a36Sopenharmony_ci return -EINVAL; 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* The 'flags' field is the same in v1 and in v2 so we can just 118562306a36Sopenharmony_ci * use v1 to access it. 118662306a36Sopenharmony_ci */ 118762306a36Sopenharmony_ci cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver); 119062306a36Sopenharmony_ci if (cmd_ver == 1) { 119162306a36Sopenharmony_ci num_sub_bands = IWL_NUM_SUB_BANDS_V1; 119262306a36Sopenharmony_ci gain = cmd->v1.gain[0]; 119362306a36Sopenharmony_ci *cmd_size = sizeof(cmd->v1); 119462306a36Sopenharmony_ci if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) { 119562306a36Sopenharmony_ci /* in this case FW supports revision 0 */ 119662306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 119762306a36Sopenharmony_ci "PPAG table rev is %d, send truncated table\n", 119862306a36Sopenharmony_ci fwrt->ppag_ver); 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci } else if (cmd_ver >= 2 && cmd_ver <= 4) { 120162306a36Sopenharmony_ci num_sub_bands = IWL_NUM_SUB_BANDS_V2; 120262306a36Sopenharmony_ci gain = cmd->v2.gain[0]; 120362306a36Sopenharmony_ci *cmd_size = sizeof(cmd->v2); 120462306a36Sopenharmony_ci if (fwrt->ppag_ver == 0) { 120562306a36Sopenharmony_ci /* in this case FW supports revisions 1 or 2 */ 120662306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 120762306a36Sopenharmony_ci "PPAG table rev is 0, send padded table\n"); 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci } else { 121062306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n"); 121162306a36Sopenharmony_ci return -EINVAL; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* ppag mode */ 121562306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 121662306a36Sopenharmony_ci "PPAG MODE bits were read from bios: %d\n", 121762306a36Sopenharmony_ci cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK)); 121862306a36Sopenharmony_ci if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa, 121962306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || 122062306a36Sopenharmony_ci (cmd_ver == 2 && fwrt->ppag_ver == 2)) { 122162306a36Sopenharmony_ci cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); 122262306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n"); 122362306a36Sopenharmony_ci } else { 122462306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n"); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 122862306a36Sopenharmony_ci "PPAG MODE bits going to be sent: %d\n", 122962306a36Sopenharmony_ci cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK)); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { 123262306a36Sopenharmony_ci for (j = 0; j < num_sub_bands; j++) { 123362306a36Sopenharmony_ci gain[i * num_sub_bands + j] = 123462306a36Sopenharmony_ci fwrt->ppag_chains[i].subbands[j]; 123562306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 123662306a36Sopenharmony_ci "PPAG table: chain[%d] band[%d]: gain = %d\n", 123762306a36Sopenharmony_ci i, j, gain[i * num_sub_bands + j]); 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return 0; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read_ppag_table); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cibool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci if (!dmi_check_system(dmi_ppag_approved_list)) { 124962306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, 125062306a36Sopenharmony_ci "System vendor '%s' is not in the approved list, disabling PPAG.\n", 125162306a36Sopenharmony_ci dmi_get_system_info(DMI_SYS_VENDOR)); 125262306a36Sopenharmony_ci fwrt->ppag_flags = 0; 125362306a36Sopenharmony_ci return false; 125462306a36Sopenharmony_ci } 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci return true; 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_is_ppag_approved); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_civoid iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, 126162306a36Sopenharmony_ci struct iwl_phy_specific_cfg *filters) 126262306a36Sopenharmony_ci{ 126362306a36Sopenharmony_ci struct iwl_phy_specific_cfg tmp = {}; 126462306a36Sopenharmony_ci union acpi_object *wifi_pkg, *data; 126562306a36Sopenharmony_ci int tbl_rev, i; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD); 126862306a36Sopenharmony_ci if (IS_ERR(data)) 126962306a36Sopenharmony_ci return; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 127262306a36Sopenharmony_ci ACPI_WPFC_WIFI_DATA_SIZE, 127362306a36Sopenharmony_ci &tbl_rev); 127462306a36Sopenharmony_ci if (IS_ERR(wifi_pkg)) 127562306a36Sopenharmony_ci goto out_free; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci if (tbl_rev != 0) 127862306a36Sopenharmony_ci goto out_free; 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) != 128162306a36Sopenharmony_ci ACPI_WPFC_WIFI_DATA_SIZE - 1); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) { 128462306a36Sopenharmony_ci if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER) 128562306a36Sopenharmony_ci goto out_free; 128662306a36Sopenharmony_ci tmp.filter_cfg_chains[i] = 128762306a36Sopenharmony_ci cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value); 128862306a36Sopenharmony_ci } 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n"); 129162306a36Sopenharmony_ci *filters = tmp; 129262306a36Sopenharmony_ciout_free: 129362306a36Sopenharmony_ci kfree(data); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters); 1296