xref: /freebsd/sys/contrib/dev/iwlwifi/fw/uefi.c (revision a4128aad8503277614f2d214011ef60a19447b83)
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