1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2bfcc09ddSBjoern A. Zeeb /* 3*a4128aadSBjoern A. Zeeb * Copyright(c) 2021-2024 Intel Corporation 4bfcc09ddSBjoern A. Zeeb */ 5bfcc09ddSBjoern A. Zeeb 6bfcc09ddSBjoern A. Zeeb #include "iwl-drv.h" 7bfcc09ddSBjoern A. Zeeb #include "pnvm.h" 8bfcc09ddSBjoern A. Zeeb #include "iwl-prph.h" 9bfcc09ddSBjoern A. Zeeb #include "iwl-io.h" 10bfcc09ddSBjoern A. Zeeb 11bfcc09ddSBjoern A. Zeeb #include "fw/uefi.h" 12bfcc09ddSBjoern A. Zeeb #include "fw/api/alive.h" 13bfcc09ddSBjoern A. Zeeb #include <linux/efi.h> 14d9836fb4SBjoern A. Zeeb #include "fw/runtime.h" 15bfcc09ddSBjoern A. Zeeb 16bfcc09ddSBjoern A. Zeeb #define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b, \ 17bfcc09ddSBjoern A. Zeeb 0xb2, 0xec, 0xf5, 0xa3, \ 18bfcc09ddSBjoern A. Zeeb 0x59, 0x4f, 0x4a, 0xea) 19bfcc09ddSBjoern A. Zeeb 209af1bba4SBjoern A. Zeeb struct iwl_uefi_pnvm_mem_desc { 219af1bba4SBjoern A. Zeeb __le32 addr; 229af1bba4SBjoern A. Zeeb __le32 size; 239af1bba4SBjoern A. Zeeb const u8 data[]; 249af1bba4SBjoern A. Zeeb } __packed; 259af1bba4SBjoern A. Zeeb 269af1bba4SBjoern A. Zeeb static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid, 279af1bba4SBjoern A. Zeeb unsigned long *data_size) 289af1bba4SBjoern A. Zeeb { 299af1bba4SBjoern A. Zeeb efi_status_t status; 309af1bba4SBjoern A. Zeeb void *data; 319af1bba4SBjoern A. Zeeb 329af1bba4SBjoern A. Zeeb if (!data_size) 339af1bba4SBjoern A. Zeeb return ERR_PTR(-EINVAL); 349af1bba4SBjoern A. Zeeb 359af1bba4SBjoern A. Zeeb if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE)) 369af1bba4SBjoern A. Zeeb return ERR_PTR(-ENODEV); 379af1bba4SBjoern A. Zeeb 389af1bba4SBjoern A. Zeeb /* first call with NULL data to get the exact entry size */ 399af1bba4SBjoern A. Zeeb *data_size = 0; 409af1bba4SBjoern A. Zeeb status = efi.get_variable(name, guid, NULL, data_size, NULL); 419af1bba4SBjoern A. Zeeb if (status != EFI_BUFFER_TOO_SMALL || !*data_size) 429af1bba4SBjoern A. Zeeb return ERR_PTR(-EIO); 439af1bba4SBjoern A. Zeeb 449af1bba4SBjoern A. Zeeb data = kmalloc(*data_size, GFP_KERNEL); 459af1bba4SBjoern A. Zeeb if (!data) 469af1bba4SBjoern A. Zeeb return ERR_PTR(-ENOMEM); 479af1bba4SBjoern A. Zeeb 489af1bba4SBjoern A. Zeeb status = efi.get_variable(name, guid, NULL, data_size, data); 499af1bba4SBjoern A. Zeeb if (status != EFI_SUCCESS) { 509af1bba4SBjoern A. Zeeb kfree(data); 519af1bba4SBjoern A. Zeeb return ERR_PTR(-ENOENT); 529af1bba4SBjoern A. Zeeb } 539af1bba4SBjoern A. Zeeb 549af1bba4SBjoern A. Zeeb return data; 559af1bba4SBjoern A. Zeeb } 569af1bba4SBjoern A. Zeeb 57bfcc09ddSBjoern A. Zeeb void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) 58bfcc09ddSBjoern A. Zeeb { 59bfcc09ddSBjoern A. Zeeb unsigned long package_size; 609af1bba4SBjoern A. Zeeb void *data; 61bfcc09ddSBjoern A. Zeeb 62bfcc09ddSBjoern A. Zeeb *len = 0; 63bfcc09ddSBjoern A. Zeeb 649af1bba4SBjoern A. Zeeb data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID, 659af1bba4SBjoern A. Zeeb &package_size); 669af1bba4SBjoern A. Zeeb if (IS_ERR(data)) { 67bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 689af1bba4SBjoern A. Zeeb "PNVM UEFI variable not found 0x%lx (len %lu)\n", 699af1bba4SBjoern A. Zeeb PTR_ERR(data), package_size); 709af1bba4SBjoern A. Zeeb return data; 71bfcc09ddSBjoern A. Zeeb } 72bfcc09ddSBjoern A. Zeeb 73bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size); 74bfcc09ddSBjoern A. Zeeb *len = package_size; 75bfcc09ddSBjoern A. Zeeb 76bfcc09ddSBjoern A. Zeeb return data; 77bfcc09ddSBjoern A. Zeeb } 78bfcc09ddSBjoern A. Zeeb 79*a4128aadSBjoern A. Zeeb static 80*a4128aadSBjoern A. Zeeb void *iwl_uefi_get_verified_variable(struct iwl_trans *trans, 81*a4128aadSBjoern A. Zeeb efi_char16_t *uefi_var_name, 82*a4128aadSBjoern A. Zeeb char *var_name, 83*a4128aadSBjoern A. Zeeb unsigned int expected_size, 84*a4128aadSBjoern A. Zeeb unsigned long *size) 85*a4128aadSBjoern A. Zeeb { 86*a4128aadSBjoern A. Zeeb void *var; 87*a4128aadSBjoern A. Zeeb unsigned long var_size; 88*a4128aadSBjoern A. Zeeb 89*a4128aadSBjoern A. Zeeb var = iwl_uefi_get_variable(uefi_var_name, &IWL_EFI_VAR_GUID, 90*a4128aadSBjoern A. Zeeb &var_size); 91*a4128aadSBjoern A. Zeeb 92*a4128aadSBjoern A. Zeeb if (IS_ERR(var)) { 93*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(trans, 94*a4128aadSBjoern A. Zeeb "%s UEFI variable not found 0x%lx\n", var_name, 95*a4128aadSBjoern A. Zeeb PTR_ERR(var)); 96*a4128aadSBjoern A. Zeeb return var; 97*a4128aadSBjoern A. Zeeb } 98*a4128aadSBjoern A. Zeeb 99*a4128aadSBjoern A. Zeeb if (var_size < expected_size) { 100*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(trans, 101*a4128aadSBjoern A. Zeeb "Invalid %s UEFI variable len (%lu)\n", 102*a4128aadSBjoern A. Zeeb var_name, var_size); 103*a4128aadSBjoern A. Zeeb kfree(var); 104*a4128aadSBjoern A. Zeeb return ERR_PTR(-EINVAL); 105*a4128aadSBjoern A. Zeeb } 106*a4128aadSBjoern A. Zeeb 107*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(trans, "%s from UEFI with size %lu\n", var_name, 108*a4128aadSBjoern A. Zeeb var_size); 109*a4128aadSBjoern A. Zeeb 110*a4128aadSBjoern A. Zeeb if (size) 111*a4128aadSBjoern A. Zeeb *size = var_size; 112*a4128aadSBjoern A. Zeeb return var; 113*a4128aadSBjoern A. Zeeb } 114*a4128aadSBjoern A. Zeeb 1159af1bba4SBjoern A. Zeeb int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, 1169af1bba4SBjoern A. Zeeb u32 tlv_len, struct iwl_pnvm_image *pnvm_data) 1179af1bba4SBjoern A. Zeeb { 1189af1bba4SBjoern A. Zeeb const struct iwl_uefi_pnvm_mem_desc *desc = (const void *)data; 1199af1bba4SBjoern A. Zeeb u32 data_len; 1209af1bba4SBjoern A. Zeeb 1219af1bba4SBjoern A. Zeeb if (tlv_len < sizeof(*desc)) { 1229af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, "TLV len (%d) is too small\n", tlv_len); 1239af1bba4SBjoern A. Zeeb return -EINVAL; 1249af1bba4SBjoern A. Zeeb } 1259af1bba4SBjoern A. Zeeb 1269af1bba4SBjoern A. Zeeb data_len = tlv_len - sizeof(*desc); 1279af1bba4SBjoern A. Zeeb 1289af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, 1299af1bba4SBjoern A. Zeeb "Handle IWL_UCODE_TLV_MEM_DESC, len %d data_len %d\n", 1309af1bba4SBjoern A. Zeeb tlv_len, data_len); 1319af1bba4SBjoern A. Zeeb 1329af1bba4SBjoern A. Zeeb if (le32_to_cpu(desc->size) != data_len) { 1339af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, "invalid mem desc size %d\n", desc->size); 1349af1bba4SBjoern A. Zeeb return -EINVAL; 1359af1bba4SBjoern A. Zeeb } 1369af1bba4SBjoern A. Zeeb 1379af1bba4SBjoern A. Zeeb if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) { 1389af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, "too many payloads to allocate in DRAM.\n"); 1399af1bba4SBjoern A. Zeeb return -EINVAL; 1409af1bba4SBjoern A. Zeeb } 1419af1bba4SBjoern A. Zeeb 1429af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, "Adding data (size %d)\n", data_len); 1439af1bba4SBjoern A. Zeeb 1449af1bba4SBjoern A. Zeeb pnvm_data->chunks[pnvm_data->n_chunks].data = desc->data; 1459af1bba4SBjoern A. Zeeb pnvm_data->chunks[pnvm_data->n_chunks].len = data_len; 1469af1bba4SBjoern A. Zeeb pnvm_data->n_chunks++; 1479af1bba4SBjoern A. Zeeb 1489af1bba4SBjoern A. Zeeb return 0; 1499af1bba4SBjoern A. Zeeb } 1509af1bba4SBjoern A. Zeeb 1519af1bba4SBjoern A. Zeeb static int iwl_uefi_reduce_power_section(struct iwl_trans *trans, 1529af1bba4SBjoern A. Zeeb const u8 *data, size_t len, 1539af1bba4SBjoern A. Zeeb struct iwl_pnvm_image *pnvm_data) 154bfcc09ddSBjoern A. Zeeb { 155d9836fb4SBjoern A. Zeeb const struct iwl_ucode_tlv *tlv; 156bfcc09ddSBjoern A. Zeeb 157bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n"); 1589af1bba4SBjoern A. Zeeb memset(pnvm_data, 0, sizeof(*pnvm_data)); 159bfcc09ddSBjoern A. Zeeb 160bfcc09ddSBjoern A. Zeeb while (len >= sizeof(*tlv)) { 161bfcc09ddSBjoern A. Zeeb u32 tlv_len, tlv_type; 162bfcc09ddSBjoern A. Zeeb 163bfcc09ddSBjoern A. Zeeb len -= sizeof(*tlv); 164d9836fb4SBjoern A. Zeeb tlv = (const void *)data; 165bfcc09ddSBjoern A. Zeeb 166bfcc09ddSBjoern A. Zeeb tlv_len = le32_to_cpu(tlv->length); 167bfcc09ddSBjoern A. Zeeb tlv_type = le32_to_cpu(tlv->type); 168bfcc09ddSBjoern A. Zeeb 169bfcc09ddSBjoern A. Zeeb if (len < tlv_len) { 170bfcc09ddSBjoern A. Zeeb IWL_ERR(trans, "invalid TLV len: %zd/%u\n", 171bfcc09ddSBjoern A. Zeeb len, tlv_len); 1729af1bba4SBjoern A. Zeeb return -EINVAL; 173bfcc09ddSBjoern A. Zeeb } 174bfcc09ddSBjoern A. Zeeb 175bfcc09ddSBjoern A. Zeeb data += sizeof(*tlv); 176bfcc09ddSBjoern A. Zeeb 177bfcc09ddSBjoern A. Zeeb switch (tlv_type) { 1789af1bba4SBjoern A. Zeeb case IWL_UCODE_TLV_MEM_DESC: 1799af1bba4SBjoern A. Zeeb if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len, 1809af1bba4SBjoern A. Zeeb pnvm_data)) 1819af1bba4SBjoern A. Zeeb return -EINVAL; 182bfcc09ddSBjoern A. Zeeb break; 183bfcc09ddSBjoern A. Zeeb case IWL_UCODE_TLV_PNVM_SKU: 184bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 185bfcc09ddSBjoern A. Zeeb "New REDUCE_POWER section started, stop parsing.\n"); 186bfcc09ddSBjoern A. Zeeb goto done; 187bfcc09ddSBjoern A. Zeeb default: 188bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n", 189bfcc09ddSBjoern A. Zeeb tlv_type, tlv_len); 190bfcc09ddSBjoern A. Zeeb break; 191bfcc09ddSBjoern A. Zeeb } 192bfcc09ddSBjoern A. Zeeb 193bfcc09ddSBjoern A. Zeeb len -= ALIGN(tlv_len, 4); 194bfcc09ddSBjoern A. Zeeb data += ALIGN(tlv_len, 4); 195bfcc09ddSBjoern A. Zeeb } 196bfcc09ddSBjoern A. Zeeb 197bfcc09ddSBjoern A. Zeeb done: 1989af1bba4SBjoern A. Zeeb if (!pnvm_data->n_chunks) { 199bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n"); 2009af1bba4SBjoern A. Zeeb return -ENOENT; 2019af1bba4SBjoern A. Zeeb } 2029af1bba4SBjoern A. Zeeb return 0; 203bfcc09ddSBjoern A. Zeeb } 204bfcc09ddSBjoern A. Zeeb 2059af1bba4SBjoern A. Zeeb int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, 2069af1bba4SBjoern A. Zeeb const u8 *data, size_t len, 2079af1bba4SBjoern A. Zeeb struct iwl_pnvm_image *pnvm_data) 208bfcc09ddSBjoern A. Zeeb { 209d9836fb4SBjoern A. Zeeb const struct iwl_ucode_tlv *tlv; 210bfcc09ddSBjoern A. Zeeb 211bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n"); 212bfcc09ddSBjoern A. Zeeb 213bfcc09ddSBjoern A. Zeeb while (len >= sizeof(*tlv)) { 214bfcc09ddSBjoern A. Zeeb u32 tlv_len, tlv_type; 215bfcc09ddSBjoern A. Zeeb 216bfcc09ddSBjoern A. Zeeb len -= sizeof(*tlv); 217d9836fb4SBjoern A. Zeeb tlv = (const void *)data; 218bfcc09ddSBjoern A. Zeeb 219bfcc09ddSBjoern A. Zeeb tlv_len = le32_to_cpu(tlv->length); 220bfcc09ddSBjoern A. Zeeb tlv_type = le32_to_cpu(tlv->type); 221bfcc09ddSBjoern A. Zeeb 222bfcc09ddSBjoern A. Zeeb if (len < tlv_len) { 223bfcc09ddSBjoern A. Zeeb IWL_ERR(trans, "invalid TLV len: %zd/%u\n", 224bfcc09ddSBjoern A. Zeeb len, tlv_len); 2259af1bba4SBjoern A. Zeeb return -EINVAL; 226bfcc09ddSBjoern A. Zeeb } 227bfcc09ddSBjoern A. Zeeb 228bfcc09ddSBjoern A. Zeeb if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) { 229d9836fb4SBjoern A. Zeeb const struct iwl_sku_id *sku_id = 230d9836fb4SBjoern A. Zeeb (const void *)(data + sizeof(*tlv)); 231bfcc09ddSBjoern A. Zeeb 232bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 233bfcc09ddSBjoern A. Zeeb "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", 234bfcc09ddSBjoern A. Zeeb tlv_len); 235bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n", 236bfcc09ddSBjoern A. Zeeb le32_to_cpu(sku_id->data[0]), 237bfcc09ddSBjoern A. Zeeb le32_to_cpu(sku_id->data[1]), 238bfcc09ddSBjoern A. Zeeb le32_to_cpu(sku_id->data[2])); 239bfcc09ddSBjoern A. Zeeb 240bfcc09ddSBjoern A. Zeeb data += sizeof(*tlv) + ALIGN(tlv_len, 4); 241bfcc09ddSBjoern A. Zeeb len -= ALIGN(tlv_len, 4); 242bfcc09ddSBjoern A. Zeeb 243bfcc09ddSBjoern A. Zeeb if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) && 244bfcc09ddSBjoern A. Zeeb trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) && 245bfcc09ddSBjoern A. Zeeb trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) { 2469af1bba4SBjoern A. Zeeb int ret = iwl_uefi_reduce_power_section(trans, 2479af1bba4SBjoern A. Zeeb data, len, 2489af1bba4SBjoern A. Zeeb pnvm_data); 2499af1bba4SBjoern A. Zeeb if (!ret) 2509af1bba4SBjoern A. Zeeb return 0; 251bfcc09ddSBjoern A. Zeeb } else { 252bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "SKU ID didn't match!\n"); 253bfcc09ddSBjoern A. Zeeb } 254bfcc09ddSBjoern A. Zeeb } else { 255bfcc09ddSBjoern A. Zeeb data += sizeof(*tlv) + ALIGN(tlv_len, 4); 256bfcc09ddSBjoern A. Zeeb len -= ALIGN(tlv_len, 4); 257bfcc09ddSBjoern A. Zeeb } 258bfcc09ddSBjoern A. Zeeb } 259bfcc09ddSBjoern A. Zeeb 2609af1bba4SBjoern A. Zeeb return -ENOENT; 261bfcc09ddSBjoern A. Zeeb } 262bfcc09ddSBjoern A. Zeeb 2639af1bba4SBjoern A. Zeeb u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len) 264bfcc09ddSBjoern A. Zeeb { 265bfcc09ddSBjoern A. Zeeb struct pnvm_sku_package *package; 266bfcc09ddSBjoern A. Zeeb unsigned long package_size; 2679af1bba4SBjoern A. Zeeb u8 *data; 268bfcc09ddSBjoern A. Zeeb 269*a4128aadSBjoern A. Zeeb package = iwl_uefi_get_verified_variable(trans, 270*a4128aadSBjoern A. Zeeb IWL_UEFI_REDUCED_POWER_NAME, 271*a4128aadSBjoern A. Zeeb "Reduced Power", 272*a4128aadSBjoern A. Zeeb sizeof(*package), 273*a4128aadSBjoern A. Zeeb &package_size); 274*a4128aadSBjoern A. Zeeb if (IS_ERR(package)) 2759af1bba4SBjoern A. Zeeb return ERR_CAST(package); 276bfcc09ddSBjoern A. Zeeb 277bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n", 278bfcc09ddSBjoern A. Zeeb package->rev, package->total_size, package->n_skus); 279bfcc09ddSBjoern A. Zeeb 2809af1bba4SBjoern A. Zeeb *len = package_size - sizeof(*package); 2819af1bba4SBjoern A. Zeeb data = kmemdup(package->data, *len, GFP_KERNEL); 2829af1bba4SBjoern A. Zeeb if (!data) { 2839af1bba4SBjoern A. Zeeb kfree(package); 2849af1bba4SBjoern A. Zeeb return ERR_PTR(-ENOMEM); 2859af1bba4SBjoern A. Zeeb } 286bfcc09ddSBjoern A. Zeeb 287bfcc09ddSBjoern A. Zeeb kfree(package); 288bfcc09ddSBjoern A. Zeeb 289bfcc09ddSBjoern A. Zeeb return data; 290bfcc09ddSBjoern A. Zeeb } 291d9836fb4SBjoern A. Zeeb 2929af1bba4SBjoern A. Zeeb static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data, 2939af1bba4SBjoern A. Zeeb struct iwl_trans *trans) 2949af1bba4SBjoern A. Zeeb { 2959af1bba4SBjoern A. Zeeb if (common_step_data->revision != 1) 2969af1bba4SBjoern A. Zeeb return -EINVAL; 2979af1bba4SBjoern A. Zeeb 2989af1bba4SBjoern A. Zeeb trans->mbx_addr_0_step = (u32)common_step_data->revision | 2999af1bba4SBjoern A. Zeeb (u32)common_step_data->cnvi_eq_channel << 8 | 3009af1bba4SBjoern A. Zeeb (u32)common_step_data->cnvr_eq_channel << 16 | 3019af1bba4SBjoern A. Zeeb (u32)common_step_data->radio1 << 24; 3029af1bba4SBjoern A. Zeeb trans->mbx_addr_1_step = (u32)common_step_data->radio2; 3039af1bba4SBjoern A. Zeeb return 0; 3049af1bba4SBjoern A. Zeeb } 3059af1bba4SBjoern A. Zeeb 3069af1bba4SBjoern A. Zeeb void iwl_uefi_get_step_table(struct iwl_trans *trans) 3079af1bba4SBjoern A. Zeeb { 3089af1bba4SBjoern A. Zeeb struct uefi_cnv_common_step_data *data; 3099af1bba4SBjoern A. Zeeb int ret; 3109af1bba4SBjoern A. Zeeb 3119af1bba4SBjoern A. Zeeb if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) 3129af1bba4SBjoern A. Zeeb return; 3139af1bba4SBjoern A. Zeeb 314*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_STEP_NAME, 315*a4128aadSBjoern A. Zeeb "STEP", sizeof(*data), NULL); 316*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 3179af1bba4SBjoern A. Zeeb return; 3189af1bba4SBjoern A. Zeeb 3199af1bba4SBjoern A. Zeeb ret = iwl_uefi_step_parse(data, trans); 3209af1bba4SBjoern A. Zeeb if (ret < 0) 3219af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, "Cannot read STEP tables. rev is invalid\n"); 3229af1bba4SBjoern A. Zeeb 3239af1bba4SBjoern A. Zeeb kfree(data); 3249af1bba4SBjoern A. Zeeb } 3259af1bba4SBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table); 3269af1bba4SBjoern A. Zeeb 327d9836fb4SBjoern A. Zeeb static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data, 328d9836fb4SBjoern A. Zeeb struct iwl_fw_runtime *fwrt) 329d9836fb4SBjoern A. Zeeb { 330d9836fb4SBjoern A. Zeeb int i, j; 331d9836fb4SBjoern A. Zeeb 332d9836fb4SBjoern A. Zeeb if (sgom_data->revision != 1) 333d9836fb4SBjoern A. Zeeb return -EINVAL; 334d9836fb4SBjoern A. Zeeb 335d9836fb4SBjoern A. Zeeb memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map, 336d9836fb4SBjoern A. Zeeb sizeof(fwrt->sgom_table.offset_map)); 337d9836fb4SBjoern A. Zeeb 338d9836fb4SBjoern A. Zeeb for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) { 339d9836fb4SBjoern A. Zeeb for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) { 340d9836fb4SBjoern A. Zeeb /* since each byte is composed of to values, */ 341d9836fb4SBjoern A. Zeeb /* one for each letter, */ 342d9836fb4SBjoern A. Zeeb /* extract and check each of them separately */ 343d9836fb4SBjoern A. Zeeb u8 value = fwrt->sgom_table.offset_map[i][j]; 344d9836fb4SBjoern A. Zeeb u8 low = value & 0xF; 345d9836fb4SBjoern A. Zeeb u8 high = (value & 0xF0) >> 4; 346d9836fb4SBjoern A. Zeeb 347d9836fb4SBjoern A. Zeeb if (high > fwrt->geo_num_profiles) 348d9836fb4SBjoern A. Zeeb high = 0; 349d9836fb4SBjoern A. Zeeb if (low > fwrt->geo_num_profiles) 350d9836fb4SBjoern A. Zeeb low = 0; 351d9836fb4SBjoern A. Zeeb fwrt->sgom_table.offset_map[i][j] = (high << 4) | low; 352d9836fb4SBjoern A. Zeeb } 353d9836fb4SBjoern A. Zeeb } 354d9836fb4SBjoern A. Zeeb 355d9836fb4SBjoern A. Zeeb fwrt->sgom_enabled = true; 356d9836fb4SBjoern A. Zeeb return 0; 357d9836fb4SBjoern A. Zeeb } 358d9836fb4SBjoern A. Zeeb 359d9836fb4SBjoern A. Zeeb void iwl_uefi_get_sgom_table(struct iwl_trans *trans, 360d9836fb4SBjoern A. Zeeb struct iwl_fw_runtime *fwrt) 361d9836fb4SBjoern A. Zeeb { 362d9836fb4SBjoern A. Zeeb struct uefi_cnv_wlan_sgom_data *data; 3639af1bba4SBjoern A. Zeeb int ret; 364d9836fb4SBjoern A. Zeeb 365d9836fb4SBjoern A. Zeeb if (!fwrt->geo_enabled) 366d9836fb4SBjoern A. Zeeb return; 367d9836fb4SBjoern A. Zeeb 368*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME, 369*a4128aadSBjoern A. Zeeb "SGOM", sizeof(*data), NULL); 370*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 371d9836fb4SBjoern A. Zeeb return; 372d9836fb4SBjoern A. Zeeb 373d9836fb4SBjoern A. Zeeb ret = iwl_uefi_sgom_parse(data, fwrt); 374d9836fb4SBjoern A. Zeeb if (ret < 0) 375d9836fb4SBjoern A. Zeeb IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n"); 376d9836fb4SBjoern A. Zeeb 377d9836fb4SBjoern A. Zeeb kfree(data); 378d9836fb4SBjoern A. Zeeb } 379d9836fb4SBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table); 380*a4128aadSBjoern A. Zeeb 381*a4128aadSBjoern A. Zeeb static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data, 382*a4128aadSBjoern A. Zeeb struct iwl_fw_runtime *fwrt) 383*a4128aadSBjoern A. Zeeb { 384*a4128aadSBjoern A. Zeeb if (uats_data->revision != 1) 385*a4128aadSBjoern A. Zeeb return -EINVAL; 386*a4128aadSBjoern A. Zeeb 387*a4128aadSBjoern A. Zeeb memcpy(fwrt->uats_table.offset_map, uats_data->offset_map, 388*a4128aadSBjoern A. Zeeb sizeof(fwrt->uats_table.offset_map)); 389*a4128aadSBjoern A. Zeeb return 0; 390*a4128aadSBjoern A. Zeeb } 391*a4128aadSBjoern A. Zeeb 392*a4128aadSBjoern A. Zeeb int iwl_uefi_get_uats_table(struct iwl_trans *trans, 393*a4128aadSBjoern A. Zeeb struct iwl_fw_runtime *fwrt) 394*a4128aadSBjoern A. Zeeb { 395*a4128aadSBjoern A. Zeeb struct uefi_cnv_wlan_uats_data *data; 396*a4128aadSBjoern A. Zeeb int ret; 397*a4128aadSBjoern A. Zeeb 398*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME, 399*a4128aadSBjoern A. Zeeb "UATS", sizeof(*data), NULL); 400*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 401*a4128aadSBjoern A. Zeeb return -EINVAL; 402*a4128aadSBjoern A. Zeeb 403*a4128aadSBjoern A. Zeeb ret = iwl_uefi_uats_parse(data, fwrt); 404*a4128aadSBjoern A. Zeeb if (ret < 0) { 405*a4128aadSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n"); 406*a4128aadSBjoern A. Zeeb kfree(data); 407*a4128aadSBjoern A. Zeeb return ret; 408*a4128aadSBjoern A. Zeeb } 409*a4128aadSBjoern A. Zeeb 410*a4128aadSBjoern A. Zeeb kfree(data); 411*a4128aadSBjoern A. Zeeb return 0; 412*a4128aadSBjoern A. Zeeb } 413*a4128aadSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table); 414*a4128aadSBjoern A. Zeeb 415*a4128aadSBjoern A. Zeeb static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt, 416*a4128aadSBjoern A. Zeeb struct uefi_sar_profile *uefi_sar_prof, 417*a4128aadSBjoern A. Zeeb u8 prof_index, bool enabled) 418*a4128aadSBjoern A. Zeeb { 419*a4128aadSBjoern A. Zeeb memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof, 420*a4128aadSBjoern A. Zeeb sizeof(struct uefi_sar_profile)); 421*a4128aadSBjoern A. Zeeb 422*a4128aadSBjoern A. Zeeb fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK; 423*a4128aadSBjoern A. Zeeb } 424*a4128aadSBjoern A. Zeeb 425*a4128aadSBjoern A. Zeeb int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt) 426*a4128aadSBjoern A. Zeeb { 427*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_wrds *data; 428*a4128aadSBjoern A. Zeeb int ret = 0; 429*a4128aadSBjoern A. Zeeb 430*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME, 431*a4128aadSBjoern A. Zeeb "WRDS", sizeof(*data), NULL); 432*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 433*a4128aadSBjoern A. Zeeb return -EINVAL; 434*a4128aadSBjoern A. Zeeb 435*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_WRDS_REVISION) { 436*a4128aadSBjoern A. Zeeb ret = -EINVAL; 437*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n", 438*a4128aadSBjoern A. Zeeb data->revision); 439*a4128aadSBjoern A. Zeeb goto out; 440*a4128aadSBjoern A. Zeeb } 441*a4128aadSBjoern A. Zeeb 442*a4128aadSBjoern A. Zeeb /* The profile from WRDS is officially profile 1, but goes 443*a4128aadSBjoern A. Zeeb * into sar_profiles[0] (because we don't have a profile 0). 444*a4128aadSBjoern A. Zeeb */ 445*a4128aadSBjoern A. Zeeb iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode); 446*a4128aadSBjoern A. Zeeb out: 447*a4128aadSBjoern A. Zeeb kfree(data); 448*a4128aadSBjoern A. Zeeb return ret; 449*a4128aadSBjoern A. Zeeb } 450*a4128aadSBjoern A. Zeeb 451*a4128aadSBjoern A. Zeeb int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt) 452*a4128aadSBjoern A. Zeeb { 453*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_ewrd *data; 454*a4128aadSBjoern A. Zeeb int i, ret = 0; 455*a4128aadSBjoern A. Zeeb 456*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME, 457*a4128aadSBjoern A. Zeeb "EWRD", sizeof(*data), NULL); 458*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 459*a4128aadSBjoern A. Zeeb return -EINVAL; 460*a4128aadSBjoern A. Zeeb 461*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_EWRD_REVISION) { 462*a4128aadSBjoern A. Zeeb ret = -EINVAL; 463*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n", 464*a4128aadSBjoern A. Zeeb data->revision); 465*a4128aadSBjoern A. Zeeb goto out; 466*a4128aadSBjoern A. Zeeb } 467*a4128aadSBjoern A. Zeeb 468*a4128aadSBjoern A. Zeeb if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) { 469*a4128aadSBjoern A. Zeeb ret = -EINVAL; 470*a4128aadSBjoern A. Zeeb goto out; 471*a4128aadSBjoern A. Zeeb } 472*a4128aadSBjoern A. Zeeb 473*a4128aadSBjoern A. Zeeb for (i = 0; i < data->num_profiles; i++) 474*a4128aadSBjoern A. Zeeb /* The EWRD profiles officially go from 2 to 4, but we 475*a4128aadSBjoern A. Zeeb * save them in sar_profiles[1-3] (because we don't 476*a4128aadSBjoern A. Zeeb * have profile 0). So in the array we start from 1. 477*a4128aadSBjoern A. Zeeb */ 478*a4128aadSBjoern A. Zeeb iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1, 479*a4128aadSBjoern A. Zeeb data->mode); 480*a4128aadSBjoern A. Zeeb 481*a4128aadSBjoern A. Zeeb out: 482*a4128aadSBjoern A. Zeeb kfree(data); 483*a4128aadSBjoern A. Zeeb return ret; 484*a4128aadSBjoern A. Zeeb } 485*a4128aadSBjoern A. Zeeb 486*a4128aadSBjoern A. Zeeb int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt) 487*a4128aadSBjoern A. Zeeb { 488*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_wgds *data; 489*a4128aadSBjoern A. Zeeb int i, ret = 0; 490*a4128aadSBjoern A. Zeeb 491*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME, 492*a4128aadSBjoern A. Zeeb "WGDS", sizeof(*data), NULL); 493*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 494*a4128aadSBjoern A. Zeeb return -EINVAL; 495*a4128aadSBjoern A. Zeeb 496*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_WGDS_REVISION) { 497*a4128aadSBjoern A. Zeeb ret = -EINVAL; 498*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n", 499*a4128aadSBjoern A. Zeeb data->revision); 500*a4128aadSBjoern A. Zeeb goto out; 501*a4128aadSBjoern A. Zeeb } 502*a4128aadSBjoern A. Zeeb 503*a4128aadSBjoern A. Zeeb if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM || 504*a4128aadSBjoern A. Zeeb data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) { 505*a4128aadSBjoern A. Zeeb ret = -EINVAL; 506*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Invalid number of profiles in WGDS: %d\n", 507*a4128aadSBjoern A. Zeeb data->num_profiles); 508*a4128aadSBjoern A. Zeeb goto out; 509*a4128aadSBjoern A. Zeeb } 510*a4128aadSBjoern A. Zeeb 511*a4128aadSBjoern A. Zeeb fwrt->geo_rev = data->revision; 512*a4128aadSBjoern A. Zeeb for (i = 0; i < data->num_profiles; i++) 513*a4128aadSBjoern A. Zeeb memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i], 514*a4128aadSBjoern A. Zeeb sizeof(struct iwl_geo_profile)); 515*a4128aadSBjoern A. Zeeb 516*a4128aadSBjoern A. Zeeb fwrt->geo_num_profiles = data->num_profiles; 517*a4128aadSBjoern A. Zeeb fwrt->geo_enabled = true; 518*a4128aadSBjoern A. Zeeb out: 519*a4128aadSBjoern A. Zeeb kfree(data); 520*a4128aadSBjoern A. Zeeb return ret; 521*a4128aadSBjoern A. Zeeb } 522*a4128aadSBjoern A. Zeeb 523*a4128aadSBjoern A. Zeeb int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) 524*a4128aadSBjoern A. Zeeb { 525*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_ppag *data; 526*a4128aadSBjoern A. Zeeb int ret = 0; 527*a4128aadSBjoern A. Zeeb 528*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME, 529*a4128aadSBjoern A. Zeeb "PPAG", sizeof(*data), NULL); 530*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 531*a4128aadSBjoern A. Zeeb return -EINVAL; 532*a4128aadSBjoern A. Zeeb 533*a4128aadSBjoern A. Zeeb if (data->revision < IWL_UEFI_MIN_PPAG_REV || 534*a4128aadSBjoern A. Zeeb data->revision > IWL_UEFI_MAX_PPAG_REV) { 535*a4128aadSBjoern A. Zeeb ret = -EINVAL; 536*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n", 537*a4128aadSBjoern A. Zeeb data->revision); 538*a4128aadSBjoern A. Zeeb goto out; 539*a4128aadSBjoern A. Zeeb } 540*a4128aadSBjoern A. Zeeb 541*a4128aadSBjoern A. Zeeb fwrt->ppag_ver = data->revision; 542*a4128aadSBjoern A. Zeeb fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes, 543*a4128aadSBjoern A. Zeeb fwrt->ppag_ver); 544*a4128aadSBjoern A. Zeeb 545*a4128aadSBjoern A. Zeeb BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains)); 546*a4128aadSBjoern A. Zeeb memcpy(&fwrt->ppag_chains, &data->ppag_chains, 547*a4128aadSBjoern A. Zeeb sizeof(data->ppag_chains)); 548*a4128aadSBjoern A. Zeeb out: 549*a4128aadSBjoern A. Zeeb kfree(data); 550*a4128aadSBjoern A. Zeeb return ret; 551*a4128aadSBjoern A. Zeeb } 552*a4128aadSBjoern A. Zeeb 553*a4128aadSBjoern A. Zeeb int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt, 554*a4128aadSBjoern A. Zeeb struct iwl_tas_data *tas_data) 555*a4128aadSBjoern A. Zeeb { 556*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_wtas *uefi_tas; 557*a4128aadSBjoern A. Zeeb int ret = 0, enabled, i; 558*a4128aadSBjoern A. Zeeb 559*a4128aadSBjoern A. Zeeb uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME, 560*a4128aadSBjoern A. Zeeb "WTAS", sizeof(*uefi_tas), NULL); 561*a4128aadSBjoern A. Zeeb if (IS_ERR(uefi_tas)) 562*a4128aadSBjoern A. Zeeb return -EINVAL; 563*a4128aadSBjoern A. Zeeb 564*a4128aadSBjoern A. Zeeb if (uefi_tas->revision != IWL_UEFI_WTAS_REVISION) { 565*a4128aadSBjoern A. Zeeb ret = -EINVAL; 566*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n", 567*a4128aadSBjoern A. Zeeb uefi_tas->revision); 568*a4128aadSBjoern A. Zeeb goto out; 569*a4128aadSBjoern A. Zeeb } 570*a4128aadSBjoern A. Zeeb 571*a4128aadSBjoern A. Zeeb enabled = iwl_parse_tas_selection(fwrt, tas_data, 572*a4128aadSBjoern A. Zeeb uefi_tas->tas_selection); 573*a4128aadSBjoern A. Zeeb if (!enabled) { 574*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); 575*a4128aadSBjoern A. Zeeb ret = 0; 576*a4128aadSBjoern A. Zeeb goto out; 577*a4128aadSBjoern A. Zeeb } 578*a4128aadSBjoern A. Zeeb 579*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", 580*a4128aadSBjoern A. Zeeb uefi_tas->revision); 581*a4128aadSBjoern A. Zeeb if (uefi_tas->black_list_size > IWL_WTAS_BLACK_LIST_MAX) { 582*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %d\n", 583*a4128aadSBjoern A. Zeeb uefi_tas->black_list_size); 584*a4128aadSBjoern A. Zeeb ret = -EINVAL; 585*a4128aadSBjoern A. Zeeb goto out; 586*a4128aadSBjoern A. Zeeb } 587*a4128aadSBjoern A. Zeeb tas_data->block_list_size = cpu_to_le32(uefi_tas->black_list_size); 588*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size); 589*a4128aadSBjoern A. Zeeb 590*a4128aadSBjoern A. Zeeb for (i = 0; i < uefi_tas->black_list_size; i++) { 591*a4128aadSBjoern A. Zeeb tas_data->block_list_array[i] = 592*a4128aadSBjoern A. Zeeb cpu_to_le32(uefi_tas->black_list[i]); 593*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", 594*a4128aadSBjoern A. Zeeb uefi_tas->black_list[i]); 595*a4128aadSBjoern A. Zeeb } 596*a4128aadSBjoern A. Zeeb out: 597*a4128aadSBjoern A. Zeeb kfree(uefi_tas); 598*a4128aadSBjoern A. Zeeb return ret; 599*a4128aadSBjoern A. Zeeb } 600*a4128aadSBjoern A. Zeeb 601*a4128aadSBjoern A. Zeeb int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt, 602*a4128aadSBjoern A. Zeeb u64 *dflt_pwr_limit) 603*a4128aadSBjoern A. Zeeb { 604*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_splc *data; 605*a4128aadSBjoern A. Zeeb int ret = 0; 606*a4128aadSBjoern A. Zeeb 607*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME, 608*a4128aadSBjoern A. Zeeb "SPLC", sizeof(*data), NULL); 609*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 610*a4128aadSBjoern A. Zeeb return -EINVAL; 611*a4128aadSBjoern A. Zeeb 612*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_SPLC_REVISION) { 613*a4128aadSBjoern A. Zeeb ret = -EINVAL; 614*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI SPLC revision:%d\n", 615*a4128aadSBjoern A. Zeeb data->revision); 616*a4128aadSBjoern A. Zeeb goto out; 617*a4128aadSBjoern A. Zeeb } 618*a4128aadSBjoern A. Zeeb *dflt_pwr_limit = data->default_pwr_limit; 619*a4128aadSBjoern A. Zeeb out: 620*a4128aadSBjoern A. Zeeb kfree(data); 621*a4128aadSBjoern A. Zeeb return ret; 622*a4128aadSBjoern A. Zeeb } 623*a4128aadSBjoern A. Zeeb 624*a4128aadSBjoern A. Zeeb int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc) 625*a4128aadSBjoern A. Zeeb { 626*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_wrdd *data; 627*a4128aadSBjoern A. Zeeb int ret = 0; 628*a4128aadSBjoern A. Zeeb 629*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME, 630*a4128aadSBjoern A. Zeeb "WRDD", sizeof(*data), NULL); 631*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 632*a4128aadSBjoern A. Zeeb return -EINVAL; 633*a4128aadSBjoern A. Zeeb 634*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_WRDD_REVISION) { 635*a4128aadSBjoern A. Zeeb ret = -EINVAL; 636*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", 637*a4128aadSBjoern A. Zeeb data->revision); 638*a4128aadSBjoern A. Zeeb goto out; 639*a4128aadSBjoern A. Zeeb } 640*a4128aadSBjoern A. Zeeb 641*a4128aadSBjoern A. Zeeb if (data->mcc != UEFI_MCC_CHINA) { 642*a4128aadSBjoern A. Zeeb ret = -EINVAL; 643*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n"); 644*a4128aadSBjoern A. Zeeb goto out; 645*a4128aadSBjoern A. Zeeb } 646*a4128aadSBjoern A. Zeeb 647*a4128aadSBjoern A. Zeeb mcc[0] = (data->mcc >> 8) & 0xff; 648*a4128aadSBjoern A. Zeeb mcc[1] = data->mcc & 0xff; 649*a4128aadSBjoern A. Zeeb mcc[2] = '\0'; 650*a4128aadSBjoern A. Zeeb out: 651*a4128aadSBjoern A. Zeeb kfree(data); 652*a4128aadSBjoern A. Zeeb return ret; 653*a4128aadSBjoern A. Zeeb } 654*a4128aadSBjoern A. Zeeb 655*a4128aadSBjoern A. Zeeb int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) 656*a4128aadSBjoern A. Zeeb { 657*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_eckv *data; 658*a4128aadSBjoern A. Zeeb int ret = 0; 659*a4128aadSBjoern A. Zeeb 660*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME, 661*a4128aadSBjoern A. Zeeb "ECKV", sizeof(*data), NULL); 662*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 663*a4128aadSBjoern A. Zeeb return -EINVAL; 664*a4128aadSBjoern A. Zeeb 665*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_ECKV_REVISION) { 666*a4128aadSBjoern A. Zeeb ret = -EINVAL; 667*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", 668*a4128aadSBjoern A. Zeeb data->revision); 669*a4128aadSBjoern A. Zeeb goto out; 670*a4128aadSBjoern A. Zeeb } 671*a4128aadSBjoern A. Zeeb *extl_clk = data->ext_clock_valid; 672*a4128aadSBjoern A. Zeeb out: 673*a4128aadSBjoern A. Zeeb kfree(data); 674*a4128aadSBjoern A. Zeeb return ret; 675*a4128aadSBjoern A. Zeeb } 676*a4128aadSBjoern A. Zeeb 677*a4128aadSBjoern A. Zeeb int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value) 678*a4128aadSBjoern A. Zeeb { 679*a4128aadSBjoern A. Zeeb struct uefi_cnv_wlan_wbem_data *data; 680*a4128aadSBjoern A. Zeeb int ret = 0; 681*a4128aadSBjoern A. Zeeb 682*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WBEM_NAME, 683*a4128aadSBjoern A. Zeeb "WBEM", sizeof(*data), NULL); 684*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 685*a4128aadSBjoern A. Zeeb return -EINVAL; 686*a4128aadSBjoern A. Zeeb 687*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_WBEM_REVISION) { 688*a4128aadSBjoern A. Zeeb ret = -EINVAL; 689*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WBEM revision:%d\n", 690*a4128aadSBjoern A. Zeeb data->revision); 691*a4128aadSBjoern A. Zeeb goto out; 692*a4128aadSBjoern A. Zeeb } 693*a4128aadSBjoern A. Zeeb *value = data->wbem_320mhz_per_mcc & IWL_UEFI_WBEM_REV0_MASK; 694*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from UEFI\n"); 695*a4128aadSBjoern A. Zeeb out: 696*a4128aadSBjoern A. Zeeb kfree(data); 697*a4128aadSBjoern A. Zeeb return ret; 698*a4128aadSBjoern A. Zeeb } 699*a4128aadSBjoern A. Zeeb 700*a4128aadSBjoern A. Zeeb int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, 701*a4128aadSBjoern A. Zeeb u32 *value) 702*a4128aadSBjoern A. Zeeb { 703*a4128aadSBjoern A. Zeeb struct uefi_cnv_var_general_cfg *data; 704*a4128aadSBjoern A. Zeeb int ret = -EINVAL; 705*a4128aadSBjoern A. Zeeb 706*a4128aadSBjoern A. Zeeb /* Not supported function index */ 707*a4128aadSBjoern A. Zeeb if (func >= DSM_FUNC_NUM_FUNCS || func == 5) 708*a4128aadSBjoern A. Zeeb return -EOPNOTSUPP; 709*a4128aadSBjoern A. Zeeb 710*a4128aadSBjoern A. Zeeb data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME, 711*a4128aadSBjoern A. Zeeb "DSM", sizeof(*data), NULL); 712*a4128aadSBjoern A. Zeeb if (IS_ERR(data)) 713*a4128aadSBjoern A. Zeeb return -EINVAL; 714*a4128aadSBjoern A. Zeeb 715*a4128aadSBjoern A. Zeeb if (data->revision != IWL_UEFI_DSM_REVISION) { 716*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSM revision:%d\n", 717*a4128aadSBjoern A. Zeeb data->revision); 718*a4128aadSBjoern A. Zeeb goto out; 719*a4128aadSBjoern A. Zeeb } 720*a4128aadSBjoern A. Zeeb 721*a4128aadSBjoern A. Zeeb if (ARRAY_SIZE(data->functions) != UEFI_MAX_DSM_FUNCS) { 722*a4128aadSBjoern A. Zeeb IWL_DEBUG_RADIO(fwrt, "Invalid size of DSM functions array\n"); 723*a4128aadSBjoern A. Zeeb goto out; 724*a4128aadSBjoern A. Zeeb } 725*a4128aadSBjoern A. Zeeb 726*a4128aadSBjoern A. Zeeb *value = data->functions[func]; 727*a4128aadSBjoern A. Zeeb ret = 0; 728*a4128aadSBjoern A. Zeeb out: 729*a4128aadSBjoern A. Zeeb kfree(data); 730*a4128aadSBjoern A. Zeeb return ret; 731*a4128aadSBjoern A. Zeeb } 732