xref: /linux/drivers/net/wireless/intel/iwlwifi/fw/uefi.c (revision 9410645520e9b820069761f3450ef6661418e279)
184c3c995SLuca Coelho // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
284c3c995SLuca Coelho /*
374f4cd71SMiri Korenblit  * Copyright(c) 2021-2024 Intel Corporation
484c3c995SLuca Coelho  */
584c3c995SLuca Coelho 
684c3c995SLuca Coelho #include "iwl-drv.h"
784c3c995SLuca Coelho #include "pnvm.h"
884c3c995SLuca Coelho #include "iwl-prph.h"
984c3c995SLuca Coelho #include "iwl-io.h"
1084c3c995SLuca Coelho 
1184c3c995SLuca Coelho #include "fw/uefi.h"
129dad325fSLuca Coelho #include "fw/api/alive.h"
1384c3c995SLuca Coelho #include <linux/efi.h>
14c593d2faSAyala Barazani #include "fw/runtime.h"
1584c3c995SLuca Coelho 
1684c3c995SLuca Coelho #define IWL_EFI_VAR_GUID EFI_GUID(0x92daaf2f, 0xc02b, 0x455b,	\
1784c3c995SLuca Coelho 				  0xb2, 0xec, 0xf5, 0xa3,	\
1884c3c995SLuca Coelho 				  0x59, 0x4f, 0x4a, 0xea)
1984c3c995SLuca Coelho 
20372a7148SGregory Greenman struct iwl_uefi_pnvm_mem_desc {
21372a7148SGregory Greenman 	__le32 addr;
22372a7148SGregory Greenman 	__le32 size;
23372a7148SGregory Greenman 	const u8 data[];
24372a7148SGregory Greenman } __packed;
25372a7148SGregory Greenman 
iwl_uefi_get_variable(efi_char16_t * name,efi_guid_t * guid,unsigned long * data_size)268ae3e231SGregory Greenman static void *iwl_uefi_get_variable(efi_char16_t *name, efi_guid_t *guid,
278ae3e231SGregory Greenman 				   unsigned long *data_size)
2884c3c995SLuca Coelho {
290c4bad7fSArd Biesheuvel 	efi_status_t status;
308ae3e231SGregory Greenman 	void *data;
3184c3c995SLuca Coelho 
328ae3e231SGregory Greenman 	if (!data_size)
338ae3e231SGregory Greenman 		return ERR_PTR(-EINVAL);
349dad325fSLuca Coelho 
350c4bad7fSArd Biesheuvel 	if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
360c4bad7fSArd Biesheuvel 		return ERR_PTR(-ENODEV);
3784c3c995SLuca Coelho 
388ae3e231SGregory Greenman 	/* first call with NULL data to get the exact entry size */
398ae3e231SGregory Greenman 	*data_size = 0;
408ae3e231SGregory Greenman 	status = efi.get_variable(name, guid, NULL, data_size, NULL);
418ae3e231SGregory Greenman 	if (status != EFI_BUFFER_TOO_SMALL || !*data_size)
428ae3e231SGregory Greenman 		return ERR_PTR(-EIO);
4384c3c995SLuca Coelho 
448ae3e231SGregory Greenman 	data = kmalloc(*data_size, GFP_KERNEL);
450c4bad7fSArd Biesheuvel 	if (!data)
460c4bad7fSArd Biesheuvel 		return ERR_PTR(-ENOMEM);
4784c3c995SLuca Coelho 
488ae3e231SGregory Greenman 	status = efi.get_variable(name, guid, NULL, data_size, data);
490c4bad7fSArd Biesheuvel 	if (status != EFI_SUCCESS) {
5084c3c995SLuca Coelho 		kfree(data);
510c4bad7fSArd Biesheuvel 		return ERR_PTR(-ENOENT);
5284c3c995SLuca Coelho 	}
5384c3c995SLuca Coelho 
548ae3e231SGregory Greenman 	return data;
558ae3e231SGregory Greenman }
568ae3e231SGregory Greenman 
iwl_uefi_get_pnvm(struct iwl_trans * trans,size_t * len)578ae3e231SGregory Greenman void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len)
588ae3e231SGregory Greenman {
598ae3e231SGregory Greenman 	unsigned long package_size;
608ae3e231SGregory Greenman 	void *data;
618ae3e231SGregory Greenman 
628ae3e231SGregory Greenman 	*len = 0;
638ae3e231SGregory Greenman 
648ae3e231SGregory Greenman 	data = iwl_uefi_get_variable(IWL_UEFI_OEM_PNVM_NAME, &IWL_EFI_VAR_GUID,
658ae3e231SGregory Greenman 				     &package_size);
668ae3e231SGregory Greenman 	if (IS_ERR(data)) {
678ae3e231SGregory Greenman 		IWL_DEBUG_FW(trans,
688ae3e231SGregory Greenman 			     "PNVM UEFI variable not found 0x%lx (len %lu)\n",
698ae3e231SGregory Greenman 			     PTR_ERR(data), package_size);
708ae3e231SGregory Greenman 		return data;
718ae3e231SGregory Greenman 	}
728ae3e231SGregory Greenman 
731476ff21SLinus Torvalds 	IWL_DEBUG_FW(trans, "Read PNVM from UEFI with size %lu\n", package_size);
7484c3c995SLuca Coelho 	*len = package_size;
7584c3c995SLuca Coelho 
7684c3c995SLuca Coelho 	return data;
7784c3c995SLuca Coelho }
789dad325fSLuca Coelho 
79a6dfe1e7SMiri Korenblit static
iwl_uefi_get_verified_variable(struct iwl_trans * trans,efi_char16_t * uefi_var_name,char * var_name,unsigned int expected_size,unsigned long * size)80a6dfe1e7SMiri Korenblit void *iwl_uefi_get_verified_variable(struct iwl_trans *trans,
81a6dfe1e7SMiri Korenblit 				     efi_char16_t *uefi_var_name,
82a6dfe1e7SMiri Korenblit 				     char *var_name,
83a6dfe1e7SMiri Korenblit 				     unsigned int expected_size,
84a6dfe1e7SMiri Korenblit 				     unsigned long *size)
85a6dfe1e7SMiri Korenblit {
86a6dfe1e7SMiri Korenblit 	void *var;
87a6dfe1e7SMiri Korenblit 	unsigned long var_size;
88a6dfe1e7SMiri Korenblit 
89a6dfe1e7SMiri Korenblit 	var = iwl_uefi_get_variable(uefi_var_name, &IWL_EFI_VAR_GUID,
90a6dfe1e7SMiri Korenblit 				    &var_size);
91a6dfe1e7SMiri Korenblit 
92a6dfe1e7SMiri Korenblit 	if (IS_ERR(var)) {
93a6dfe1e7SMiri Korenblit 		IWL_DEBUG_RADIO(trans,
94a6dfe1e7SMiri Korenblit 				"%s UEFI variable not found 0x%lx\n", var_name,
95a6dfe1e7SMiri Korenblit 				PTR_ERR(var));
96a6dfe1e7SMiri Korenblit 		return var;
97a6dfe1e7SMiri Korenblit 	}
98a6dfe1e7SMiri Korenblit 
99a6dfe1e7SMiri Korenblit 	if (var_size < expected_size) {
100a6dfe1e7SMiri Korenblit 		IWL_DEBUG_RADIO(trans,
101a6dfe1e7SMiri Korenblit 				"Invalid %s UEFI variable len (%lu)\n",
102a6dfe1e7SMiri Korenblit 				var_name, var_size);
103a6dfe1e7SMiri Korenblit 		kfree(var);
104a6dfe1e7SMiri Korenblit 		return ERR_PTR(-EINVAL);
105a6dfe1e7SMiri Korenblit 	}
106a6dfe1e7SMiri Korenblit 
107a6dfe1e7SMiri Korenblit 	IWL_DEBUG_RADIO(trans, "%s from UEFI with size %lu\n", var_name,
108a6dfe1e7SMiri Korenblit 			var_size);
109a6dfe1e7SMiri Korenblit 
110a6dfe1e7SMiri Korenblit 	if (size)
111a6dfe1e7SMiri Korenblit 		*size = var_size;
112a6dfe1e7SMiri Korenblit 	return var;
113a6dfe1e7SMiri Korenblit }
114a6dfe1e7SMiri Korenblit 
iwl_uefi_handle_tlv_mem_desc(struct iwl_trans * trans,const u8 * data,u32 tlv_len,struct iwl_pnvm_image * pnvm_data)115372a7148SGregory Greenman int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,
116372a7148SGregory Greenman 				 u32 tlv_len, struct iwl_pnvm_image *pnvm_data)
117372a7148SGregory Greenman {
118372a7148SGregory Greenman 	const struct iwl_uefi_pnvm_mem_desc *desc = (const void *)data;
119372a7148SGregory Greenman 	u32 data_len;
120372a7148SGregory Greenman 
121372a7148SGregory Greenman 	if (tlv_len < sizeof(*desc)) {
122372a7148SGregory Greenman 		IWL_DEBUG_FW(trans, "TLV len (%d) is too small\n", tlv_len);
123372a7148SGregory Greenman 		return -EINVAL;
124372a7148SGregory Greenman 	}
125372a7148SGregory Greenman 
126372a7148SGregory Greenman 	data_len = tlv_len - sizeof(*desc);
127372a7148SGregory Greenman 
128372a7148SGregory Greenman 	IWL_DEBUG_FW(trans,
129372a7148SGregory Greenman 		     "Handle IWL_UCODE_TLV_MEM_DESC, len %d data_len %d\n",
130372a7148SGregory Greenman 		     tlv_len, data_len);
131372a7148SGregory Greenman 
132372a7148SGregory Greenman 	if (le32_to_cpu(desc->size) != data_len) {
133372a7148SGregory Greenman 		IWL_DEBUG_FW(trans, "invalid mem desc size %d\n", desc->size);
134372a7148SGregory Greenman 		return -EINVAL;
135372a7148SGregory Greenman 	}
136372a7148SGregory Greenman 
137372a7148SGregory Greenman 	if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) {
138372a7148SGregory Greenman 		IWL_DEBUG_FW(trans, "too many payloads to allocate in DRAM.\n");
139372a7148SGregory Greenman 		return -EINVAL;
140372a7148SGregory Greenman 	}
141372a7148SGregory Greenman 
142372a7148SGregory Greenman 	IWL_DEBUG_FW(trans, "Adding data (size %d)\n", data_len);
143372a7148SGregory Greenman 
144372a7148SGregory Greenman 	pnvm_data->chunks[pnvm_data->n_chunks].data = desc->data;
145372a7148SGregory Greenman 	pnvm_data->chunks[pnvm_data->n_chunks].len = data_len;
146372a7148SGregory Greenman 	pnvm_data->n_chunks++;
147372a7148SGregory Greenman 
148372a7148SGregory Greenman 	return 0;
149372a7148SGregory Greenman }
150372a7148SGregory Greenman 
iwl_uefi_reduce_power_section(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data)151ea3571f4SAlon Giladi static int iwl_uefi_reduce_power_section(struct iwl_trans *trans,
152ea3571f4SAlon Giladi 					 const u8 *data, size_t len,
153ea3571f4SAlon Giladi 					 struct iwl_pnvm_image *pnvm_data)
1549dad325fSLuca Coelho {
15586e8e657SJohannes Berg 	const struct iwl_ucode_tlv *tlv;
1569dad325fSLuca Coelho 
1579dad325fSLuca Coelho 	IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");
158ea3571f4SAlon Giladi 	memset(pnvm_data, 0, sizeof(*pnvm_data));
1599dad325fSLuca Coelho 
1609dad325fSLuca Coelho 	while (len >= sizeof(*tlv)) {
1619dad325fSLuca Coelho 		u32 tlv_len, tlv_type;
1629dad325fSLuca Coelho 
1639dad325fSLuca Coelho 		len -= sizeof(*tlv);
16486e8e657SJohannes Berg 		tlv = (const void *)data;
1659dad325fSLuca Coelho 
1669dad325fSLuca Coelho 		tlv_len = le32_to_cpu(tlv->length);
1679dad325fSLuca Coelho 		tlv_type = le32_to_cpu(tlv->type);
1689dad325fSLuca Coelho 
1699dad325fSLuca Coelho 		if (len < tlv_len) {
1709dad325fSLuca Coelho 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
1719dad325fSLuca Coelho 				len, tlv_len);
172ea3571f4SAlon Giladi 			return -EINVAL;
1739dad325fSLuca Coelho 		}
1749dad325fSLuca Coelho 
1759dad325fSLuca Coelho 		data += sizeof(*tlv);
1769dad325fSLuca Coelho 
1779dad325fSLuca Coelho 		switch (tlv_type) {
178372a7148SGregory Greenman 		case IWL_UCODE_TLV_MEM_DESC:
179372a7148SGregory Greenman 			if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len,
180372a7148SGregory Greenman 							 pnvm_data))
181ea3571f4SAlon Giladi 				return -EINVAL;
1829dad325fSLuca Coelho 			break;
1839dad325fSLuca Coelho 		case IWL_UCODE_TLV_PNVM_SKU:
1849dad325fSLuca Coelho 			IWL_DEBUG_FW(trans,
1859dad325fSLuca Coelho 				     "New REDUCE_POWER section started, stop parsing.\n");
1869dad325fSLuca Coelho 			goto done;
1879dad325fSLuca Coelho 		default:
1889dad325fSLuca Coelho 			IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
1899dad325fSLuca Coelho 				     tlv_type, tlv_len);
1909dad325fSLuca Coelho 			break;
1919dad325fSLuca Coelho 		}
1929dad325fSLuca Coelho 
1939dad325fSLuca Coelho 		len -= ALIGN(tlv_len, 4);
1949dad325fSLuca Coelho 		data += ALIGN(tlv_len, 4);
1959dad325fSLuca Coelho 	}
1969dad325fSLuca Coelho 
1979dad325fSLuca Coelho done:
198ea3571f4SAlon Giladi 	if (!pnvm_data->n_chunks) {
1999dad325fSLuca Coelho 		IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
200ea3571f4SAlon Giladi 		return -ENOENT;
201ea3571f4SAlon Giladi 	}
202ea3571f4SAlon Giladi 	return 0;
2039dad325fSLuca Coelho }
2049dad325fSLuca Coelho 
iwl_uefi_reduce_power_parse(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data)205380bf72dSAlon Giladi int iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
206ea3571f4SAlon Giladi 				const u8 *data, size_t len,
207ea3571f4SAlon Giladi 				struct iwl_pnvm_image *pnvm_data)
2089dad325fSLuca Coelho {
20986e8e657SJohannes Berg 	const struct iwl_ucode_tlv *tlv;
2109dad325fSLuca Coelho 
2119dad325fSLuca Coelho 	IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");
2129dad325fSLuca Coelho 
2139dad325fSLuca Coelho 	while (len >= sizeof(*tlv)) {
2149dad325fSLuca Coelho 		u32 tlv_len, tlv_type;
2159dad325fSLuca Coelho 
2169dad325fSLuca Coelho 		len -= sizeof(*tlv);
21786e8e657SJohannes Berg 		tlv = (const void *)data;
2189dad325fSLuca Coelho 
2199dad325fSLuca Coelho 		tlv_len = le32_to_cpu(tlv->length);
2209dad325fSLuca Coelho 		tlv_type = le32_to_cpu(tlv->type);
2219dad325fSLuca Coelho 
2229dad325fSLuca Coelho 		if (len < tlv_len) {
2239dad325fSLuca Coelho 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
2249dad325fSLuca Coelho 				len, tlv_len);
225ea3571f4SAlon Giladi 			return -EINVAL;
2269dad325fSLuca Coelho 		}
2279dad325fSLuca Coelho 
2289dad325fSLuca Coelho 		if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
22986e8e657SJohannes Berg 			const struct iwl_sku_id *sku_id =
23086e8e657SJohannes Berg 				(const void *)(data + sizeof(*tlv));
2319dad325fSLuca Coelho 
2329dad325fSLuca Coelho 			IWL_DEBUG_FW(trans,
2339dad325fSLuca Coelho 				     "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
2349dad325fSLuca Coelho 				     tlv_len);
2359dad325fSLuca Coelho 			IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
2369dad325fSLuca Coelho 				     le32_to_cpu(sku_id->data[0]),
2379dad325fSLuca Coelho 				     le32_to_cpu(sku_id->data[1]),
2389dad325fSLuca Coelho 				     le32_to_cpu(sku_id->data[2]));
2399dad325fSLuca Coelho 
2409dad325fSLuca Coelho 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
2419dad325fSLuca Coelho 			len -= ALIGN(tlv_len, 4);
2429dad325fSLuca Coelho 
2439dad325fSLuca Coelho 			if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
2449dad325fSLuca Coelho 			    trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
2459dad325fSLuca Coelho 			    trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
246ea3571f4SAlon Giladi 				int ret = iwl_uefi_reduce_power_section(trans,
247ea3571f4SAlon Giladi 								    data, len,
248ea3571f4SAlon Giladi 								    pnvm_data);
249ea3571f4SAlon Giladi 				if (!ret)
250ea3571f4SAlon Giladi 					return 0;
2519dad325fSLuca Coelho 			} else {
2529dad325fSLuca Coelho 				IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
2539dad325fSLuca Coelho 			}
2549dad325fSLuca Coelho 		} else {
2559dad325fSLuca Coelho 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
2569dad325fSLuca Coelho 			len -= ALIGN(tlv_len, 4);
2579dad325fSLuca Coelho 		}
2589dad325fSLuca Coelho 	}
2599dad325fSLuca Coelho 
260ea3571f4SAlon Giladi 	return -ENOENT;
2619dad325fSLuca Coelho }
2629dad325fSLuca Coelho 
iwl_uefi_get_reduced_power(struct iwl_trans * trans,size_t * len)263380bf72dSAlon Giladi u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
2649dad325fSLuca Coelho {
2659dad325fSLuca Coelho 	struct pnvm_sku_package *package;
2669dad325fSLuca Coelho 	unsigned long package_size;
267380bf72dSAlon Giladi 	u8 *data;
2689dad325fSLuca Coelho 
269a6dfe1e7SMiri Korenblit 	package = iwl_uefi_get_verified_variable(trans,
270a6dfe1e7SMiri Korenblit 						 IWL_UEFI_REDUCED_POWER_NAME,
271a6dfe1e7SMiri Korenblit 						 "Reduced Power",
272a6dfe1e7SMiri Korenblit 						 sizeof(*package),
273a6dfe1e7SMiri Korenblit 						 &package_size);
274a6dfe1e7SMiri Korenblit 	if (IS_ERR(package))
2758ae3e231SGregory Greenman 		return ERR_CAST(package);
2769dad325fSLuca Coelho 
2779dad325fSLuca Coelho 	IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
2789dad325fSLuca Coelho 		     package->rev, package->total_size, package->n_skus);
2799dad325fSLuca Coelho 
280380bf72dSAlon Giladi 	*len = package_size - sizeof(*package);
281380bf72dSAlon Giladi 	data = kmemdup(package->data, *len, GFP_KERNEL);
2828ae3e231SGregory Greenman 	if (!data) {
2838ae3e231SGregory Greenman 		kfree(package);
284380bf72dSAlon Giladi 		return ERR_PTR(-ENOMEM);
2858ae3e231SGregory Greenman 	}
2868ae3e231SGregory Greenman 
2879dad325fSLuca Coelho 	kfree(package);
2889dad325fSLuca Coelho 
289380bf72dSAlon Giladi 	return data;
2909dad325fSLuca Coelho }
291c593d2faSAyala Barazani 
iwl_uefi_step_parse(struct uefi_cnv_common_step_data * common_step_data,struct iwl_trans * trans)29209b4c35dSAyala Barazani static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data,
29309b4c35dSAyala Barazani 			       struct iwl_trans *trans)
29409b4c35dSAyala Barazani {
29509b4c35dSAyala Barazani 	if (common_step_data->revision != 1)
29609b4c35dSAyala Barazani 		return -EINVAL;
29709b4c35dSAyala Barazani 
29809b4c35dSAyala Barazani 	trans->mbx_addr_0_step = (u32)common_step_data->revision |
29909b4c35dSAyala Barazani 		(u32)common_step_data->cnvi_eq_channel << 8 |
30009b4c35dSAyala Barazani 		(u32)common_step_data->cnvr_eq_channel << 16 |
30109b4c35dSAyala Barazani 		(u32)common_step_data->radio1 << 24;
30209b4c35dSAyala Barazani 	trans->mbx_addr_1_step = (u32)common_step_data->radio2;
30309b4c35dSAyala Barazani 	return 0;
30409b4c35dSAyala Barazani }
30509b4c35dSAyala Barazani 
iwl_uefi_get_step_table(struct iwl_trans * trans)30609b4c35dSAyala Barazani void iwl_uefi_get_step_table(struct iwl_trans *trans)
30709b4c35dSAyala Barazani {
30809b4c35dSAyala Barazani 	struct uefi_cnv_common_step_data *data;
30909b4c35dSAyala Barazani 	int ret;
31009b4c35dSAyala Barazani 
31109b4c35dSAyala Barazani 	if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
31209b4c35dSAyala Barazani 		return;
31309b4c35dSAyala Barazani 
314a6dfe1e7SMiri Korenblit 	data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_STEP_NAME,
315a6dfe1e7SMiri Korenblit 					      "STEP", sizeof(*data), NULL);
316a6dfe1e7SMiri Korenblit 	if (IS_ERR(data))
3178ae3e231SGregory Greenman 		return;
31809b4c35dSAyala Barazani 
31909b4c35dSAyala Barazani 	ret = iwl_uefi_step_parse(data, trans);
32009b4c35dSAyala Barazani 	if (ret < 0)
32109b4c35dSAyala Barazani 		IWL_DEBUG_FW(trans, "Cannot read STEP tables. rev is invalid\n");
32209b4c35dSAyala Barazani 
32309b4c35dSAyala Barazani 	kfree(data);
32409b4c35dSAyala Barazani }
32509b4c35dSAyala Barazani IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table);
32609b4c35dSAyala Barazani 
iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data * sgom_data,struct iwl_fw_runtime * fwrt)327c593d2faSAyala Barazani static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
328c593d2faSAyala Barazani 			       struct iwl_fw_runtime *fwrt)
329c593d2faSAyala Barazani {
330c593d2faSAyala Barazani 	int i, j;
331c593d2faSAyala Barazani 
332c593d2faSAyala Barazani 	if (sgom_data->revision != 1)
333c593d2faSAyala Barazani 		return -EINVAL;
334c593d2faSAyala Barazani 
335c593d2faSAyala Barazani 	memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
336c593d2faSAyala Barazani 	       sizeof(fwrt->sgom_table.offset_map));
337c593d2faSAyala Barazani 
338c593d2faSAyala Barazani 	for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
339c593d2faSAyala Barazani 		for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
340c593d2faSAyala Barazani 			/* since each byte is composed of to values, */
341c593d2faSAyala Barazani 			/* one for each letter, */
342c593d2faSAyala Barazani 			/* extract and check each of them separately */
343c593d2faSAyala Barazani 			u8 value = fwrt->sgom_table.offset_map[i][j];
344c593d2faSAyala Barazani 			u8 low = value & 0xF;
345c593d2faSAyala Barazani 			u8 high = (value & 0xF0) >> 4;
346c593d2faSAyala Barazani 
347c593d2faSAyala Barazani 			if (high > fwrt->geo_num_profiles)
348c593d2faSAyala Barazani 				high = 0;
349c593d2faSAyala Barazani 			if (low > fwrt->geo_num_profiles)
350c593d2faSAyala Barazani 				low = 0;
351c593d2faSAyala Barazani 			fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
352c593d2faSAyala Barazani 		}
353c593d2faSAyala Barazani 	}
354c593d2faSAyala Barazani 
355c593d2faSAyala Barazani 	fwrt->sgom_enabled = true;
356c593d2faSAyala Barazani 	return 0;
357c593d2faSAyala Barazani }
358c593d2faSAyala Barazani 
iwl_uefi_get_sgom_table(struct iwl_trans * trans,struct iwl_fw_runtime * fwrt)359c593d2faSAyala Barazani void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
360c593d2faSAyala Barazani 			     struct iwl_fw_runtime *fwrt)
361c593d2faSAyala Barazani {
362c593d2faSAyala Barazani 	struct uefi_cnv_wlan_sgom_data *data;
3630c4bad7fSArd Biesheuvel 	int ret;
364c593d2faSAyala Barazani 
3658ae3e231SGregory Greenman 	if (!fwrt->geo_enabled)
366c593d2faSAyala Barazani 		return;
367c593d2faSAyala Barazani 
368a6dfe1e7SMiri Korenblit 	data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_SGOM_NAME,
369a6dfe1e7SMiri Korenblit 					      "SGOM", sizeof(*data), NULL);
370a6dfe1e7SMiri Korenblit 	if (IS_ERR(data))
3718ae3e231SGregory Greenman 		return;
372c593d2faSAyala Barazani 
373c593d2faSAyala Barazani 	ret = iwl_uefi_sgom_parse(data, fwrt);
374c593d2faSAyala Barazani 	if (ret < 0)
375c593d2faSAyala Barazani 		IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");
376c593d2faSAyala Barazani 
377c593d2faSAyala Barazani 	kfree(data);
378c593d2faSAyala Barazani }
379c593d2faSAyala Barazani IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
3804a9bb5b4SMukesh Sisodiya 
iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data * uats_data,struct iwl_fw_runtime * fwrt)3814a9bb5b4SMukesh Sisodiya static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data,
3824a9bb5b4SMukesh Sisodiya 			       struct iwl_fw_runtime *fwrt)
3834a9bb5b4SMukesh Sisodiya {
3844a9bb5b4SMukesh Sisodiya 	if (uats_data->revision != 1)
3854a9bb5b4SMukesh Sisodiya 		return -EINVAL;
3864a9bb5b4SMukesh Sisodiya 
3874a9bb5b4SMukesh Sisodiya 	memcpy(fwrt->uats_table.offset_map, uats_data->offset_map,
3884a9bb5b4SMukesh Sisodiya 	       sizeof(fwrt->uats_table.offset_map));
3894a9bb5b4SMukesh Sisodiya 	return 0;
3904a9bb5b4SMukesh Sisodiya }
3914a9bb5b4SMukesh Sisodiya 
iwl_uefi_get_uats_table(struct iwl_trans * trans,struct iwl_fw_runtime * fwrt)3924a9bb5b4SMukesh Sisodiya int iwl_uefi_get_uats_table(struct iwl_trans *trans,
3934a9bb5b4SMukesh Sisodiya 			    struct iwl_fw_runtime *fwrt)
3944a9bb5b4SMukesh Sisodiya {
3954a9bb5b4SMukesh Sisodiya 	struct uefi_cnv_wlan_uats_data *data;
3964a9bb5b4SMukesh Sisodiya 	int ret;
3974a9bb5b4SMukesh Sisodiya 
398a6dfe1e7SMiri Korenblit 	data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME,
399a6dfe1e7SMiri Korenblit 					      "UATS", sizeof(*data), NULL);
400a6dfe1e7SMiri Korenblit 	if (IS_ERR(data))
4014a9bb5b4SMukesh Sisodiya 		return -EINVAL;
4024a9bb5b4SMukesh Sisodiya 
4034a9bb5b4SMukesh Sisodiya 	ret = iwl_uefi_uats_parse(data, fwrt);
4044a9bb5b4SMukesh Sisodiya 	if (ret < 0) {
4054a9bb5b4SMukesh Sisodiya 		IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n");
4064a9bb5b4SMukesh Sisodiya 		kfree(data);
4074a9bb5b4SMukesh Sisodiya 		return ret;
4084a9bb5b4SMukesh Sisodiya 	}
4094a9bb5b4SMukesh Sisodiya 
4104a9bb5b4SMukesh Sisodiya 	kfree(data);
4114a9bb5b4SMukesh Sisodiya 	return 0;
4124a9bb5b4SMukesh Sisodiya }
4134a9bb5b4SMukesh Sisodiya IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table);
414427661e4SMiri Korenblit 
iwl_uefi_set_sar_profile(struct iwl_fw_runtime * fwrt,struct uefi_sar_profile * uefi_sar_prof,u8 prof_index,bool enabled)415427661e4SMiri Korenblit static void iwl_uefi_set_sar_profile(struct iwl_fw_runtime *fwrt,
416427661e4SMiri Korenblit 				     struct uefi_sar_profile *uefi_sar_prof,
417427661e4SMiri Korenblit 				     u8 prof_index, bool enabled)
418427661e4SMiri Korenblit {
419427661e4SMiri Korenblit 	memcpy(&fwrt->sar_profiles[prof_index].chains, uefi_sar_prof,
420427661e4SMiri Korenblit 	       sizeof(struct uefi_sar_profile));
421427661e4SMiri Korenblit 
422427661e4SMiri Korenblit 	fwrt->sar_profiles[prof_index].enabled = enabled & IWL_SAR_ENABLE_MSK;
423427661e4SMiri Korenblit }
424427661e4SMiri Korenblit 
iwl_uefi_get_wrds_table(struct iwl_fw_runtime * fwrt)425427661e4SMiri Korenblit int iwl_uefi_get_wrds_table(struct iwl_fw_runtime *fwrt)
426427661e4SMiri Korenblit {
427427661e4SMiri Korenblit 	struct uefi_cnv_var_wrds *data;
428427661e4SMiri Korenblit 	int ret = 0;
429427661e4SMiri Korenblit 
430427661e4SMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDS_NAME,
431427661e4SMiri Korenblit 					      "WRDS", sizeof(*data), NULL);
432427661e4SMiri Korenblit 	if (IS_ERR(data))
433427661e4SMiri Korenblit 		return -EINVAL;
434427661e4SMiri Korenblit 
435427661e4SMiri Korenblit 	if (data->revision != IWL_UEFI_WRDS_REVISION) {
436427661e4SMiri Korenblit 		ret = -EINVAL;
437427661e4SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDS revision:%d\n",
438427661e4SMiri Korenblit 				data->revision);
439427661e4SMiri Korenblit 		goto out;
440427661e4SMiri Korenblit 	}
441427661e4SMiri Korenblit 
442427661e4SMiri Korenblit 	/* The profile from WRDS is officially profile 1, but goes
443427661e4SMiri Korenblit 	 * into sar_profiles[0] (because we don't have a profile 0).
444427661e4SMiri Korenblit 	 */
445427661e4SMiri Korenblit 	iwl_uefi_set_sar_profile(fwrt, &data->sar_profile, 0, data->mode);
446427661e4SMiri Korenblit out:
447427661e4SMiri Korenblit 	kfree(data);
448427661e4SMiri Korenblit 	return ret;
449427661e4SMiri Korenblit }
450427661e4SMiri Korenblit 
iwl_uefi_get_ewrd_table(struct iwl_fw_runtime * fwrt)451427661e4SMiri Korenblit int iwl_uefi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
452427661e4SMiri Korenblit {
453427661e4SMiri Korenblit 	struct uefi_cnv_var_ewrd *data;
454427661e4SMiri Korenblit 	int i, ret = 0;
455427661e4SMiri Korenblit 
456427661e4SMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_EWRD_NAME,
457427661e4SMiri Korenblit 					      "EWRD", sizeof(*data), NULL);
458427661e4SMiri Korenblit 	if (IS_ERR(data))
459427661e4SMiri Korenblit 		return -EINVAL;
460427661e4SMiri Korenblit 
461427661e4SMiri Korenblit 	if (data->revision != IWL_UEFI_EWRD_REVISION) {
462427661e4SMiri Korenblit 		ret = -EINVAL;
463427661e4SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI EWRD revision:%d\n",
464427661e4SMiri Korenblit 				data->revision);
465427661e4SMiri Korenblit 		goto out;
466427661e4SMiri Korenblit 	}
467427661e4SMiri Korenblit 
468427661e4SMiri Korenblit 	if (data->num_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
469427661e4SMiri Korenblit 		ret = -EINVAL;
470427661e4SMiri Korenblit 		goto out;
471427661e4SMiri Korenblit 	}
472427661e4SMiri Korenblit 
473427661e4SMiri Korenblit 	for (i = 0; i < data->num_profiles; i++)
474427661e4SMiri Korenblit 		/* The EWRD profiles officially go from 2 to 4, but we
475427661e4SMiri Korenblit 		 * save them in sar_profiles[1-3] (because we don't
476427661e4SMiri Korenblit 		 * have profile 0).  So in the array we start from 1.
477427661e4SMiri Korenblit 		 */
478427661e4SMiri Korenblit 		iwl_uefi_set_sar_profile(fwrt, &data->sar_profiles[i], i + 1,
479427661e4SMiri Korenblit 					 data->mode);
480427661e4SMiri Korenblit 
481427661e4SMiri Korenblit out:
482427661e4SMiri Korenblit 	kfree(data);
483427661e4SMiri Korenblit 	return ret;
484427661e4SMiri Korenblit }
485427661e4SMiri Korenblit 
iwl_uefi_get_wgds_table(struct iwl_fw_runtime * fwrt)486427661e4SMiri Korenblit int iwl_uefi_get_wgds_table(struct iwl_fw_runtime *fwrt)
487427661e4SMiri Korenblit {
488427661e4SMiri Korenblit 	struct uefi_cnv_var_wgds *data;
489427661e4SMiri Korenblit 	int i, ret = 0;
490427661e4SMiri Korenblit 
491427661e4SMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WGDS_NAME,
492427661e4SMiri Korenblit 					      "WGDS", sizeof(*data), NULL);
493427661e4SMiri Korenblit 	if (IS_ERR(data))
494427661e4SMiri Korenblit 		return -EINVAL;
495427661e4SMiri Korenblit 
496427661e4SMiri Korenblit 	if (data->revision != IWL_UEFI_WGDS_REVISION) {
497427661e4SMiri Korenblit 		ret = -EINVAL;
498427661e4SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WGDS revision:%d\n",
499427661e4SMiri Korenblit 				data->revision);
500427661e4SMiri Korenblit 		goto out;
501427661e4SMiri Korenblit 	}
502427661e4SMiri Korenblit 
503427661e4SMiri Korenblit 	if (data->num_profiles < BIOS_GEO_MIN_PROFILE_NUM ||
504427661e4SMiri Korenblit 	    data->num_profiles > BIOS_GEO_MAX_PROFILE_NUM) {
505427661e4SMiri Korenblit 		ret = -EINVAL;
506427661e4SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Invalid number of profiles in WGDS: %d\n",
507427661e4SMiri Korenblit 				data->num_profiles);
508427661e4SMiri Korenblit 		goto out;
509427661e4SMiri Korenblit 	}
510427661e4SMiri Korenblit 
511427661e4SMiri Korenblit 	fwrt->geo_rev = data->revision;
512427661e4SMiri Korenblit 	for (i = 0; i < data->num_profiles; i++)
513427661e4SMiri Korenblit 		memcpy(&fwrt->geo_profiles[i], &data->geo_profiles[i],
514427661e4SMiri Korenblit 		       sizeof(struct iwl_geo_profile));
515427661e4SMiri Korenblit 
516427661e4SMiri Korenblit 	fwrt->geo_num_profiles = data->num_profiles;
517427661e4SMiri Korenblit 	fwrt->geo_enabled = true;
518427661e4SMiri Korenblit out:
519427661e4SMiri Korenblit 	kfree(data);
520427661e4SMiri Korenblit 	return ret;
521427661e4SMiri Korenblit }
522bc8d0a45SMiri Korenblit 
iwl_uefi_get_ppag_table(struct iwl_fw_runtime * fwrt)523bc8d0a45SMiri Korenblit int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt)
524bc8d0a45SMiri Korenblit {
525bc8d0a45SMiri Korenblit 	struct uefi_cnv_var_ppag *data;
526bc8d0a45SMiri Korenblit 	int ret = 0;
527bc8d0a45SMiri Korenblit 
528bc8d0a45SMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_PPAG_NAME,
529bc8d0a45SMiri Korenblit 					      "PPAG", sizeof(*data), NULL);
530bc8d0a45SMiri Korenblit 	if (IS_ERR(data))
531bc8d0a45SMiri Korenblit 		return -EINVAL;
532bc8d0a45SMiri Korenblit 
533bc8d0a45SMiri Korenblit 	if (data->revision < IWL_UEFI_MIN_PPAG_REV ||
534bc8d0a45SMiri Korenblit 	    data->revision > IWL_UEFI_MAX_PPAG_REV) {
535bc8d0a45SMiri Korenblit 		ret = -EINVAL;
536bc8d0a45SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PPAG revision:%d\n",
537bc8d0a45SMiri Korenblit 				data->revision);
538bc8d0a45SMiri Korenblit 		goto out;
539bc8d0a45SMiri Korenblit 	}
540bc8d0a45SMiri Korenblit 
541bc8d0a45SMiri Korenblit 	fwrt->ppag_ver = data->revision;
5423d801a75SAnjaneyulu 	fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes,
5433d801a75SAnjaneyulu 						   fwrt->ppag_ver);
544bc8d0a45SMiri Korenblit 
545bc8d0a45SMiri Korenblit 	BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains));
546bc8d0a45SMiri Korenblit 	memcpy(&fwrt->ppag_chains, &data->ppag_chains,
547bc8d0a45SMiri Korenblit 	       sizeof(data->ppag_chains));
548bc8d0a45SMiri Korenblit out:
549bc8d0a45SMiri Korenblit 	kfree(data);
550bc8d0a45SMiri Korenblit 	return ret;
551bc8d0a45SMiri Korenblit }
552084e0452SMiri Korenblit 
iwl_uefi_get_tas_table(struct iwl_fw_runtime * fwrt,struct iwl_tas_data * tas_data)553084e0452SMiri Korenblit int iwl_uefi_get_tas_table(struct iwl_fw_runtime *fwrt,
554084e0452SMiri Korenblit 			   struct iwl_tas_data *tas_data)
555084e0452SMiri Korenblit {
556084e0452SMiri Korenblit 	struct uefi_cnv_var_wtas *uefi_tas;
557084e0452SMiri Korenblit 	int ret = 0, enabled, i;
558084e0452SMiri Korenblit 
559084e0452SMiri Korenblit 	uefi_tas = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WTAS_NAME,
560084e0452SMiri Korenblit 						  "WTAS", sizeof(*uefi_tas), NULL);
561084e0452SMiri Korenblit 	if (IS_ERR(uefi_tas))
562084e0452SMiri Korenblit 		return -EINVAL;
563084e0452SMiri Korenblit 
564084e0452SMiri Korenblit 	if (uefi_tas->revision != IWL_UEFI_WTAS_REVISION) {
565084e0452SMiri Korenblit 		ret = -EINVAL;
566084e0452SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WTAS revision:%d\n",
567084e0452SMiri Korenblit 				uefi_tas->revision);
568084e0452SMiri Korenblit 		goto out;
569084e0452SMiri Korenblit 	}
570084e0452SMiri Korenblit 
571084e0452SMiri Korenblit 	enabled = iwl_parse_tas_selection(fwrt, tas_data,
572084e0452SMiri Korenblit 					  uefi_tas->tas_selection);
573084e0452SMiri Korenblit 	if (!enabled) {
574084e0452SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");
575084e0452SMiri Korenblit 		ret = 0;
576084e0452SMiri Korenblit 		goto out;
577084e0452SMiri Korenblit 	}
578084e0452SMiri Korenblit 
579084e0452SMiri Korenblit 	IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n",
580084e0452SMiri Korenblit 			uefi_tas->revision);
581084e0452SMiri Korenblit 	if (uefi_tas->black_list_size > IWL_WTAS_BLACK_LIST_MAX) {
582084e0452SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %d\n",
583084e0452SMiri Korenblit 				uefi_tas->black_list_size);
584084e0452SMiri Korenblit 		ret = -EINVAL;
585084e0452SMiri Korenblit 		goto out;
586084e0452SMiri Korenblit 	}
587084e0452SMiri Korenblit 	tas_data->block_list_size = cpu_to_le32(uefi_tas->black_list_size);
588084e0452SMiri Korenblit 	IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", uefi_tas->black_list_size);
589084e0452SMiri Korenblit 
590084e0452SMiri Korenblit 	for (i = 0; i < uefi_tas->black_list_size; i++) {
591084e0452SMiri Korenblit 		tas_data->block_list_array[i] =
592084e0452SMiri Korenblit 			cpu_to_le32(uefi_tas->black_list[i]);
593084e0452SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n",
594084e0452SMiri Korenblit 				uefi_tas->black_list[i]);
595084e0452SMiri Korenblit 	}
596084e0452SMiri Korenblit out:
597084e0452SMiri Korenblit 	kfree(uefi_tas);
598084e0452SMiri Korenblit 	return ret;
599084e0452SMiri Korenblit }
60018f52365SMiri Korenblit 
iwl_uefi_get_pwr_limit(struct iwl_fw_runtime * fwrt,u64 * dflt_pwr_limit)60118f52365SMiri Korenblit int iwl_uefi_get_pwr_limit(struct iwl_fw_runtime *fwrt,
60218f52365SMiri Korenblit 			   u64 *dflt_pwr_limit)
60318f52365SMiri Korenblit {
60418f52365SMiri Korenblit 	struct uefi_cnv_var_splc *data;
60518f52365SMiri Korenblit 	int ret = 0;
60618f52365SMiri Korenblit 
60718f52365SMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_SPLC_NAME,
60818f52365SMiri Korenblit 					      "SPLC", sizeof(*data), NULL);
60918f52365SMiri Korenblit 	if (IS_ERR(data))
61018f52365SMiri Korenblit 		return -EINVAL;
61118f52365SMiri Korenblit 
61218f52365SMiri Korenblit 	if (data->revision != IWL_UEFI_SPLC_REVISION) {
61318f52365SMiri Korenblit 		ret = -EINVAL;
61418f52365SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI SPLC revision:%d\n",
61518f52365SMiri Korenblit 				data->revision);
61618f52365SMiri Korenblit 		goto out;
61718f52365SMiri Korenblit 	}
61818f52365SMiri Korenblit 	*dflt_pwr_limit = data->default_pwr_limit;
61918f52365SMiri Korenblit out:
62018f52365SMiri Korenblit 	kfree(data);
62118f52365SMiri Korenblit 	return ret;
62218f52365SMiri Korenblit }
623669761e8SMiri Korenblit 
iwl_uefi_get_mcc(struct iwl_fw_runtime * fwrt,char * mcc)624669761e8SMiri Korenblit int iwl_uefi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)
625669761e8SMiri Korenblit {
626669761e8SMiri Korenblit 	struct uefi_cnv_var_wrdd *data;
627669761e8SMiri Korenblit 	int ret = 0;
628669761e8SMiri Korenblit 
629669761e8SMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WRDD_NAME,
630669761e8SMiri Korenblit 					      "WRDD", sizeof(*data), NULL);
631669761e8SMiri Korenblit 	if (IS_ERR(data))
632669761e8SMiri Korenblit 		return -EINVAL;
633669761e8SMiri Korenblit 
634669761e8SMiri Korenblit 	if (data->revision != IWL_UEFI_WRDD_REVISION) {
635669761e8SMiri Korenblit 		ret = -EINVAL;
636669761e8SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n",
637669761e8SMiri Korenblit 				data->revision);
638669761e8SMiri Korenblit 		goto out;
639669761e8SMiri Korenblit 	}
640669761e8SMiri Korenblit 
641*ff5aabe7SAnjaneyulu 	if (data->mcc != BIOS_MCC_CHINA) {
642669761e8SMiri Korenblit 		ret = -EINVAL;
643669761e8SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "UEFI WRDD is supported only for CN\n");
644669761e8SMiri Korenblit 		goto out;
645669761e8SMiri Korenblit 	}
646669761e8SMiri Korenblit 
647669761e8SMiri Korenblit 	mcc[0] = (data->mcc >> 8) & 0xff;
648669761e8SMiri Korenblit 	mcc[1] = data->mcc & 0xff;
649669761e8SMiri Korenblit 	mcc[2] = '\0';
650669761e8SMiri Korenblit out:
651669761e8SMiri Korenblit 	kfree(data);
652669761e8SMiri Korenblit 	return ret;
653669761e8SMiri Korenblit }
65420935f3eSMiri Korenblit 
iwl_uefi_get_eckv(struct iwl_fw_runtime * fwrt,u32 * extl_clk)65520935f3eSMiri Korenblit int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)
65620935f3eSMiri Korenblit {
65720935f3eSMiri Korenblit 	struct uefi_cnv_var_eckv *data;
65820935f3eSMiri Korenblit 	int ret = 0;
65920935f3eSMiri Korenblit 
66020935f3eSMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME,
66120935f3eSMiri Korenblit 					      "ECKV", sizeof(*data), NULL);
66220935f3eSMiri Korenblit 	if (IS_ERR(data))
66320935f3eSMiri Korenblit 		return -EINVAL;
66420935f3eSMiri Korenblit 
66520935f3eSMiri Korenblit 	if (data->revision != IWL_UEFI_ECKV_REVISION) {
66620935f3eSMiri Korenblit 		ret = -EINVAL;
66720935f3eSMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n",
66820935f3eSMiri Korenblit 				data->revision);
66920935f3eSMiri Korenblit 		goto out;
67020935f3eSMiri Korenblit 	}
67120935f3eSMiri Korenblit 	*extl_clk = data->ext_clock_valid;
67220935f3eSMiri Korenblit out:
67320935f3eSMiri Korenblit 	kfree(data);
67420935f3eSMiri Korenblit 	return ret;
67520935f3eSMiri Korenblit }
676fc7214c3SMiri Korenblit 
iwl_uefi_get_wbem(struct iwl_fw_runtime * fwrt,u32 * value)677332ff432SAnjaneyulu int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)
678332ff432SAnjaneyulu {
679332ff432SAnjaneyulu 	struct uefi_cnv_wlan_wbem_data *data;
680332ff432SAnjaneyulu 	int ret = 0;
681332ff432SAnjaneyulu 
682332ff432SAnjaneyulu 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WBEM_NAME,
683332ff432SAnjaneyulu 					      "WBEM", sizeof(*data), NULL);
684332ff432SAnjaneyulu 	if (IS_ERR(data))
685332ff432SAnjaneyulu 		return -EINVAL;
686332ff432SAnjaneyulu 
687332ff432SAnjaneyulu 	if (data->revision != IWL_UEFI_WBEM_REVISION) {
688332ff432SAnjaneyulu 		ret = -EINVAL;
689332ff432SAnjaneyulu 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WBEM revision:%d\n",
690332ff432SAnjaneyulu 				data->revision);
691332ff432SAnjaneyulu 		goto out;
692332ff432SAnjaneyulu 	}
693332ff432SAnjaneyulu 	*value = data->wbem_320mhz_per_mcc & IWL_UEFI_WBEM_REV0_MASK;
694332ff432SAnjaneyulu 	IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from UEFI\n");
695332ff432SAnjaneyulu out:
696332ff432SAnjaneyulu 	kfree(data);
697332ff432SAnjaneyulu 	return ret;
698332ff432SAnjaneyulu }
699332ff432SAnjaneyulu 
iwl_uefi_get_dsm(struct iwl_fw_runtime * fwrt,enum iwl_dsm_funcs func,u32 * value)700fc7214c3SMiri Korenblit int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func,
701fc7214c3SMiri Korenblit 		     u32 *value)
702fc7214c3SMiri Korenblit {
703fc7214c3SMiri Korenblit 	struct uefi_cnv_var_general_cfg *data;
704f29a8be8SDan Carpenter 	int ret = -EINVAL;
705fc7214c3SMiri Korenblit 
706fc7214c3SMiri Korenblit 	/* Not supported function index */
707fc7214c3SMiri Korenblit 	if (func >= DSM_FUNC_NUM_FUNCS || func == 5)
708fc7214c3SMiri Korenblit 		return -EOPNOTSUPP;
709fc7214c3SMiri Korenblit 
710fc7214c3SMiri Korenblit 	data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_DSM_NAME,
711fc7214c3SMiri Korenblit 					      "DSM", sizeof(*data), NULL);
712fc7214c3SMiri Korenblit 	if (IS_ERR(data))
713fc7214c3SMiri Korenblit 		return -EINVAL;
714fc7214c3SMiri Korenblit 
715fc7214c3SMiri Korenblit 	if (data->revision != IWL_UEFI_DSM_REVISION) {
716fc7214c3SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI DSM revision:%d\n",
717fc7214c3SMiri Korenblit 				data->revision);
718fc7214c3SMiri Korenblit 		goto out;
719fc7214c3SMiri Korenblit 	}
720fc7214c3SMiri Korenblit 
721fc7214c3SMiri Korenblit 	if (ARRAY_SIZE(data->functions) != UEFI_MAX_DSM_FUNCS) {
722fc7214c3SMiri Korenblit 		IWL_DEBUG_RADIO(fwrt, "Invalid size of DSM functions array\n");
723fc7214c3SMiri Korenblit 		goto out;
724fc7214c3SMiri Korenblit 	}
725fc7214c3SMiri Korenblit 
726fc7214c3SMiri Korenblit 	*value = data->functions[func];
727fc7214c3SMiri Korenblit 	ret = 0;
728fc7214c3SMiri Korenblit out:
729fc7214c3SMiri Korenblit 	kfree(data);
730fc7214c3SMiri Korenblit 	return ret;
731fc7214c3SMiri Korenblit }
732b312e357SSomashekhar(Som) 
iwl_uefi_get_puncturing(struct iwl_fw_runtime * fwrt)733b312e357SSomashekhar(Som) int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt)
734b312e357SSomashekhar(Som) {
735b312e357SSomashekhar(Som) 	struct uefi_cnv_var_puncturing_data *data;
736b312e357SSomashekhar(Som) 	/* default value is not enabled if there is any issue in reading
737b312e357SSomashekhar(Som) 	 * uefi variable or revision is not supported
738b312e357SSomashekhar(Som) 	 */
739b312e357SSomashekhar(Som) 	int puncturing = 0;
740b312e357SSomashekhar(Som) 
741b312e357SSomashekhar(Som) 	data = iwl_uefi_get_verified_variable(fwrt->trans,
742b312e357SSomashekhar(Som) 					      IWL_UEFI_PUNCTURING_NAME,
743b312e357SSomashekhar(Som) 					      "UefiCnvWlanPuncturing",
744b312e357SSomashekhar(Som) 					      sizeof(*data), NULL);
745b312e357SSomashekhar(Som) 	if (IS_ERR(data))
746b312e357SSomashekhar(Som) 		return puncturing;
747b312e357SSomashekhar(Som) 
748b312e357SSomashekhar(Som) 	if (data->revision != IWL_UEFI_PUNCTURING_REVISION) {
749b312e357SSomashekhar(Som) 		IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI PUNCTURING rev:%d\n",
750b312e357SSomashekhar(Som) 				data->revision);
751b312e357SSomashekhar(Som) 	} else {
752b312e357SSomashekhar(Som) 		puncturing = data->puncturing & IWL_UEFI_PUNCTURING_REV0_MASK;
753b312e357SSomashekhar(Som) 		IWL_DEBUG_RADIO(fwrt, "Loaded puncturing bits from UEFI: %d\n",
754b312e357SSomashekhar(Som) 				puncturing);
755b312e357SSomashekhar(Som) 	}
756b312e357SSomashekhar(Som) 
757b312e357SSomashekhar(Som) 	kfree(data);
758b312e357SSomashekhar(Som) 	return puncturing;
759b312e357SSomashekhar(Som) }
760b312e357SSomashekhar(Som) IWL_EXPORT_SYMBOL(iwl_uefi_get_puncturing);
761