brintos

brintos / linux-shallow public Read only

0
0
Text · 25.1 KiB · 0bc3229 Raw
1026 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright (C) 2017 Intel Deutschland GmbH4 * Copyright (C) 2019-2024 Intel Corporation5 */6#include <linux/uuid.h>7#include "iwl-drv.h"8#include "iwl-debug.h"9#include "acpi.h"10#include "fw/runtime.h"11 12const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,13				  0xA5, 0xB3, 0x1F, 0x73,14				  0x8E, 0x28, 0x5A, 0xDE);15 16static const size_t acpi_dsm_size[DSM_FUNC_NUM_FUNCS] = {17	[DSM_FUNC_QUERY] =			sizeof(u32),18	[DSM_FUNC_DISABLE_SRD] =		sizeof(u8),19	[DSM_FUNC_ENABLE_INDONESIA_5G2] =	sizeof(u8),20	[DSM_FUNC_ENABLE_6E] =			sizeof(u32),21	[DSM_FUNC_REGULATORY_CONFIG] =		sizeof(u32),22	/* Not supported in driver */23	[5] =					(size_t)0,24	[DSM_FUNC_11AX_ENABLEMENT] =		sizeof(u32),25	[DSM_FUNC_ENABLE_UNII4_CHAN] =		sizeof(u32),26	[DSM_FUNC_ACTIVATE_CHANNEL] =		sizeof(u32),27	[DSM_FUNC_FORCE_DISABLE_CHANNELS] =	sizeof(u32),28	[DSM_FUNC_ENERGY_DETECTION_THRESHOLD] =	sizeof(u32),29	[DSM_FUNC_RFI_CONFIG] =			sizeof(u32),30	[DSM_FUNC_ENABLE_11BE] =		sizeof(u32),31};32 33static int iwl_acpi_get_handle(struct device *dev, acpi_string method,34			       acpi_handle *ret_handle)35{36	acpi_handle root_handle;37	acpi_status status;38 39	root_handle = ACPI_HANDLE(dev);40	if (!root_handle) {41		IWL_DEBUG_DEV_RADIO(dev,42				    "ACPI: Could not retrieve root port handle\n");43		return -ENOENT;44	}45 46	status = acpi_get_handle(root_handle, method, ret_handle);47	if (ACPI_FAILURE(status)) {48		IWL_DEBUG_DEV_RADIO(dev,49				    "ACPI: %s method not found\n", method);50		return -ENOENT;51	}52	return 0;53}54 55static void *iwl_acpi_get_object(struct device *dev, acpi_string method)56{57	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};58	acpi_handle handle;59	acpi_status status;60	int ret;61 62	ret = iwl_acpi_get_handle(dev, method, &handle);63	if (ret)64		return ERR_PTR(-ENOENT);65 66	/* Call the method with no arguments */67	status = acpi_evaluate_object(handle, NULL, NULL, &buf);68	if (ACPI_FAILURE(status)) {69		IWL_DEBUG_DEV_RADIO(dev,70				    "ACPI: %s method invocation failed (status: 0x%x)\n",71				    method, status);72		return ERR_PTR(-ENOENT);73	}74	return buf.pointer;75}76 77/*78 * Generic function for evaluating a method defined in the device specific79 * method (DSM) interface. The returned acpi object must be freed by calling80 * function.81 */82static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,83				     union acpi_object *args,84				     const guid_t *guid)85{86	union acpi_object *obj;87 88	obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), guid, rev, func,89				args);90	if (!obj) {91		IWL_DEBUG_DEV_RADIO(dev,92				    "ACPI: DSM method invocation failed (rev: %d, func:%d)\n",93				    rev, func);94		return ERR_PTR(-ENOENT);95	}96	return obj;97}98 99/*100 * Generic function to evaluate a DSM with no arguments101 * and an integer return value,102 * (as an integer object or inside a buffer object),103 * verify and assign the value in the "value" parameter.104 * return 0 in success and the appropriate errno otherwise.105 */106static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func,107				    const guid_t *guid, u64 *value,108				    size_t expected_size)109{110	union acpi_object *obj;111	int ret = 0;112 113	obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid);114	if (IS_ERR(obj)) {115		IWL_DEBUG_DEV_RADIO(dev,116				    "Failed to get  DSM object. func= %d\n",117				    func);118		return -ENOENT;119	}120 121	if (obj->type == ACPI_TYPE_INTEGER) {122		*value = obj->integer.value;123	} else if (obj->type == ACPI_TYPE_BUFFER) {124		__le64 le_value = 0;125 126		if (WARN_ON_ONCE(expected_size > sizeof(le_value)))127			return -EINVAL;128 129		/* if the buffer size doesn't match the expected size */130		if (obj->buffer.length != expected_size)131			IWL_DEBUG_DEV_RADIO(dev,132					    "ACPI: DSM invalid buffer size, padding or truncating (%d)\n",133					    obj->buffer.length);134 135		 /* assuming LE from Intel BIOS spec */136		memcpy(&le_value, obj->buffer.pointer,137		       min_t(size_t, expected_size, (size_t)obj->buffer.length));138		*value = le64_to_cpu(le_value);139	} else {140		IWL_DEBUG_DEV_RADIO(dev,141				    "ACPI: DSM method did not return a valid object, type=%d\n",142				    obj->type);143		ret = -EINVAL;144		goto out;145	}146 147	IWL_DEBUG_DEV_RADIO(dev,148			    "ACPI: DSM method evaluated: func=%d, ret=%d\n",149			    func, ret);150out:151	ACPI_FREE(obj);152	return ret;153}154 155/*156 * This function receives a DSM function number, calculates its expected size157 * according to Intel BIOS spec, and fills in the value in a 32-bit field.158 * In case the expected size is smaller than 32-bit, padding will be added.159 */160int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt,161		     enum iwl_dsm_funcs func, u32 *value)162{163	size_t expected_size;164	u64 tmp;165	int ret;166 167	BUILD_BUG_ON(ARRAY_SIZE(acpi_dsm_size) != DSM_FUNC_NUM_FUNCS);168 169	if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size)))170		return -EINVAL;171 172	expected_size = acpi_dsm_size[func];173 174	/* Currently all ACPI DSMs are either 8-bit or 32-bit */175	if (expected_size != sizeof(u8) && expected_size != sizeof(u32))176		return -EOPNOTSUPP;177 178	ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func,179				       &iwl_guid, &tmp, expected_size);180	if (ret)181		return ret;182 183	if ((expected_size == sizeof(u8) && tmp != (u8)tmp) ||184	    (expected_size == sizeof(u32) && tmp != (u32)tmp))185		IWL_DEBUG_RADIO(fwrt,186				"DSM value overflows the expected size, truncating\n");187	*value = (u32)tmp;188 189	return 0;190}191 192static union acpi_object *193iwl_acpi_get_wifi_pkg_range(struct device *dev,194			    union acpi_object *data,195			    int min_data_size,196			    int max_data_size,197			    int *tbl_rev)198{199	int i;200	union acpi_object *wifi_pkg;201 202	/*203	 * We need at least one entry in the wifi package that204	 * describes the domain, and one more entry, otherwise there's205	 * no point in reading it.206	 */207	if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))208		return ERR_PTR(-EINVAL);209 210	/*211	 * We need at least two packages, one for the revision and one212	 * for the data itself.  Also check that the revision is valid213	 * (i.e. it is an integer (each caller has to check by itself214	 * if the returned revision is supported)).215	 */216	if (data->type != ACPI_TYPE_PACKAGE ||217	    data->package.count < 2 ||218	    data->package.elements[0].type != ACPI_TYPE_INTEGER) {219		IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");220		return ERR_PTR(-EINVAL);221	}222 223	*tbl_rev = data->package.elements[0].integer.value;224 225	/* loop through all the packages to find the one for WiFi */226	for (i = 1; i < data->package.count; i++) {227		union acpi_object *domain;228 229		wifi_pkg = &data->package.elements[i];230 231		/* skip entries that are not a package with the right size */232		if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||233		    wifi_pkg->package.count < min_data_size ||234		    wifi_pkg->package.count > max_data_size)235			continue;236 237		domain = &wifi_pkg->package.elements[0];238		if (domain->type == ACPI_TYPE_INTEGER &&239		    domain->integer.value == ACPI_WIFI_DOMAIN)240			goto found;241	}242 243	return ERR_PTR(-ENOENT);244 245found:246	return wifi_pkg;247}248 249static union acpi_object *250iwl_acpi_get_wifi_pkg(struct device *dev,251		      union acpi_object *data,252		      int data_size, int *tbl_rev)253{254	return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size,255					   tbl_rev);256}257 258int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt,259			   struct iwl_tas_data *tas_data)260{261	union acpi_object *wifi_pkg, *data;262	int ret, tbl_rev, i, block_list_size, enabled;263 264	data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);265	if (IS_ERR(data))266		return PTR_ERR(data);267 268	/* try to read wtas table revision 1 or revision 0*/269	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,270					 ACPI_WTAS_WIFI_DATA_SIZE,271					 &tbl_rev);272	if (IS_ERR(wifi_pkg)) {273		ret = PTR_ERR(wifi_pkg);274		goto out_free;275	}276 277	if (tbl_rev == 1 && wifi_pkg->package.elements[1].type ==278		ACPI_TYPE_INTEGER) {279		u32 tas_selection =280			(u32)wifi_pkg->package.elements[1].integer.value;281 282		enabled = iwl_parse_tas_selection(fwrt, tas_data,283						  tas_selection);284 285	} else if (tbl_rev == 0 &&286		wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) {287		enabled = !!wifi_pkg->package.elements[1].integer.value;288	} else {289		ret = -EINVAL;290		goto out_free;291	}292 293	if (!enabled) {294		IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");295		ret = 0;296		goto out_free;297	}298 299	IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);300	if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||301	    wifi_pkg->package.elements[2].integer.value >302	    IWL_WTAS_BLACK_LIST_MAX) {303		IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",304				wifi_pkg->package.elements[2].integer.value);305		ret = -EINVAL;306		goto out_free;307	}308	block_list_size = wifi_pkg->package.elements[2].integer.value;309	tas_data->block_list_size = cpu_to_le32(block_list_size);310 311	IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);312 313	for (i = 0; i < block_list_size; i++) {314		u32 country;315 316		if (wifi_pkg->package.elements[3 + i].type !=317		    ACPI_TYPE_INTEGER) {318			IWL_DEBUG_RADIO(fwrt,319					"TAS invalid array elem %d\n", 3 + i);320			ret = -EINVAL;321			goto out_free;322		}323 324		country = wifi_pkg->package.elements[3 + i].integer.value;325		tas_data->block_list_array[i] = cpu_to_le32(country);326		IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);327	}328 329	ret = 1;330out_free:331	kfree(data);332	return ret;333}334 335int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)336{337	union acpi_object *wifi_pkg, *data;338	u32 mcc_val;339	int ret, tbl_rev;340 341	data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDD_METHOD);342	if (IS_ERR(data))343		return PTR_ERR(data);344 345	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,346					 ACPI_WRDD_WIFI_DATA_SIZE,347					 &tbl_rev);348	if (IS_ERR(wifi_pkg)) {349		ret = PTR_ERR(wifi_pkg);350		goto out_free;351	}352 353	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||354	    tbl_rev != 0) {355		ret = -EINVAL;356		goto out_free;357	}358 359	mcc_val = wifi_pkg->package.elements[1].integer.value;360	if (mcc_val != BIOS_MCC_CHINA) {361		ret = -EINVAL;362		IWL_DEBUG_RADIO(fwrt, "ACPI WRDD is supported only for CN\n");363		goto out_free;364	}365 366	mcc[0] = (mcc_val >> 8) & 0xff;367	mcc[1] = mcc_val & 0xff;368	mcc[2] = '\0';369 370	ret = 0;371out_free:372	kfree(data);373	return ret;374}375 376int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit)377{378	union acpi_object *data, *wifi_pkg;379	int tbl_rev, ret = -EINVAL;380 381	*dflt_pwr_limit = 0;382	data = iwl_acpi_get_object(fwrt->dev, ACPI_SPLC_METHOD);383	if (IS_ERR(data))384		goto out;385 386	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,387					 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);388	if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||389	    wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER)390		goto out_free;391 392	*dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;393	ret = 0;394out_free:395	kfree(data);396out:397	return ret;398}399 400int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)401{402	union acpi_object *wifi_pkg, *data;403	int ret, tbl_rev;404 405	data = iwl_acpi_get_object(fwrt->dev, ACPI_ECKV_METHOD);406	if (IS_ERR(data))407		return PTR_ERR(data);408 409	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,410					 ACPI_ECKV_WIFI_DATA_SIZE,411					 &tbl_rev);412	if (IS_ERR(wifi_pkg)) {413		ret = PTR_ERR(wifi_pkg);414		goto out_free;415	}416 417	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||418	    tbl_rev != 0) {419		ret = -EINVAL;420		goto out_free;421	}422 423	*extl_clk = wifi_pkg->package.elements[1].integer.value;424 425	ret = 0;426 427out_free:428	kfree(data);429	return ret;430}431 432static int433iwl_acpi_parse_chains_table(union acpi_object *table,434			    struct iwl_sar_profile_chain *chains,435			    u8 num_chains, u8 num_sub_bands)436{437	for (u8 chain = 0; chain < num_chains; chain++) {438		for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM;439		     subband++) {440			/* if we don't have the values, use the default */441			if (subband >= num_sub_bands) {442				chains[chain].subbands[subband] = 0;443			} else if (table->type != ACPI_TYPE_INTEGER ||444				   table->integer.value > U8_MAX) {445				return -EINVAL;446			} else {447				chains[chain].subbands[subband] =448					table->integer.value;449				table++;450			}451		}452	}453 454	return 0;455}456 457int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)458{459	union acpi_object *wifi_pkg, *table, *data;460	int ret, tbl_rev;461	u32 flags;462	u8 num_chains, num_sub_bands;463 464	data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);465	if (IS_ERR(data))466		return PTR_ERR(data);467 468	/* start by trying to read revision 2 */469	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,470					 ACPI_WRDS_WIFI_DATA_SIZE_REV2,471					 &tbl_rev);472	if (!IS_ERR(wifi_pkg)) {473		if (tbl_rev != 2) {474			ret = -EINVAL;475			goto out_free;476		}477 478		num_chains = ACPI_SAR_NUM_CHAINS_REV2;479		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;480 481		goto read_table;482	}483 484	/* then try revision 1 */485	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,486					 ACPI_WRDS_WIFI_DATA_SIZE_REV1,487					 &tbl_rev);488	if (!IS_ERR(wifi_pkg)) {489		if (tbl_rev != 1) {490			ret = -EINVAL;491			goto out_free;492		}493 494		num_chains = ACPI_SAR_NUM_CHAINS_REV1;495		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;496 497		goto read_table;498	}499 500	/* then finally revision 0 */501	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,502					 ACPI_WRDS_WIFI_DATA_SIZE_REV0,503					 &tbl_rev);504	if (!IS_ERR(wifi_pkg)) {505		if (tbl_rev != 0) {506			ret = -EINVAL;507			goto out_free;508		}509 510		num_chains = ACPI_SAR_NUM_CHAINS_REV0;511		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;512 513		goto read_table;514	}515 516	ret = PTR_ERR(wifi_pkg);517	goto out_free;518 519read_table:520	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {521		ret = -EINVAL;522		goto out_free;523	}524 525	IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);526 527	flags = wifi_pkg->package.elements[1].integer.value;528	fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;529 530	/* position of the actual table */531	table = &wifi_pkg->package.elements[2];532 533	/* The profile from WRDS is officially profile 1, but goes534	 * into sar_profiles[0] (because we don't have a profile 0).535	 */536	ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains,537					  num_chains, num_sub_bands);538	if (!ret && flags & IWL_SAR_ENABLE_MSK)539		fwrt->sar_profiles[0].enabled = true;540 541out_free:542	kfree(data);543	return ret;544}545 546int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)547{548	union acpi_object *wifi_pkg, *data;549	bool enabled;550	int i, n_profiles, tbl_rev, pos;551	int ret = 0;552	u8 num_sub_bands;553 554	data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);555	if (IS_ERR(data))556		return PTR_ERR(data);557 558	/* start by trying to read revision 2 */559	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,560					 ACPI_EWRD_WIFI_DATA_SIZE_REV2,561					 &tbl_rev);562	if (!IS_ERR(wifi_pkg)) {563		if (tbl_rev != 2) {564			ret = -EINVAL;565			goto out_free;566		}567 568		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;569 570		goto read_table;571	}572 573	/* then try revision 1 */574	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,575					 ACPI_EWRD_WIFI_DATA_SIZE_REV1,576					 &tbl_rev);577	if (!IS_ERR(wifi_pkg)) {578		if (tbl_rev != 1) {579			ret = -EINVAL;580			goto out_free;581		}582 583		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;584 585		goto read_table;586	}587 588	/* then finally revision 0 */589	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,590					 ACPI_EWRD_WIFI_DATA_SIZE_REV0,591					 &tbl_rev);592	if (!IS_ERR(wifi_pkg)) {593		if (tbl_rev != 0) {594			ret = -EINVAL;595			goto out_free;596		}597 598		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;599 600		goto read_table;601	}602 603	ret = PTR_ERR(wifi_pkg);604	goto out_free;605 606read_table:607	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||608	    wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {609		ret = -EINVAL;610		goto out_free;611	}612 613	enabled = !!(wifi_pkg->package.elements[1].integer.value);614	n_profiles = wifi_pkg->package.elements[2].integer.value;615 616	/*617	 * Check the validity of n_profiles.  The EWRD profiles start618	 * from index 1, so the maximum value allowed here is619	 * ACPI_SAR_PROFILES_NUM - 1.620	 */621	if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {622		ret = -EINVAL;623		goto out_free;624	}625 626	/* the tables start at element 3 */627	pos = 3;628 629	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1);630	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0);631 632	/* parse non-cdb chains for all profiles */633	for (i = 0; i < n_profiles; i++) {634		union acpi_object *table = &wifi_pkg->package.elements[pos];635 636		/* The EWRD profiles officially go from 2 to 4, but we637		 * save them in sar_profiles[1-3] (because we don't638		 * have profile 0).  So in the array we start from 1.639		 */640		ret = iwl_acpi_parse_chains_table(table,641						  fwrt->sar_profiles[i + 1].chains,642						  ACPI_SAR_NUM_CHAINS_REV0,643						  num_sub_bands);644		if (ret < 0)645			goto out_free;646 647		/* go to the next table */648		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;649	}650 651	/* non-cdb table revisions */652	if (tbl_rev < 2)653		goto set_enabled;654 655	/* parse cdb chains for all profiles */656	for (i = 0; i < n_profiles; i++) {657		struct iwl_sar_profile_chain *chains;658		union acpi_object *table;659 660		table = &wifi_pkg->package.elements[pos];661		chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0];662		ret = iwl_acpi_parse_chains_table(table,663						  chains,664						  ACPI_SAR_NUM_CHAINS_REV0,665						  num_sub_bands);666		if (ret < 0)667			goto out_free;668 669		/* go to the next table */670		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;671	}672 673set_enabled:674	for (i = 0; i < n_profiles; i++)675		fwrt->sar_profiles[i + 1].enabled = enabled;676 677out_free:678	kfree(data);679	return ret;680}681 682int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)683{684	union acpi_object *wifi_pkg, *data;685	int i, j, k, ret, tbl_rev;686	u8 num_bands, num_profiles;687	static const struct {688		u8 revisions;689		u8 bands;690		u8 profiles;691		u8 min_profiles;692	} rev_data[] = {693		{694			.revisions = BIT(3),695			.bands = ACPI_GEO_NUM_BANDS_REV2,696			.profiles = ACPI_NUM_GEO_PROFILES_REV3,697			.min_profiles = BIOS_GEO_MIN_PROFILE_NUM,698		},699		{700			.revisions = BIT(2),701			.bands = ACPI_GEO_NUM_BANDS_REV2,702			.profiles = ACPI_NUM_GEO_PROFILES,703		},704		{705			.revisions = BIT(0) | BIT(1),706			.bands = ACPI_GEO_NUM_BANDS_REV0,707			.profiles = ACPI_NUM_GEO_PROFILES,708		},709	};710	int idx;711	/* start from one to skip the domain */712	int entry_idx = 1;713 714	BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);715	BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);716 717	data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);718	if (IS_ERR(data))719		return PTR_ERR(data);720 721	/* read the highest revision we understand first */722	for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {723		/* min_profiles != 0 requires num_profiles header */724		u32 hdr_size = 1 + !!rev_data[idx].min_profiles;725		u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *726				   rev_data[idx].bands;727		u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;728		u32 min_size;729 730		if (!rev_data[idx].min_profiles)731			min_size = max_size;732		else733			min_size = hdr_size +734				   profile_size * rev_data[idx].min_profiles;735 736		wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,737						       min_size, max_size,738						       &tbl_rev);739		if (!IS_ERR(wifi_pkg)) {740			if (!(BIT(tbl_rev) & rev_data[idx].revisions))741				continue;742 743			num_bands = rev_data[idx].bands;744			num_profiles = rev_data[idx].profiles;745 746			if (rev_data[idx].min_profiles) {747				/* read header that says # of profiles */748				union acpi_object *entry;749 750				entry = &wifi_pkg->package.elements[entry_idx];751				entry_idx++;752				if (entry->type != ACPI_TYPE_INTEGER ||753				    entry->integer.value > num_profiles ||754				    entry->integer.value <755					rev_data[idx].min_profiles) {756					ret = -EINVAL;757					goto out_free;758				}759 760				/*761				 * Check to see if we received package count762				 * same as max # of profiles763				 */764				if (wifi_pkg->package.count !=765				    hdr_size + profile_size * num_profiles) {766					ret = -EINVAL;767					goto out_free;768				}769 770				/* Number of valid profiles */771				num_profiles = entry->integer.value;772			}773			goto read_table;774		}775	}776 777	if (idx < ARRAY_SIZE(rev_data))778		ret = PTR_ERR(wifi_pkg);779	else780		ret = -ENOENT;781	goto out_free;782 783read_table:784	fwrt->geo_rev = tbl_rev;785	for (i = 0; i < num_profiles; i++) {786		for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) {787			union acpi_object *entry;788 789			/*790			 * num_bands is either 2 or 3, if it's only 2 then791			 * fill the third band (6 GHz) with the values from792			 * 5 GHz (second band)793			 */794			if (j >= num_bands) {795				fwrt->geo_profiles[i].bands[j].max =796					fwrt->geo_profiles[i].bands[1].max;797			} else {798				entry = &wifi_pkg->package.elements[entry_idx];799				entry_idx++;800				if (entry->type != ACPI_TYPE_INTEGER ||801				    entry->integer.value > U8_MAX) {802					ret = -EINVAL;803					goto out_free;804				}805 806				fwrt->geo_profiles[i].bands[j].max =807					entry->integer.value;808			}809 810			for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) {811				/* same here as above */812				if (j >= num_bands) {813					fwrt->geo_profiles[i].bands[j].chains[k] =814						fwrt->geo_profiles[i].bands[1].chains[k];815				} else {816					entry = &wifi_pkg->package.elements[entry_idx];817					entry_idx++;818					if (entry->type != ACPI_TYPE_INTEGER ||819					    entry->integer.value > U8_MAX) {820						ret = -EINVAL;821						goto out_free;822					}823 824					fwrt->geo_profiles[i].bands[j].chains[k] =825						entry->integer.value;826				}827			}828		}829	}830 831	fwrt->geo_num_profiles = num_profiles;832	fwrt->geo_enabled = true;833	ret = 0;834out_free:835	kfree(data);836	return ret;837}838 839int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)840{841	union acpi_object *wifi_pkg, *data, *flags;842	int i, j, ret, tbl_rev, num_sub_bands = 0;843	int idx = 2;844 845	data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);846	if (IS_ERR(data))847		return PTR_ERR(data);848 849	/* try to read ppag table rev 3, 2 or 1 (all have the same data size) */850	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,851				ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);852 853	if (!IS_ERR(wifi_pkg)) {854		if (tbl_rev >= 1 && tbl_rev <= 3) {855			num_sub_bands = IWL_NUM_SUB_BANDS_V2;856			IWL_DEBUG_RADIO(fwrt,857					"Reading PPAG table (tbl_rev=%d)\n",858					tbl_rev);859			goto read_table;860		} else {861			ret = -EINVAL;862			goto out_free;863		}864	}865 866	/* try to read ppag table revision 0 */867	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,868			ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);869 870	if (!IS_ERR(wifi_pkg)) {871		if (tbl_rev != 0) {872			ret = -EINVAL;873			goto out_free;874		}875		num_sub_bands = IWL_NUM_SUB_BANDS_V1;876		IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");877		goto read_table;878	}879 880	ret = PTR_ERR(wifi_pkg);881	goto out_free;882 883read_table:884	fwrt->ppag_ver = tbl_rev;885	flags = &wifi_pkg->package.elements[1];886 887	if (flags->type != ACPI_TYPE_INTEGER) {888		ret = -EINVAL;889		goto out_free;890	}891 892	fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value,893						   fwrt->ppag_ver);894 895	/*896	 * read, verify gain values and save them into the PPAG table.897	 * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the898	 * following sub-bands to High-Band (5GHz).899	 */900	for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {901		for (j = 0; j < num_sub_bands; j++) {902			union acpi_object *ent;903 904			ent = &wifi_pkg->package.elements[idx++];905			if (ent->type != ACPI_TYPE_INTEGER) {906				ret = -EINVAL;907				goto out_free;908			}909 910			fwrt->ppag_chains[i].subbands[j] = ent->integer.value;911		}912	}913 914	ret = 0;915 916out_free:917	kfree(data);918	return ret;919}920 921void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt,922			      struct iwl_phy_specific_cfg *filters)923{924	struct iwl_phy_specific_cfg tmp = {};925	union acpi_object *wifi_pkg, *data;926	int tbl_rev, i;927 928	data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);929	if (IS_ERR(data))930		return;931 932	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,933					 ACPI_WPFC_WIFI_DATA_SIZE,934					 &tbl_rev);935	if (IS_ERR(wifi_pkg))936		goto out_free;937 938	if (tbl_rev != 0)939		goto out_free;940 941	BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=942		     ACPI_WPFC_WIFI_DATA_SIZE - 1);943 944	for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {945		if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)946			goto out_free;947		tmp.filter_cfg_chains[i] =948			cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);949	}950 951	IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");952	*filters = tmp;953out_free:954	kfree(data);955}956IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);957 958void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt)959{960	union acpi_object *wifi_pkg, *data;961	int tbl_rev;962 963	data = iwl_acpi_get_object(fwrt->dev, ACPI_GLAI_METHOD);964	if (IS_ERR(data))965		return;966 967	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,968					 ACPI_GLAI_WIFI_DATA_SIZE,969					 &tbl_rev);970	if (IS_ERR(wifi_pkg))971		goto out_free;972 973	if (tbl_rev != 0) {974		IWL_DEBUG_RADIO(fwrt, "Invalid GLAI revision: %d\n", tbl_rev);975		goto out_free;976	}977 978	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||979	    wifi_pkg->package.elements[1].integer.value > ACPI_GLAI_MAX_STATUS)980		goto out_free;981 982	fwrt->uefi_tables_lock_status =983		wifi_pkg->package.elements[1].integer.value;984 985	IWL_DEBUG_RADIO(fwrt,986			"Loaded UEFI WIFI GUID lock status: %d from ACPI\n",987			fwrt->uefi_tables_lock_status);988out_free:989	kfree(data);990}991IWL_EXPORT_SYMBOL(iwl_acpi_get_guid_lock_status);992 993int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)994{995	union acpi_object *wifi_pkg, *data;996	int ret = -ENOENT;997	int tbl_rev;998 999	data = iwl_acpi_get_object(fwrt->dev, ACPI_WBEM_METHOD);1000	if (IS_ERR(data))1001		return ret;1002 1003	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,1004					 ACPI_WBEM_WIFI_DATA_SIZE,1005					 &tbl_rev);1006	if (IS_ERR(wifi_pkg))1007		goto out_free;1008 1009	if (tbl_rev != IWL_ACPI_WBEM_REVISION) {1010		IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI WBEM revision:%d\n",1011				tbl_rev);1012		goto out_free;1013	}1014 1015	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)1016		goto out_free;1017 1018	*value = wifi_pkg->package.elements[1].integer.value &1019		 IWL_ACPI_WBEM_REV0_MASK;1020	IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from ACPI\n");1021	ret = 0;1022out_free:1023	kfree(data);1024	return ret;1025}1026