File Index
Files and directories
Sign in
FILE

acpi.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/wireless/intel/iwlwifi/fw/acpi.c C
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2017 Intel Deutschland GmbH
   4 * Copyright (C) 2019-2025 Intel Corporation
   5 */
   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
  12static const 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 specific
  79 * method (DSM) interface. The returned acpi object must be freed by calling
  80 * function.
  81 */
  82union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev,
  83					   int func, 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 arguments
 101 * 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;
 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			ret = -EINVAL;
 128			goto out;
 129		}
 130
 131		/* if the buffer size doesn't match the expected size */
 132		if (obj->buffer.length != expected_size)
 133			IWL_DEBUG_DEV_RADIO(dev,
 134					    "ACPI: DSM invalid buffer size, padding or truncating (%d)\n",
 135					    obj->buffer.length);
 136
 137		 /* assuming LE from Intel BIOS spec */
 138		memcpy(&le_value, obj->buffer.pointer,
 139		       min_t(size_t, expected_size, (size_t)obj->buffer.length));
 140		*value = le64_to_cpu(le_value);
 141	} else {
 142		IWL_DEBUG_DEV_RADIO(dev,
 143				    "ACPI: DSM method did not return a valid object, type=%d\n",
 144				    obj->type);
 145		ret = -EINVAL;
 146		goto out;
 147	}
 148
 149	IWL_DEBUG_DEV_RADIO(dev,
 150			    "ACPI: DSM method evaluated: func=%d, value=%lld\n",
 151			    func, *value);
 152	ret = 0;
 153out:
 154	ACPI_FREE(obj);
 155	return ret;
 156}
 157
 158/*
 159 * This function receives a DSM function number, calculates its expected size
 160 * according to Intel BIOS spec, and fills in the value in a 32-bit field.
 161 * In case the expected size is smaller than 32-bit, padding will be added.
 162 */
 163int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt,
 164		     enum iwl_dsm_funcs func, u32 *value)
 165{
 166	size_t expected_size;
 167	u64 tmp;
 168	int ret;
 169
 170	BUILD_BUG_ON(ARRAY_SIZE(acpi_dsm_size) != DSM_FUNC_NUM_FUNCS);
 171
 172	if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size) || !func))
 173		return -EINVAL;
 174
 175	expected_size = acpi_dsm_size[func];
 176
 177	/* Currently all ACPI DSMs are either 8-bit or 32-bit */
 178	if (expected_size != sizeof(u8) && expected_size != sizeof(u32))
 179		return -EOPNOTSUPP;
 180
 181	if (!fwrt->acpi_dsm_funcs_valid) {
 182		ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV,
 183					       DSM_FUNC_QUERY,
 184					       &iwl_guid, &tmp,
 185					       acpi_dsm_size[DSM_FUNC_QUERY]);
 186		if (ret) {
 187			/* always indicate BIT(0) to avoid re-reading */
 188			fwrt->acpi_dsm_funcs_valid = BIT(0);
 189			return ret;
 190		}
 191
 192		IWL_DEBUG_RADIO(fwrt, "ACPI DSM validity bitmap 0x%x\n",
 193				(u32)tmp);
 194		/* always indicate BIT(0) to avoid re-reading */
 195		fwrt->acpi_dsm_funcs_valid = tmp | BIT(0);
 196	}
 197
 198	if (!(fwrt->acpi_dsm_funcs_valid & BIT(func))) {
 199		IWL_DEBUG_RADIO(fwrt, "ACPI DSM %d not indicated as valid\n",
 200				func);
 201		return -ENODATA;
 202	}
 203
 204	ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func,
 205				       &iwl_guid, &tmp, expected_size);
 206	if (ret)
 207		return ret;
 208
 209	if ((expected_size == sizeof(u8) && tmp != (u8)tmp) ||
 210	    (expected_size == sizeof(u32) && tmp != (u32)tmp))
 211		IWL_DEBUG_RADIO(fwrt,
 212				"DSM value overflows the expected size, truncating\n");
 213	*value = (u32)tmp;
 214
 215	return 0;
 216}
 217
 218static union acpi_object *
 219iwl_acpi_get_wifi_pkg_range(struct device *dev,
 220			    union acpi_object *data,
 221			    int min_data_size,
 222			    int max_data_size,
 223			    int *tbl_rev)
 224{
 225	int i;
 226	union acpi_object *wifi_pkg;
 227
 228	/*
 229	 * We need at least one entry in the wifi package that
 230	 * describes the domain, and one more entry, otherwise there's
 231	 * no point in reading it.
 232	 */
 233	if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))
 234		return ERR_PTR(-EINVAL);
 235
 236	/*
 237	 * We need at least two packages, one for the revision and one
 238	 * for the data itself.  Also check that the revision is valid
 239	 * (i.e. it is an integer (each caller has to check by itself
 240	 * if the returned revision is supported)).
 241	 */
 242	if (data->type != ACPI_TYPE_PACKAGE ||
 243	    data->package.count < 2 ||
 244	    data->package.elements[0].type != ACPI_TYPE_INTEGER) {
 245		IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");
 246		return ERR_PTR(-EINVAL);
 247	}
 248
 249	*tbl_rev = data->package.elements[0].integer.value;
 250
 251	/* loop through all the packages to find the one for WiFi */
 252	for (i = 1; i < data->package.count; i++) {
 253		union acpi_object *domain;
 254
 255		wifi_pkg = &data->package.elements[i];
 256
 257		/* skip entries that are not a package with the right size */
 258		if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
 259		    wifi_pkg->package.count < min_data_size ||
 260		    wifi_pkg->package.count > max_data_size)
 261			continue;
 262
 263		domain = &wifi_pkg->package.elements[0];
 264		if (domain->type == ACPI_TYPE_INTEGER &&
 265		    domain->integer.value == ACPI_WIFI_DOMAIN)
 266			goto found;
 267	}
 268
 269	return ERR_PTR(-ENOENT);
 270
 271found:
 272	return wifi_pkg;
 273}
 274
 275static union acpi_object *
 276iwl_acpi_get_wifi_pkg(struct device *dev,
 277		      union acpi_object *data,
 278		      int data_size, int *tbl_rev)
 279{
 280	return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size,
 281					   tbl_rev);
 282}
 283
 284int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt,
 285			   struct iwl_tas_data *tas_data)
 286{
 287	union acpi_object *wifi_pkg, *data;
 288	int ret, tbl_rev, block_list_size, enabled;
 289	u32 tas_selection;
 290
 291	data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
 292	if (IS_ERR(data))
 293		return PTR_ERR(data);
 294
 295	/* try to read wtas table */
 296	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 297					 ACPI_WTAS_WIFI_DATA_SIZE,
 298					 &tbl_rev);
 299	if (IS_ERR(wifi_pkg)) {
 300		ret = PTR_ERR(wifi_pkg);
 301		goto out_free;
 302	}
 303
 304	if (tbl_rev < 0 || tbl_rev > 2 ||
 305	    wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
 306		ret = -EINVAL;
 307		goto out_free;
 308	}
 309
 310	tas_selection = (u32)wifi_pkg->package.elements[1].integer.value;
 311	enabled = tas_selection & IWL_WTAS_ENABLED_MSK;
 312
 313	IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n",
 314			tas_selection);
 315	tas_data->table_source = BIOS_SOURCE_ACPI;
 316	tas_data->table_revision = tbl_rev;
 317	tas_data->tas_selection = tas_selection;
 318
 319	IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n",
 320			enabled ? "is" : "not");
 321
 322	IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);
 323	if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
 324	    wifi_pkg->package.elements[2].integer.value >
 325	    IWL_WTAS_BLACK_LIST_MAX) {
 326		IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
 327				wifi_pkg->package.elements[2].integer.value);
 328		ret = -EINVAL;
 329		goto out_free;
 330	}
 331
 332	block_list_size = wifi_pkg->package.elements[2].integer.value;
 333	tas_data->block_list_size = block_list_size;
 334
 335	IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);
 336
 337	for (int i = 0; i < block_list_size; i++) {
 338		u16 country;
 339
 340		if (wifi_pkg->package.elements[3 + i].type !=
 341		    ACPI_TYPE_INTEGER) {
 342			IWL_DEBUG_RADIO(fwrt,
 343					"TAS invalid array elem %d\n", 3 + i);
 344			ret = -EINVAL;
 345			goto out_free;
 346		}
 347
 348		country = wifi_pkg->package.elements[3 + i].integer.value;
 349		tas_data->block_list_array[i] = country;
 350		IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
 351	}
 352
 353	ret = enabled;
 354out_free:
 355	kfree(data);
 356	return ret;
 357}
 358
 359int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)
 360{
 361	union acpi_object *wifi_pkg, *data;
 362	u32 mcc_val;
 363	int ret, tbl_rev;
 364
 365	data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDD_METHOD);
 366	if (IS_ERR(data))
 367		return PTR_ERR(data);
 368
 369	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 370					 ACPI_WRDD_WIFI_DATA_SIZE,
 371					 &tbl_rev);
 372	if (IS_ERR(wifi_pkg)) {
 373		ret = PTR_ERR(wifi_pkg);
 374		goto out_free;
 375	}
 376
 377	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
 378	    tbl_rev != 0) {
 379		ret = -EINVAL;
 380		goto out_free;
 381	}
 382
 383	mcc_val = wifi_pkg->package.elements[1].integer.value;
 384	if (mcc_val != BIOS_MCC_CHINA) {
 385		ret = -EINVAL;
 386		IWL_DEBUG_RADIO(fwrt, "ACPI WRDD is supported only for CN\n");
 387		goto out_free;
 388	}
 389
 390	mcc[0] = (mcc_val >> 8) & 0xff;
 391	mcc[1] = mcc_val & 0xff;
 392	mcc[2] = '\0';
 393
 394	ret = 0;
 395out_free:
 396	kfree(data);
 397	return ret;
 398}
 399
 400int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit)
 401{
 402	union acpi_object *data, *wifi_pkg;
 403	int tbl_rev, ret = -EINVAL;
 404
 405	*dflt_pwr_limit = 0;
 406	data = iwl_acpi_get_object(fwrt->dev, ACPI_SPLC_METHOD);
 407	if (IS_ERR(data))
 408		goto out;
 409
 410	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 411					 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
 412	if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
 413	    wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER)
 414		goto out_free;
 415
 416	*dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
 417	ret = 0;
 418out_free:
 419	kfree(data);
 420out:
 421	return ret;
 422}
 423
 424int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)
 425{
 426	union acpi_object *wifi_pkg, *data;
 427	int ret, tbl_rev;
 428
 429	data = iwl_acpi_get_object(fwrt->dev, ACPI_ECKV_METHOD);
 430	if (IS_ERR(data))
 431		return PTR_ERR(data);
 432
 433	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 434					 ACPI_ECKV_WIFI_DATA_SIZE,
 435					 &tbl_rev);
 436	if (IS_ERR(wifi_pkg)) {
 437		ret = PTR_ERR(wifi_pkg);
 438		goto out_free;
 439	}
 440
 441	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
 442	    tbl_rev != 0) {
 443		ret = -EINVAL;
 444		goto out_free;
 445	}
 446
 447	*extl_clk = wifi_pkg->package.elements[1].integer.value;
 448
 449	ret = 0;
 450
 451out_free:
 452	kfree(data);
 453	return ret;
 454}
 455
 456static int
 457iwl_acpi_parse_chains_table(union acpi_object *table,
 458			    struct iwl_sar_profile_chain *chains,
 459			    u8 num_chains, u8 num_sub_bands)
 460{
 461	for (u8 chain = 0; chain < num_chains; chain++) {
 462		for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM;
 463		     subband++) {
 464			/* if we don't have the values, use the default */
 465			if (subband >= num_sub_bands) {
 466				chains[chain].subbands[subband] = 0;
 467			} else if (table->type != ACPI_TYPE_INTEGER ||
 468				   table->integer.value > U8_MAX) {
 469				return -EINVAL;
 470			} else {
 471				chains[chain].subbands[subband] =
 472					table->integer.value;
 473				table++;
 474			}
 475		}
 476	}
 477
 478	return 0;
 479}
 480
 481int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
 482{
 483	union acpi_object *wifi_pkg, *table, *data;
 484	int ret, tbl_rev;
 485	u32 flags;
 486	u8 num_chains, num_sub_bands;
 487
 488	data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
 489	if (IS_ERR(data))
 490		return PTR_ERR(data);
 491
 492	/* start by trying to read revision 2 */
 493	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 494					 ACPI_WRDS_WIFI_DATA_SIZE_REV2,
 495					 &tbl_rev);
 496	if (!IS_ERR(wifi_pkg)) {
 497		if (tbl_rev != 2) {
 498			ret = -EINVAL;
 499			goto out_free;
 500		}
 501
 502		num_chains = ACPI_SAR_NUM_CHAINS_REV2;
 503		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
 504
 505		goto read_table;
 506	}
 507
 508	/* then try revision 1 */
 509	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 510					 ACPI_WRDS_WIFI_DATA_SIZE_REV1,
 511					 &tbl_rev);
 512	if (!IS_ERR(wifi_pkg)) {
 513		if (tbl_rev != 1) {
 514			ret = -EINVAL;
 515			goto out_free;
 516		}
 517
 518		num_chains = ACPI_SAR_NUM_CHAINS_REV1;
 519		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
 520
 521		goto read_table;
 522	}
 523
 524	/* then finally revision 0 */
 525	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 526					 ACPI_WRDS_WIFI_DATA_SIZE_REV0,
 527					 &tbl_rev);
 528	if (!IS_ERR(wifi_pkg)) {
 529		if (tbl_rev != 0) {
 530			ret = -EINVAL;
 531			goto out_free;
 532		}
 533
 534		num_chains = ACPI_SAR_NUM_CHAINS_REV0;
 535		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
 536
 537		goto read_table;
 538	}
 539
 540	ret = PTR_ERR(wifi_pkg);
 541	goto out_free;
 542
 543read_table:
 544	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
 545		ret = -EINVAL;
 546		goto out_free;
 547	}
 548
 549	IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
 550
 551	flags = wifi_pkg->package.elements[1].integer.value;
 552	fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;
 553
 554	/* position of the actual table */
 555	table = &wifi_pkg->package.elements[2];
 556
 557	/* The profile from WRDS is officially profile 1, but goes
 558	 * into sar_profiles[0] (because we don't have a profile 0).
 559	 */
 560	ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains,
 561					  num_chains, num_sub_bands);
 562	if (!ret && flags & IWL_SAR_ENABLE_MSK)
 563		fwrt->sar_profiles[0].enabled = true;
 564
 565out_free:
 566	kfree(data);
 567	return ret;
 568}
 569
 570int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
 571{
 572	union acpi_object *wifi_pkg, *data;
 573	bool enabled;
 574	int i, n_profiles, tbl_rev, pos;
 575	int ret = 0;
 576	u8 num_sub_bands;
 577
 578	data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
 579	if (IS_ERR(data))
 580		return PTR_ERR(data);
 581
 582	/* start by trying to read revision 2 */
 583	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 584					 ACPI_EWRD_WIFI_DATA_SIZE_REV2,
 585					 &tbl_rev);
 586	if (!IS_ERR(wifi_pkg)) {
 587		if (tbl_rev != 2) {
 588			ret = -EINVAL;
 589			goto out_free;
 590		}
 591
 592		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
 593
 594		goto read_table;
 595	}
 596
 597	/* then try revision 1 */
 598	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 599					 ACPI_EWRD_WIFI_DATA_SIZE_REV1,
 600					 &tbl_rev);
 601	if (!IS_ERR(wifi_pkg)) {
 602		if (tbl_rev != 1) {
 603			ret = -EINVAL;
 604			goto out_free;
 605		}
 606
 607		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
 608
 609		goto read_table;
 610	}
 611
 612	/* then finally revision 0 */
 613	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 614					 ACPI_EWRD_WIFI_DATA_SIZE_REV0,
 615					 &tbl_rev);
 616	if (!IS_ERR(wifi_pkg)) {
 617		if (tbl_rev != 0) {
 618			ret = -EINVAL;
 619			goto out_free;
 620		}
 621
 622		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
 623
 624		goto read_table;
 625	}
 626
 627	ret = PTR_ERR(wifi_pkg);
 628	goto out_free;
 629
 630read_table:
 631	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
 632	    wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
 633		ret = -EINVAL;
 634		goto out_free;
 635	}
 636
 637	enabled = !!(wifi_pkg->package.elements[1].integer.value);
 638	n_profiles = wifi_pkg->package.elements[2].integer.value;
 639
 640	/*
 641	 * Check the validity of n_profiles.  The EWRD profiles start
 642	 * from index 1, so the maximum value allowed here is
 643	 * ACPI_SAR_PROFILES_NUM - 1.
 644	 */
 645	if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
 646		ret = -EINVAL;
 647		goto out_free;
 648	}
 649
 650	/* the tables start at element 3 */
 651	pos = 3;
 652
 653	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1);
 654	BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0);
 655
 656	/* parse non-cdb chains for all profiles */
 657	for (i = 0; i < n_profiles; i++) {
 658		union acpi_object *table = &wifi_pkg->package.elements[pos];
 659
 660		/* The EWRD profiles officially go from 2 to 4, but we
 661		 * save them in sar_profiles[1-3] (because we don't
 662		 * have profile 0).  So in the array we start from 1.
 663		 */
 664		ret = iwl_acpi_parse_chains_table(table,
 665						  fwrt->sar_profiles[i + 1].chains,
 666						  ACPI_SAR_NUM_CHAINS_REV0,
 667						  num_sub_bands);
 668		if (ret < 0)
 669			goto out_free;
 670
 671		/* go to the next table */
 672		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
 673	}
 674
 675	/* non-cdb table revisions */
 676	if (tbl_rev < 2)
 677		goto set_enabled;
 678
 679	/* parse cdb chains for all profiles */
 680	for (i = 0; i < n_profiles; i++) {
 681		struct iwl_sar_profile_chain *chains;
 682		union acpi_object *table;
 683
 684		table = &wifi_pkg->package.elements[pos];
 685		chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0];
 686		ret = iwl_acpi_parse_chains_table(table,
 687						  chains,
 688						  ACPI_SAR_NUM_CHAINS_REV0,
 689						  num_sub_bands);
 690		if (ret < 0)
 691			goto out_free;
 692
 693		/* go to the next table */
 694		pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
 695	}
 696
 697set_enabled:
 698	for (i = 0; i < n_profiles; i++)
 699		fwrt->sar_profiles[i + 1].enabled = enabled;
 700
 701out_free:
 702	kfree(data);
 703	return ret;
 704}
 705
 706int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
 707{
 708	union acpi_object *wifi_pkg, *data;
 709	int i, j, k, ret, tbl_rev;
 710	u8 num_bands, num_profiles;
 711	static const struct {
 712		u8 revisions;
 713		u8 bands;
 714		u8 profiles;
 715		u8 min_profiles;
 716	} rev_data[] = {
 717		{
 718			.revisions = BIT(3),
 719			.bands = ACPI_GEO_NUM_BANDS_REV2,
 720			.profiles = ACPI_NUM_GEO_PROFILES_REV3,
 721			.min_profiles = BIOS_GEO_MIN_PROFILE_NUM,
 722		},
 723		{
 724			.revisions = BIT(2),
 725			.bands = ACPI_GEO_NUM_BANDS_REV2,
 726			.profiles = ACPI_NUM_GEO_PROFILES,
 727		},
 728		{
 729			.revisions = BIT(0) | BIT(1),
 730			.bands = ACPI_GEO_NUM_BANDS_REV0,
 731			.profiles = ACPI_NUM_GEO_PROFILES,
 732		},
 733	};
 734	int idx;
 735	/* start from one to skip the domain */
 736	int entry_idx = 1;
 737
 738	BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);
 739	BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);
 740
 741	data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
 742	if (IS_ERR(data))
 743		return PTR_ERR(data);
 744
 745	/* read the highest revision we understand first */
 746	for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {
 747		/* min_profiles != 0 requires num_profiles header */
 748		u32 hdr_size = 1 + !!rev_data[idx].min_profiles;
 749		u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *
 750				   rev_data[idx].bands;
 751		u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;
 752		u32 min_size;
 753
 754		if (!rev_data[idx].min_profiles)
 755			min_size = max_size;
 756		else
 757			min_size = hdr_size +
 758				   profile_size * rev_data[idx].min_profiles;
 759
 760		wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,
 761						       min_size, max_size,
 762						       &tbl_rev);
 763		if (!IS_ERR(wifi_pkg)) {
 764			if (!(BIT(tbl_rev) & rev_data[idx].revisions))
 765				continue;
 766
 767			num_bands = rev_data[idx].bands;
 768			num_profiles = rev_data[idx].profiles;
 769
 770			if (rev_data[idx].min_profiles) {
 771				/* read header that says # of profiles */
 772				union acpi_object *entry;
 773
 774				entry = &wifi_pkg->package.elements[entry_idx];
 775				entry_idx++;
 776				if (entry->type != ACPI_TYPE_INTEGER ||
 777				    entry->integer.value > num_profiles ||
 778				    entry->integer.value <
 779					rev_data[idx].min_profiles) {
 780					ret = -EINVAL;
 781					goto out_free;
 782				}
 783
 784				/*
 785				 * Check to see if we received package count
 786				 * same as max # of profiles
 787				 */
 788				if (wifi_pkg->package.count !=
 789				    hdr_size + profile_size * num_profiles) {
 790					ret = -EINVAL;
 791					goto out_free;
 792				}
 793
 794				/* Number of valid profiles */
 795				num_profiles = entry->integer.value;
 796			}
 797			goto read_table;
 798		}
 799	}
 800
 801	if (idx < ARRAY_SIZE(rev_data))
 802		ret = PTR_ERR(wifi_pkg);
 803	else
 804		ret = -ENOENT;
 805	goto out_free;
 806
 807read_table:
 808	fwrt->geo_rev = tbl_rev;
 809	for (i = 0; i < num_profiles; i++) {
 810		for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) {
 811			union acpi_object *entry;
 812
 813			/*
 814			 * num_bands is either 2 or 3, if it's only 2 then
 815			 * fill the third band (6 GHz) with the values from
 816			 * 5 GHz (second band)
 817			 */
 818			if (j >= num_bands) {
 819				fwrt->geo_profiles[i].bands[j].max =
 820					fwrt->geo_profiles[i].bands[1].max;
 821			} else {
 822				entry = &wifi_pkg->package.elements[entry_idx];
 823				entry_idx++;
 824				if (entry->type != ACPI_TYPE_INTEGER ||
 825				    entry->integer.value > U8_MAX) {
 826					ret = -EINVAL;
 827					goto out_free;
 828				}
 829
 830				fwrt->geo_profiles[i].bands[j].max =
 831					entry->integer.value;
 832			}
 833
 834			for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) {
 835				/* same here as above */
 836				if (j >= num_bands) {
 837					fwrt->geo_profiles[i].bands[j].chains[k] =
 838						fwrt->geo_profiles[i].bands[1].chains[k];
 839				} else {
 840					entry = &wifi_pkg->package.elements[entry_idx];
 841					entry_idx++;
 842					if (entry->type != ACPI_TYPE_INTEGER ||
 843					    entry->integer.value > U8_MAX) {
 844						ret = -EINVAL;
 845						goto out_free;
 846					}
 847
 848					fwrt->geo_profiles[i].bands[j].chains[k] =
 849						entry->integer.value;
 850				}
 851			}
 852		}
 853	}
 854
 855	fwrt->geo_num_profiles = num_profiles;
 856	fwrt->geo_enabled = true;
 857	ret = 0;
 858out_free:
 859	kfree(data);
 860	return ret;
 861}
 862
 863int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
 864{
 865	union acpi_object *wifi_pkg, *data, *flags;
 866	int i, j, ret, tbl_rev, num_sub_bands = 0;
 867	int idx = 2;
 868
 869	data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);
 870	if (IS_ERR(data))
 871		return PTR_ERR(data);
 872
 873	/* try to read ppag table rev 1 to 4 (all have the same data size) */
 874	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 875				ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
 876
 877	if (!IS_ERR(wifi_pkg)) {
 878		if (tbl_rev >= 1 && tbl_rev <= 4) {
 879			num_sub_bands = IWL_NUM_SUB_BANDS_V2;
 880			IWL_DEBUG_RADIO(fwrt,
 881					"Reading PPAG table (tbl_rev=%d)\n",
 882					tbl_rev);
 883			goto read_table;
 884		} else {
 885			ret = -EINVAL;
 886			goto out_free;
 887		}
 888	}
 889
 890	/* try to read ppag table revision 0 */
 891	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 892			ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);
 893
 894	if (!IS_ERR(wifi_pkg)) {
 895		if (tbl_rev != 0) {
 896			ret = -EINVAL;
 897			goto out_free;
 898		}
 899		num_sub_bands = IWL_NUM_SUB_BANDS_V1;
 900		IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");
 901		goto read_table;
 902	}
 903
 904	ret = PTR_ERR(wifi_pkg);
 905	goto out_free;
 906
 907read_table:
 908	fwrt->ppag_bios_rev = tbl_rev;
 909	flags = &wifi_pkg->package.elements[1];
 910
 911	if (flags->type != ACPI_TYPE_INTEGER) {
 912		ret = -EINVAL;
 913		goto out_free;
 914	}
 915
 916	fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value,
 917						   fwrt->ppag_bios_rev);
 918
 919	/*
 920	 * read, verify gain values and save them into the PPAG table.
 921	 * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
 922	 * following sub-bands to High-Band (5GHz).
 923	 */
 924	for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
 925		for (j = 0; j < num_sub_bands; j++) {
 926			union acpi_object *ent;
 927
 928			ent = &wifi_pkg->package.elements[idx++];
 929			if (ent->type != ACPI_TYPE_INTEGER) {
 930				ret = -EINVAL;
 931				goto out_free;
 932			}
 933
 934			fwrt->ppag_chains[i].subbands[j] = ent->integer.value;
 935		}
 936	}
 937
 938	fwrt->ppag_bios_source = BIOS_SOURCE_ACPI;
 939	ret = 0;
 940
 941out_free:
 942	kfree(data);
 943	return ret;
 944}
 945
 946int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt)
 947{
 948	struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters;
 949	struct iwl_phy_specific_cfg tmp = {};
 950	union acpi_object *wifi_pkg, *data __free(kfree);
 951	int tbl_rev, i;
 952
 953	data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);
 954	if (IS_ERR(data))
 955		return PTR_ERR(data);
 956
 957	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 958					 ACPI_WPFC_WIFI_DATA_SIZE,
 959					 &tbl_rev);
 960	if (IS_ERR(wifi_pkg))
 961		return PTR_ERR(wifi_pkg);
 962
 963	if (tbl_rev != 0)
 964		return -EINVAL;
 965
 966	BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
 967		     ACPI_WPFC_WIFI_DATA_SIZE - 1);
 968
 969	for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
 970		if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)
 971			return -EINVAL;
 972		tmp.filter_cfg_chains[i] =
 973			cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);
 974	}
 975
 976	IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");
 977	*filters = tmp;
 978	return 0;
 979}
 980IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);
 981
 982void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt)
 983{
 984	union acpi_object *wifi_pkg, *data;
 985	int tbl_rev;
 986
 987	data = iwl_acpi_get_object(fwrt->dev, ACPI_GLAI_METHOD);
 988	if (IS_ERR(data))
 989		return;
 990
 991	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 992					 ACPI_GLAI_WIFI_DATA_SIZE,
 993					 &tbl_rev);
 994	if (IS_ERR(wifi_pkg))
 995		goto out_free;
 996
 997	if (tbl_rev != 0) {
 998		IWL_DEBUG_RADIO(fwrt, "Invalid GLAI revision: %d\n", tbl_rev);
 999		goto out_free;
1000	}
1001
1002	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
1003	    wifi_pkg->package.elements[1].integer.value > ACPI_GLAI_MAX_STATUS)
1004		goto out_free;
1005
1006	fwrt->uefi_tables_lock_status =
1007		wifi_pkg->package.elements[1].integer.value;
1008
1009	IWL_DEBUG_RADIO(fwrt,
1010			"Loaded UEFI WIFI GUID lock status: %d from ACPI\n",
1011			fwrt->uefi_tables_lock_status);
1012out_free:
1013	kfree(data);
1014}
1015IWL_EXPORT_SYMBOL(iwl_acpi_get_guid_lock_status);
1016
1017int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)
1018{
1019	union acpi_object *wifi_pkg, *data;
1020	int ret = -ENOENT;
1021	int tbl_rev;
1022
1023	data = iwl_acpi_get_object(fwrt->dev, ACPI_WBEM_METHOD);
1024	if (IS_ERR(data))
1025		return ret;
1026
1027	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1028					 ACPI_WBEM_WIFI_DATA_SIZE,
1029					 &tbl_rev);
1030	if (IS_ERR(wifi_pkg))
1031		goto out_free;
1032
1033	if (tbl_rev != IWL_ACPI_WBEM_REVISION) {
1034		IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI WBEM revision:%d\n",
1035				tbl_rev);
1036		goto out_free;
1037	}
1038
1039	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
1040		goto out_free;
1041
1042	*value = wifi_pkg->package.elements[1].integer.value &
1043		 IWL_ACPI_WBEM_REV0_MASK;
1044	IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from ACPI\n");
1045	ret = 0;
1046out_free:
1047	kfree(data);
1048	return ret;
1049}
1050
1051int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value)
1052{
1053	union acpi_object *wifi_pkg, *data;
1054	int ret = -ENOENT;
1055	int tbl_rev;
1056
1057	data = iwl_acpi_get_object(fwrt->dev, ACPI_DSBR_METHOD);
1058	if (IS_ERR(data))
1059		return ret;
1060
1061	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1062					 ACPI_DSBR_WIFI_DATA_SIZE,
1063					 &tbl_rev);
1064	if (IS_ERR(wifi_pkg))
1065		goto out_free;
1066
1067	if (tbl_rev != ACPI_DSBR_WIFI_DATA_REV) {
1068		IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI DSBR revision:%d\n",
1069				tbl_rev);
1070		goto out_free;
1071	}
1072
1073	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
1074		goto out_free;
1075
1076	*value = wifi_pkg->package.elements[1].integer.value;
1077	IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from ACPI value: 0x%x\n",
1078			*value);
1079	ret = 0;
1080out_free:
1081	kfree(data);
1082	return ret;
1083}
~/ Read-only file preview.