184c3c995SLuca Coelho // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
284c3c995SLuca Coelho /*
3380bf72dSAlon Giladi  * Copyright(c) 2021-2023 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 
20*372a7148SGregory Greenman struct iwl_uefi_pnvm_mem_desc {
21*372a7148SGregory Greenman 	__le32 addr;
22*372a7148SGregory Greenman 	__le32 size;
23*372a7148SGregory Greenman 	const u8 data[];
24*372a7148SGregory Greenman } __packed;
25*372a7148SGregory 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 
iwl_uefi_handle_tlv_mem_desc(struct iwl_trans * trans,const u8 * data,u32 tlv_len,struct iwl_pnvm_image * pnvm_data)79*372a7148SGregory Greenman int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data,
80*372a7148SGregory Greenman 				 u32 tlv_len, struct iwl_pnvm_image *pnvm_data)
81*372a7148SGregory Greenman {
82*372a7148SGregory Greenman 	const struct iwl_uefi_pnvm_mem_desc *desc = (const void *)data;
83*372a7148SGregory Greenman 	u32 data_len;
84*372a7148SGregory Greenman 
85*372a7148SGregory Greenman 	if (tlv_len < sizeof(*desc)) {
86*372a7148SGregory Greenman 		IWL_DEBUG_FW(trans, "TLV len (%d) is too small\n", tlv_len);
87*372a7148SGregory Greenman 		return -EINVAL;
88*372a7148SGregory Greenman 	}
89*372a7148SGregory Greenman 
90*372a7148SGregory Greenman 	data_len = tlv_len - sizeof(*desc);
91*372a7148SGregory Greenman 
92*372a7148SGregory Greenman 	IWL_DEBUG_FW(trans,
93*372a7148SGregory Greenman 		     "Handle IWL_UCODE_TLV_MEM_DESC, len %d data_len %d\n",
94*372a7148SGregory Greenman 		     tlv_len, data_len);
95*372a7148SGregory Greenman 
96*372a7148SGregory Greenman 	if (le32_to_cpu(desc->size) != data_len) {
97*372a7148SGregory Greenman 		IWL_DEBUG_FW(trans, "invalid mem desc size %d\n", desc->size);
98*372a7148SGregory Greenman 		return -EINVAL;
99*372a7148SGregory Greenman 	}
100*372a7148SGregory Greenman 
101*372a7148SGregory Greenman 	if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) {
102*372a7148SGregory Greenman 		IWL_DEBUG_FW(trans, "too many payloads to allocate in DRAM.\n");
103*372a7148SGregory Greenman 		return -EINVAL;
104*372a7148SGregory Greenman 	}
105*372a7148SGregory Greenman 
106*372a7148SGregory Greenman 	IWL_DEBUG_FW(trans, "Adding data (size %d)\n", data_len);
107*372a7148SGregory Greenman 
108*372a7148SGregory Greenman 	pnvm_data->chunks[pnvm_data->n_chunks].data = desc->data;
109*372a7148SGregory Greenman 	pnvm_data->chunks[pnvm_data->n_chunks].len = data_len;
110*372a7148SGregory Greenman 	pnvm_data->n_chunks++;
111*372a7148SGregory Greenman 
112*372a7148SGregory Greenman 	return 0;
113*372a7148SGregory Greenman }
114*372a7148SGregory Greenman 
iwl_uefi_reduce_power_section(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data)115ea3571f4SAlon Giladi static int iwl_uefi_reduce_power_section(struct iwl_trans *trans,
116ea3571f4SAlon Giladi 					 const u8 *data, size_t len,
117ea3571f4SAlon Giladi 					 struct iwl_pnvm_image *pnvm_data)
1189dad325fSLuca Coelho {
11986e8e657SJohannes Berg 	const struct iwl_ucode_tlv *tlv;
1209dad325fSLuca Coelho 
1219dad325fSLuca Coelho 	IWL_DEBUG_FW(trans, "Handling REDUCE_POWER section\n");
122ea3571f4SAlon Giladi 	memset(pnvm_data, 0, sizeof(*pnvm_data));
1239dad325fSLuca Coelho 
1249dad325fSLuca Coelho 	while (len >= sizeof(*tlv)) {
1259dad325fSLuca Coelho 		u32 tlv_len, tlv_type;
1269dad325fSLuca Coelho 
1279dad325fSLuca Coelho 		len -= sizeof(*tlv);
12886e8e657SJohannes Berg 		tlv = (const void *)data;
1299dad325fSLuca Coelho 
1309dad325fSLuca Coelho 		tlv_len = le32_to_cpu(tlv->length);
1319dad325fSLuca Coelho 		tlv_type = le32_to_cpu(tlv->type);
1329dad325fSLuca Coelho 
1339dad325fSLuca Coelho 		if (len < tlv_len) {
1349dad325fSLuca Coelho 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
1359dad325fSLuca Coelho 				len, tlv_len);
136ea3571f4SAlon Giladi 			return -EINVAL;
1379dad325fSLuca Coelho 		}
1389dad325fSLuca Coelho 
1399dad325fSLuca Coelho 		data += sizeof(*tlv);
1409dad325fSLuca Coelho 
1419dad325fSLuca Coelho 		switch (tlv_type) {
142*372a7148SGregory Greenman 		case IWL_UCODE_TLV_MEM_DESC:
143*372a7148SGregory Greenman 			if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len,
144*372a7148SGregory Greenman 							 pnvm_data))
145ea3571f4SAlon Giladi 				return -EINVAL;
1469dad325fSLuca Coelho 			break;
1479dad325fSLuca Coelho 		case IWL_UCODE_TLV_PNVM_SKU:
1489dad325fSLuca Coelho 			IWL_DEBUG_FW(trans,
1499dad325fSLuca Coelho 				     "New REDUCE_POWER section started, stop parsing.\n");
1509dad325fSLuca Coelho 			goto done;
1519dad325fSLuca Coelho 		default:
1529dad325fSLuca Coelho 			IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n",
1539dad325fSLuca Coelho 				     tlv_type, tlv_len);
1549dad325fSLuca Coelho 			break;
1559dad325fSLuca Coelho 		}
1569dad325fSLuca Coelho 
1579dad325fSLuca Coelho 		len -= ALIGN(tlv_len, 4);
1589dad325fSLuca Coelho 		data += ALIGN(tlv_len, 4);
1599dad325fSLuca Coelho 	}
1609dad325fSLuca Coelho 
1619dad325fSLuca Coelho done:
162ea3571f4SAlon Giladi 	if (!pnvm_data->n_chunks) {
1639dad325fSLuca Coelho 		IWL_DEBUG_FW(trans, "Empty REDUCE_POWER, skipping.\n");
164ea3571f4SAlon Giladi 		return -ENOENT;
165ea3571f4SAlon Giladi 	}
166ea3571f4SAlon Giladi 	return 0;
1679dad325fSLuca Coelho }
1689dad325fSLuca Coelho 
iwl_uefi_reduce_power_parse(struct iwl_trans * trans,const u8 * data,size_t len,struct iwl_pnvm_image * pnvm_data)169380bf72dSAlon Giladi int iwl_uefi_reduce_power_parse(struct iwl_trans *trans,
170ea3571f4SAlon Giladi 				const u8 *data, size_t len,
171ea3571f4SAlon Giladi 				struct iwl_pnvm_image *pnvm_data)
1729dad325fSLuca Coelho {
17386e8e657SJohannes Berg 	const struct iwl_ucode_tlv *tlv;
1749dad325fSLuca Coelho 
1759dad325fSLuca Coelho 	IWL_DEBUG_FW(trans, "Parsing REDUCE_POWER data\n");
1769dad325fSLuca Coelho 
1779dad325fSLuca Coelho 	while (len >= sizeof(*tlv)) {
1789dad325fSLuca Coelho 		u32 tlv_len, tlv_type;
1799dad325fSLuca Coelho 
1809dad325fSLuca Coelho 		len -= sizeof(*tlv);
18186e8e657SJohannes Berg 		tlv = (const void *)data;
1829dad325fSLuca Coelho 
1839dad325fSLuca Coelho 		tlv_len = le32_to_cpu(tlv->length);
1849dad325fSLuca Coelho 		tlv_type = le32_to_cpu(tlv->type);
1859dad325fSLuca Coelho 
1869dad325fSLuca Coelho 		if (len < tlv_len) {
1879dad325fSLuca Coelho 			IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
1889dad325fSLuca Coelho 				len, tlv_len);
189ea3571f4SAlon Giladi 			return -EINVAL;
1909dad325fSLuca Coelho 		}
1919dad325fSLuca Coelho 
1929dad325fSLuca Coelho 		if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) {
19386e8e657SJohannes Berg 			const struct iwl_sku_id *sku_id =
19486e8e657SJohannes Berg 				(const void *)(data + sizeof(*tlv));
1959dad325fSLuca Coelho 
1969dad325fSLuca Coelho 			IWL_DEBUG_FW(trans,
1979dad325fSLuca Coelho 				     "Got IWL_UCODE_TLV_PNVM_SKU len %d\n",
1989dad325fSLuca Coelho 				     tlv_len);
1999dad325fSLuca Coelho 			IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n",
2009dad325fSLuca Coelho 				     le32_to_cpu(sku_id->data[0]),
2019dad325fSLuca Coelho 				     le32_to_cpu(sku_id->data[1]),
2029dad325fSLuca Coelho 				     le32_to_cpu(sku_id->data[2]));
2039dad325fSLuca Coelho 
2049dad325fSLuca Coelho 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
2059dad325fSLuca Coelho 			len -= ALIGN(tlv_len, 4);
2069dad325fSLuca Coelho 
2079dad325fSLuca Coelho 			if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) &&
2089dad325fSLuca Coelho 			    trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) &&
2099dad325fSLuca Coelho 			    trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) {
210ea3571f4SAlon Giladi 				int ret = iwl_uefi_reduce_power_section(trans,
211ea3571f4SAlon Giladi 								    data, len,
212ea3571f4SAlon Giladi 								    pnvm_data);
213ea3571f4SAlon Giladi 				if (!ret)
214ea3571f4SAlon Giladi 					return 0;
2159dad325fSLuca Coelho 			} else {
2169dad325fSLuca Coelho 				IWL_DEBUG_FW(trans, "SKU ID didn't match!\n");
2179dad325fSLuca Coelho 			}
2189dad325fSLuca Coelho 		} else {
2199dad325fSLuca Coelho 			data += sizeof(*tlv) + ALIGN(tlv_len, 4);
2209dad325fSLuca Coelho 			len -= ALIGN(tlv_len, 4);
2219dad325fSLuca Coelho 		}
2229dad325fSLuca Coelho 	}
2239dad325fSLuca Coelho 
224ea3571f4SAlon Giladi 	return -ENOENT;
2259dad325fSLuca Coelho }
2269dad325fSLuca Coelho 
iwl_uefi_get_reduced_power(struct iwl_trans * trans,size_t * len)227380bf72dSAlon Giladi u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len)
2289dad325fSLuca Coelho {
2299dad325fSLuca Coelho 	struct pnvm_sku_package *package;
2309dad325fSLuca Coelho 	unsigned long package_size;
231380bf72dSAlon Giladi 	u8 *data;
2329dad325fSLuca Coelho 
2338ae3e231SGregory Greenman 	package = iwl_uefi_get_variable(IWL_UEFI_REDUCED_POWER_NAME,
2348ae3e231SGregory Greenman 					&IWL_EFI_VAR_GUID, &package_size);
2359dad325fSLuca Coelho 
2368ae3e231SGregory Greenman 	if (IS_ERR(package)) {
2379dad325fSLuca Coelho 		IWL_DEBUG_FW(trans,
2380c4bad7fSArd Biesheuvel 			     "Reduced Power UEFI variable not found 0x%lx (len %lu)\n",
2398ae3e231SGregory Greenman 			     PTR_ERR(package), package_size);
2408ae3e231SGregory Greenman 		return ERR_CAST(package);
2418ae3e231SGregory Greenman 	}
2428ae3e231SGregory Greenman 
2438ae3e231SGregory Greenman 	if (package_size < sizeof(*package)) {
2448ae3e231SGregory Greenman 		IWL_DEBUG_FW(trans,
2458ae3e231SGregory Greenman 			     "Invalid Reduced Power UEFI variable len (%lu)\n",
2468ae3e231SGregory Greenman 			     package_size);
2479dad325fSLuca Coelho 		kfree(package);
2488ae3e231SGregory Greenman 		return ERR_PTR(-EINVAL);
2499dad325fSLuca Coelho 	}
2509dad325fSLuca Coelho 
2519dad325fSLuca Coelho 	IWL_DEBUG_FW(trans, "Read reduced power from UEFI with size %lu\n",
2529dad325fSLuca Coelho 		     package_size);
2539dad325fSLuca Coelho 
2549dad325fSLuca Coelho 	IWL_DEBUG_FW(trans, "rev %d, total_size %d, n_skus %d\n",
2559dad325fSLuca Coelho 		     package->rev, package->total_size, package->n_skus);
2569dad325fSLuca Coelho 
257380bf72dSAlon Giladi 	*len = package_size - sizeof(*package);
258380bf72dSAlon Giladi 	data = kmemdup(package->data, *len, GFP_KERNEL);
2598ae3e231SGregory Greenman 	if (!data) {
2608ae3e231SGregory Greenman 		kfree(package);
261380bf72dSAlon Giladi 		return ERR_PTR(-ENOMEM);
2628ae3e231SGregory Greenman 	}
2638ae3e231SGregory Greenman 
2649dad325fSLuca Coelho 	kfree(package);
2659dad325fSLuca Coelho 
266380bf72dSAlon Giladi 	return data;
2679dad325fSLuca Coelho }
268c593d2faSAyala Barazani 
iwl_uefi_step_parse(struct uefi_cnv_common_step_data * common_step_data,struct iwl_trans * trans)26909b4c35dSAyala Barazani static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_data,
27009b4c35dSAyala Barazani 			       struct iwl_trans *trans)
27109b4c35dSAyala Barazani {
27209b4c35dSAyala Barazani 	if (common_step_data->revision != 1)
27309b4c35dSAyala Barazani 		return -EINVAL;
27409b4c35dSAyala Barazani 
27509b4c35dSAyala Barazani 	trans->mbx_addr_0_step = (u32)common_step_data->revision |
27609b4c35dSAyala Barazani 		(u32)common_step_data->cnvi_eq_channel << 8 |
27709b4c35dSAyala Barazani 		(u32)common_step_data->cnvr_eq_channel << 16 |
27809b4c35dSAyala Barazani 		(u32)common_step_data->radio1 << 24;
27909b4c35dSAyala Barazani 	trans->mbx_addr_1_step = (u32)common_step_data->radio2;
28009b4c35dSAyala Barazani 	return 0;
28109b4c35dSAyala Barazani }
28209b4c35dSAyala Barazani 
iwl_uefi_get_step_table(struct iwl_trans * trans)28309b4c35dSAyala Barazani void iwl_uefi_get_step_table(struct iwl_trans *trans)
28409b4c35dSAyala Barazani {
28509b4c35dSAyala Barazani 	struct uefi_cnv_common_step_data *data;
28609b4c35dSAyala Barazani 	unsigned long package_size;
28709b4c35dSAyala Barazani 	int ret;
28809b4c35dSAyala Barazani 
28909b4c35dSAyala Barazani 	if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210)
29009b4c35dSAyala Barazani 		return;
29109b4c35dSAyala Barazani 
2928ae3e231SGregory Greenman 	data = iwl_uefi_get_variable(IWL_UEFI_STEP_NAME, &IWL_EFI_VAR_GUID,
2938ae3e231SGregory Greenman 				     &package_size);
29409b4c35dSAyala Barazani 
2958ae3e231SGregory Greenman 	if (IS_ERR(data)) {
29609b4c35dSAyala Barazani 		IWL_DEBUG_FW(trans,
2978ae3e231SGregory Greenman 			     "STEP UEFI variable not found 0x%lx\n",
2988ae3e231SGregory Greenman 			     PTR_ERR(data));
2998ae3e231SGregory Greenman 		return;
3008ae3e231SGregory Greenman 	}
3018ae3e231SGregory Greenman 
3028ae3e231SGregory Greenman 	if (package_size < sizeof(*data)) {
3038ae3e231SGregory Greenman 		IWL_DEBUG_FW(trans,
3048ae3e231SGregory Greenman 			     "Invalid STEP table UEFI variable len (%lu)\n",
3058ae3e231SGregory Greenman 			     package_size);
3068ae3e231SGregory Greenman 		kfree(data);
3078ae3e231SGregory Greenman 		return;
30809b4c35dSAyala Barazani 	}
30909b4c35dSAyala Barazani 
31009b4c35dSAyala Barazani 	IWL_DEBUG_FW(trans, "Read STEP from UEFI with size %lu\n",
31109b4c35dSAyala Barazani 		     package_size);
31209b4c35dSAyala Barazani 
31309b4c35dSAyala Barazani 	ret = iwl_uefi_step_parse(data, trans);
31409b4c35dSAyala Barazani 	if (ret < 0)
31509b4c35dSAyala Barazani 		IWL_DEBUG_FW(trans, "Cannot read STEP tables. rev is invalid\n");
31609b4c35dSAyala Barazani 
31709b4c35dSAyala Barazani 	kfree(data);
31809b4c35dSAyala Barazani }
31909b4c35dSAyala Barazani IWL_EXPORT_SYMBOL(iwl_uefi_get_step_table);
32009b4c35dSAyala Barazani 
321c593d2faSAyala Barazani #ifdef CONFIG_ACPI
iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data * sgom_data,struct iwl_fw_runtime * fwrt)322c593d2faSAyala Barazani static int iwl_uefi_sgom_parse(struct uefi_cnv_wlan_sgom_data *sgom_data,
323c593d2faSAyala Barazani 			       struct iwl_fw_runtime *fwrt)
324c593d2faSAyala Barazani {
325c593d2faSAyala Barazani 	int i, j;
326c593d2faSAyala Barazani 
327c593d2faSAyala Barazani 	if (sgom_data->revision != 1)
328c593d2faSAyala Barazani 		return -EINVAL;
329c593d2faSAyala Barazani 
330c593d2faSAyala Barazani 	memcpy(fwrt->sgom_table.offset_map, sgom_data->offset_map,
331c593d2faSAyala Barazani 	       sizeof(fwrt->sgom_table.offset_map));
332c593d2faSAyala Barazani 
333c593d2faSAyala Barazani 	for (i = 0; i < MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE; i++) {
334c593d2faSAyala Barazani 		for (j = 0; j < MCC_TO_SAR_OFFSET_TABLE_COL_SIZE; j++) {
335c593d2faSAyala Barazani 			/* since each byte is composed of to values, */
336c593d2faSAyala Barazani 			/* one for each letter, */
337c593d2faSAyala Barazani 			/* extract and check each of them separately */
338c593d2faSAyala Barazani 			u8 value = fwrt->sgom_table.offset_map[i][j];
339c593d2faSAyala Barazani 			u8 low = value & 0xF;
340c593d2faSAyala Barazani 			u8 high = (value & 0xF0) >> 4;
341c593d2faSAyala Barazani 
342c593d2faSAyala Barazani 			if (high > fwrt->geo_num_profiles)
343c593d2faSAyala Barazani 				high = 0;
344c593d2faSAyala Barazani 			if (low > fwrt->geo_num_profiles)
345c593d2faSAyala Barazani 				low = 0;
346c593d2faSAyala Barazani 			fwrt->sgom_table.offset_map[i][j] = (high << 4) | low;
347c593d2faSAyala Barazani 		}
348c593d2faSAyala Barazani 	}
349c593d2faSAyala Barazani 
350c593d2faSAyala Barazani 	fwrt->sgom_enabled = true;
351c593d2faSAyala Barazani 	return 0;
352c593d2faSAyala Barazani }
353c593d2faSAyala Barazani 
iwl_uefi_get_sgom_table(struct iwl_trans * trans,struct iwl_fw_runtime * fwrt)354c593d2faSAyala Barazani void iwl_uefi_get_sgom_table(struct iwl_trans *trans,
355c593d2faSAyala Barazani 			     struct iwl_fw_runtime *fwrt)
356c593d2faSAyala Barazani {
357c593d2faSAyala Barazani 	struct uefi_cnv_wlan_sgom_data *data;
358c593d2faSAyala Barazani 	unsigned long package_size;
3590c4bad7fSArd Biesheuvel 	int ret;
360c593d2faSAyala Barazani 
3618ae3e231SGregory Greenman 	if (!fwrt->geo_enabled)
362c593d2faSAyala Barazani 		return;
363c593d2faSAyala Barazani 
3648ae3e231SGregory Greenman 	data = iwl_uefi_get_variable(IWL_UEFI_SGOM_NAME, &IWL_EFI_VAR_GUID,
3658ae3e231SGregory Greenman 				     &package_size);
3668ae3e231SGregory Greenman 	if (IS_ERR(data)) {
367c593d2faSAyala Barazani 		IWL_DEBUG_FW(trans,
3688ae3e231SGregory Greenman 			     "SGOM UEFI variable not found 0x%lx\n",
3698ae3e231SGregory Greenman 			     PTR_ERR(data));
3708ae3e231SGregory Greenman 		return;
3718ae3e231SGregory Greenman 	}
3728ae3e231SGregory Greenman 
3738ae3e231SGregory Greenman 	if (package_size < sizeof(*data)) {
3748ae3e231SGregory Greenman 		IWL_DEBUG_FW(trans,
3758ae3e231SGregory Greenman 			     "Invalid SGOM table UEFI variable len (%lu)\n",
3768ae3e231SGregory Greenman 			     package_size);
3778ae3e231SGregory Greenman 		kfree(data);
3788ae3e231SGregory Greenman 		return;
379c593d2faSAyala Barazani 	}
380c593d2faSAyala Barazani 
381c593d2faSAyala Barazani 	IWL_DEBUG_FW(trans, "Read SGOM from UEFI with size %lu\n",
382c593d2faSAyala Barazani 		     package_size);
383c593d2faSAyala Barazani 
384c593d2faSAyala Barazani 	ret = iwl_uefi_sgom_parse(data, fwrt);
385c593d2faSAyala Barazani 	if (ret < 0)
386c593d2faSAyala Barazani 		IWL_DEBUG_FW(trans, "Cannot read SGOM tables. rev is invalid\n");
387c593d2faSAyala Barazani 
388c593d2faSAyala Barazani 	kfree(data);
389c593d2faSAyala Barazani }
390c593d2faSAyala Barazani IWL_EXPORT_SYMBOL(iwl_uefi_get_sgom_table);
391c593d2faSAyala Barazani #endif /* CONFIG_ACPI */
392