162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2017 Intel Deutschland GmbH
462306a36Sopenharmony_ci * Copyright (C) 2019-2023 Intel Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/uuid.h>
762306a36Sopenharmony_ci#include <linux/dmi.h>
862306a36Sopenharmony_ci#include "iwl-drv.h"
962306a36Sopenharmony_ci#include "iwl-debug.h"
1062306a36Sopenharmony_ci#include "acpi.h"
1162306a36Sopenharmony_ci#include "fw/runtime.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciconst guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
1462306a36Sopenharmony_ci				  0xA5, 0xB3, 0x1F, 0x73,
1562306a36Sopenharmony_ci				  0x8E, 0x28, 0x5A, 0xDE);
1662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_guid);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciconst guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29,
1962306a36Sopenharmony_ci				      0x81, 0x4F, 0x75, 0xE4,
2062306a36Sopenharmony_ci				      0xDD, 0x26, 0xB5, 0xFD);
2162306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_rfi_guid);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic const struct dmi_system_id dmi_ppag_approved_list[] = {
2462306a36Sopenharmony_ci	{ .ident = "HP",
2562306a36Sopenharmony_ci	  .matches = {
2662306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
2762306a36Sopenharmony_ci		},
2862306a36Sopenharmony_ci	},
2962306a36Sopenharmony_ci	{ .ident = "SAMSUNG",
3062306a36Sopenharmony_ci	  .matches = {
3162306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
3262306a36Sopenharmony_ci		},
3362306a36Sopenharmony_ci	},
3462306a36Sopenharmony_ci	{ .ident = "MSFT",
3562306a36Sopenharmony_ci	  .matches = {
3662306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
3762306a36Sopenharmony_ci		},
3862306a36Sopenharmony_ci	},
3962306a36Sopenharmony_ci	{ .ident = "ASUS",
4062306a36Sopenharmony_ci	  .matches = {
4162306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
4262306a36Sopenharmony_ci		},
4362306a36Sopenharmony_ci	},
4462306a36Sopenharmony_ci	{ .ident = "GOOGLE-HP",
4562306a36Sopenharmony_ci	  .matches = {
4662306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
4762306a36Sopenharmony_ci			DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
4862306a36Sopenharmony_ci		},
4962306a36Sopenharmony_ci	},
5062306a36Sopenharmony_ci	{ .ident = "GOOGLE-ASUS",
5162306a36Sopenharmony_ci	  .matches = {
5262306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
5362306a36Sopenharmony_ci			DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTek COMPUTER INC."),
5462306a36Sopenharmony_ci		},
5562306a36Sopenharmony_ci	},
5662306a36Sopenharmony_ci	{ .ident = "GOOGLE-SAMSUNG",
5762306a36Sopenharmony_ci	  .matches = {
5862306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Google"),
5962306a36Sopenharmony_ci			DMI_MATCH(DMI_BOARD_VENDOR, "SAMSUNG ELECTRONICS CO., LTD"),
6062306a36Sopenharmony_ci		},
6162306a36Sopenharmony_ci	},
6262306a36Sopenharmony_ci	{ .ident = "DELL",
6362306a36Sopenharmony_ci	  .matches = {
6462306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
6562306a36Sopenharmony_ci		},
6662306a36Sopenharmony_ci	},
6762306a36Sopenharmony_ci	{ .ident = "DELL",
6862306a36Sopenharmony_ci	  .matches = {
6962306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
7062306a36Sopenharmony_ci		},
7162306a36Sopenharmony_ci	},
7262306a36Sopenharmony_ci	{ .ident = "RAZER",
7362306a36Sopenharmony_ci	  .matches = {
7462306a36Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
7562306a36Sopenharmony_ci		},
7662306a36Sopenharmony_ci	},
7762306a36Sopenharmony_ci	{}
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic int iwl_acpi_get_handle(struct device *dev, acpi_string method,
8162306a36Sopenharmony_ci			       acpi_handle *ret_handle)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	acpi_handle root_handle;
8462306a36Sopenharmony_ci	acpi_status status;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	root_handle = ACPI_HANDLE(dev);
8762306a36Sopenharmony_ci	if (!root_handle) {
8862306a36Sopenharmony_ci		IWL_DEBUG_DEV_RADIO(dev,
8962306a36Sopenharmony_ci				    "ACPI: Could not retrieve root port handle\n");
9062306a36Sopenharmony_ci		return -ENOENT;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	status = acpi_get_handle(root_handle, method, ret_handle);
9462306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
9562306a36Sopenharmony_ci		IWL_DEBUG_DEV_RADIO(dev,
9662306a36Sopenharmony_ci				    "ACPI: %s method not found\n", method);
9762306a36Sopenharmony_ci		return -ENOENT;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void *iwl_acpi_get_object(struct device *dev, acpi_string method)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
10562306a36Sopenharmony_ci	acpi_handle handle;
10662306a36Sopenharmony_ci	acpi_status status;
10762306a36Sopenharmony_ci	int ret;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	ret = iwl_acpi_get_handle(dev, method, &handle);
11062306a36Sopenharmony_ci	if (ret)
11162306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	/* Call the method with no arguments */
11462306a36Sopenharmony_ci	status = acpi_evaluate_object(handle, NULL, NULL, &buf);
11562306a36Sopenharmony_ci	if (ACPI_FAILURE(status)) {
11662306a36Sopenharmony_ci		IWL_DEBUG_DEV_RADIO(dev,
11762306a36Sopenharmony_ci				    "ACPI: %s method invocation failed (status: 0x%x)\n",
11862306a36Sopenharmony_ci				    method, status);
11962306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci	return buf.pointer;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * Generic function for evaluating a method defined in the device specific
12662306a36Sopenharmony_ci * method (DSM) interface. The returned acpi object must be freed by calling
12762306a36Sopenharmony_ci * function.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_cistatic void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
13062306a36Sopenharmony_ci				     union acpi_object *args,
13162306a36Sopenharmony_ci				     const guid_t *guid)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	union acpi_object *obj;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), guid, rev, func,
13662306a36Sopenharmony_ci				args);
13762306a36Sopenharmony_ci	if (!obj) {
13862306a36Sopenharmony_ci		IWL_DEBUG_DEV_RADIO(dev,
13962306a36Sopenharmony_ci				    "ACPI: DSM method invocation failed (rev: %d, func:%d)\n",
14062306a36Sopenharmony_ci				    rev, func);
14162306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	return obj;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci/*
14762306a36Sopenharmony_ci * Generic function to evaluate a DSM with no arguments
14862306a36Sopenharmony_ci * and an integer return value,
14962306a36Sopenharmony_ci * (as an integer object or inside a buffer object),
15062306a36Sopenharmony_ci * verify and assign the value in the "value" parameter.
15162306a36Sopenharmony_ci * return 0 in success and the appropriate errno otherwise.
15262306a36Sopenharmony_ci */
15362306a36Sopenharmony_cistatic int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func,
15462306a36Sopenharmony_ci				    const guid_t *guid, u64 *value,
15562306a36Sopenharmony_ci				    size_t expected_size)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	union acpi_object *obj;
15862306a36Sopenharmony_ci	int ret = 0;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid);
16162306a36Sopenharmony_ci	if (IS_ERR(obj)) {
16262306a36Sopenharmony_ci		IWL_DEBUG_DEV_RADIO(dev,
16362306a36Sopenharmony_ci				    "Failed to get  DSM object. func= %d\n",
16462306a36Sopenharmony_ci				    func);
16562306a36Sopenharmony_ci		return -ENOENT;
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (obj->type == ACPI_TYPE_INTEGER) {
16962306a36Sopenharmony_ci		*value = obj->integer.value;
17062306a36Sopenharmony_ci	} else if (obj->type == ACPI_TYPE_BUFFER) {
17162306a36Sopenharmony_ci		__le64 le_value = 0;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		if (WARN_ON_ONCE(expected_size > sizeof(le_value)))
17462306a36Sopenharmony_ci			return -EINVAL;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		/* if the buffer size doesn't match the expected size */
17762306a36Sopenharmony_ci		if (obj->buffer.length != expected_size)
17862306a36Sopenharmony_ci			IWL_DEBUG_DEV_RADIO(dev,
17962306a36Sopenharmony_ci					    "ACPI: DSM invalid buffer size, padding or truncating (%d)\n",
18062306a36Sopenharmony_ci					    obj->buffer.length);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		 /* assuming LE from Intel BIOS spec */
18362306a36Sopenharmony_ci		memcpy(&le_value, obj->buffer.pointer,
18462306a36Sopenharmony_ci		       min_t(size_t, expected_size, (size_t)obj->buffer.length));
18562306a36Sopenharmony_ci		*value = le64_to_cpu(le_value);
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		IWL_DEBUG_DEV_RADIO(dev,
18862306a36Sopenharmony_ci				    "ACPI: DSM method did not return a valid object, type=%d\n",
18962306a36Sopenharmony_ci				    obj->type);
19062306a36Sopenharmony_ci		ret = -EINVAL;
19162306a36Sopenharmony_ci		goto out;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	IWL_DEBUG_DEV_RADIO(dev,
19562306a36Sopenharmony_ci			    "ACPI: DSM method evaluated: func=%d, ret=%d\n",
19662306a36Sopenharmony_ci			    func, ret);
19762306a36Sopenharmony_ciout:
19862306a36Sopenharmony_ci	ACPI_FREE(obj);
19962306a36Sopenharmony_ci	return ret;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci/*
20362306a36Sopenharmony_ci * Evaluate a DSM with no arguments and a u8 return value,
20462306a36Sopenharmony_ci */
20562306a36Sopenharmony_ciint iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func,
20662306a36Sopenharmony_ci			const guid_t *guid, u8 *value)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	int ret;
20962306a36Sopenharmony_ci	u64 val;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ret = iwl_acpi_get_dsm_integer(dev, rev, func,
21262306a36Sopenharmony_ci				       guid, &val, sizeof(u8));
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (ret < 0)
21562306a36Sopenharmony_ci		return ret;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* cast val (u64) to be u8 */
21862306a36Sopenharmony_ci	*value = (u8)val;
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci/*
22462306a36Sopenharmony_ci * Evaluate a DSM with no arguments and a u32 return value,
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_ciint iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func,
22762306a36Sopenharmony_ci			 const guid_t *guid, u32 *value)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	int ret;
23062306a36Sopenharmony_ci	u64 val;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	ret = iwl_acpi_get_dsm_integer(dev, rev, func,
23362306a36Sopenharmony_ci				       guid, &val, sizeof(u32));
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (ret < 0)
23662306a36Sopenharmony_ci		return ret;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* cast val (u64) to be u32 */
23962306a36Sopenharmony_ci	*value = (u32)val;
24062306a36Sopenharmony_ci	return 0;
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u32);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic union acpi_object *
24562306a36Sopenharmony_ciiwl_acpi_get_wifi_pkg_range(struct device *dev,
24662306a36Sopenharmony_ci			    union acpi_object *data,
24762306a36Sopenharmony_ci			    int min_data_size,
24862306a36Sopenharmony_ci			    int max_data_size,
24962306a36Sopenharmony_ci			    int *tbl_rev)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	int i;
25262306a36Sopenharmony_ci	union acpi_object *wifi_pkg;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	/*
25562306a36Sopenharmony_ci	 * We need at least one entry in the wifi package that
25662306a36Sopenharmony_ci	 * describes the domain, and one more entry, otherwise there's
25762306a36Sopenharmony_ci	 * no point in reading it.
25862306a36Sopenharmony_ci	 */
25962306a36Sopenharmony_ci	if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))
26062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/*
26362306a36Sopenharmony_ci	 * We need at least two packages, one for the revision and one
26462306a36Sopenharmony_ci	 * for the data itself.  Also check that the revision is valid
26562306a36Sopenharmony_ci	 * (i.e. it is an integer (each caller has to check by itself
26662306a36Sopenharmony_ci	 * if the returned revision is supported)).
26762306a36Sopenharmony_ci	 */
26862306a36Sopenharmony_ci	if (data->type != ACPI_TYPE_PACKAGE ||
26962306a36Sopenharmony_ci	    data->package.count < 2 ||
27062306a36Sopenharmony_ci	    data->package.elements[0].type != ACPI_TYPE_INTEGER) {
27162306a36Sopenharmony_ci		IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");
27262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	*tbl_rev = data->package.elements[0].integer.value;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* loop through all the packages to find the one for WiFi */
27862306a36Sopenharmony_ci	for (i = 1; i < data->package.count; i++) {
27962306a36Sopenharmony_ci		union acpi_object *domain;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		wifi_pkg = &data->package.elements[i];
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		/* skip entries that are not a package with the right size */
28462306a36Sopenharmony_ci		if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
28562306a36Sopenharmony_ci		    wifi_pkg->package.count < min_data_size ||
28662306a36Sopenharmony_ci		    wifi_pkg->package.count > max_data_size)
28762306a36Sopenharmony_ci			continue;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		domain = &wifi_pkg->package.elements[0];
29062306a36Sopenharmony_ci		if (domain->type == ACPI_TYPE_INTEGER &&
29162306a36Sopenharmony_ci		    domain->integer.value == ACPI_WIFI_DOMAIN)
29262306a36Sopenharmony_ci			goto found;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return ERR_PTR(-ENOENT);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cifound:
29862306a36Sopenharmony_ci	return wifi_pkg;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic union acpi_object *
30262306a36Sopenharmony_ciiwl_acpi_get_wifi_pkg(struct device *dev,
30362306a36Sopenharmony_ci		      union acpi_object *data,
30462306a36Sopenharmony_ci		      int data_size, int *tbl_rev)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size,
30762306a36Sopenharmony_ci					   tbl_rev);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ciint iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt,
31262306a36Sopenharmony_ci		     union iwl_tas_config_cmd *cmd, int fw_ver)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *data;
31562306a36Sopenharmony_ci	int ret, tbl_rev, i, block_list_size, enabled;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
31862306a36Sopenharmony_ci	if (IS_ERR(data))
31962306a36Sopenharmony_ci		return PTR_ERR(data);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* try to read wtas table revision 1 or revision 0*/
32262306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
32362306a36Sopenharmony_ci					 ACPI_WTAS_WIFI_DATA_SIZE,
32462306a36Sopenharmony_ci					 &tbl_rev);
32562306a36Sopenharmony_ci	if (IS_ERR(wifi_pkg)) {
32662306a36Sopenharmony_ci		ret = PTR_ERR(wifi_pkg);
32762306a36Sopenharmony_ci		goto out_free;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (tbl_rev == 1 && wifi_pkg->package.elements[1].type ==
33162306a36Sopenharmony_ci		ACPI_TYPE_INTEGER) {
33262306a36Sopenharmony_ci		u32 tas_selection =
33362306a36Sopenharmony_ci			(u32)wifi_pkg->package.elements[1].integer.value;
33462306a36Sopenharmony_ci		u16 override_iec =
33562306a36Sopenharmony_ci			(tas_selection & ACPI_WTAS_OVERRIDE_IEC_MSK) >> ACPI_WTAS_OVERRIDE_IEC_POS;
33662306a36Sopenharmony_ci		u16 enabled_iec = (tas_selection & ACPI_WTAS_ENABLE_IEC_MSK) >>
33762306a36Sopenharmony_ci			ACPI_WTAS_ENABLE_IEC_POS;
33862306a36Sopenharmony_ci		u8 usa_tas_uhb = (tas_selection & ACPI_WTAS_USA_UHB_MSK) >> ACPI_WTAS_USA_UHB_POS;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		enabled = tas_selection & ACPI_WTAS_ENABLED_MSK;
34262306a36Sopenharmony_ci		if (fw_ver <= 3) {
34362306a36Sopenharmony_ci			cmd->v3.override_tas_iec = cpu_to_le16(override_iec);
34462306a36Sopenharmony_ci			cmd->v3.enable_tas_iec = cpu_to_le16(enabled_iec);
34562306a36Sopenharmony_ci		} else {
34662306a36Sopenharmony_ci			cmd->v4.usa_tas_uhb_allowed = usa_tas_uhb;
34762306a36Sopenharmony_ci			cmd->v4.override_tas_iec = (u8)override_iec;
34862306a36Sopenharmony_ci			cmd->v4.enable_tas_iec = (u8)enabled_iec;
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	} else if (tbl_rev == 0 &&
35262306a36Sopenharmony_ci		wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) {
35362306a36Sopenharmony_ci		enabled = !!wifi_pkg->package.elements[1].integer.value;
35462306a36Sopenharmony_ci	} else {
35562306a36Sopenharmony_ci		ret = -EINVAL;
35662306a36Sopenharmony_ci		goto out_free;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (!enabled) {
36062306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");
36162306a36Sopenharmony_ci		ret = 0;
36262306a36Sopenharmony_ci		goto out_free;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);
36662306a36Sopenharmony_ci	if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
36762306a36Sopenharmony_ci	    wifi_pkg->package.elements[2].integer.value >
36862306a36Sopenharmony_ci	    APCI_WTAS_BLACK_LIST_MAX) {
36962306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
37062306a36Sopenharmony_ci				wifi_pkg->package.elements[2].integer.value);
37162306a36Sopenharmony_ci		ret = -EINVAL;
37262306a36Sopenharmony_ci		goto out_free;
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	block_list_size = wifi_pkg->package.elements[2].integer.value;
37562306a36Sopenharmony_ci	cmd->v4.block_list_size = cpu_to_le32(block_list_size);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);
37862306a36Sopenharmony_ci	if (block_list_size > APCI_WTAS_BLACK_LIST_MAX) {
37962306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n",
38062306a36Sopenharmony_ci				block_list_size);
38162306a36Sopenharmony_ci		ret = -EINVAL;
38262306a36Sopenharmony_ci		goto out_free;
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	for (i = 0; i < block_list_size; i++) {
38662306a36Sopenharmony_ci		u32 country;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci		if (wifi_pkg->package.elements[3 + i].type !=
38962306a36Sopenharmony_ci		    ACPI_TYPE_INTEGER) {
39062306a36Sopenharmony_ci			IWL_DEBUG_RADIO(fwrt,
39162306a36Sopenharmony_ci					"TAS invalid array elem %d\n", 3 + i);
39262306a36Sopenharmony_ci			ret = -EINVAL;
39362306a36Sopenharmony_ci			goto out_free;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci		country = wifi_pkg->package.elements[3 + i].integer.value;
39762306a36Sopenharmony_ci		cmd->v4.block_list_array[i] = cpu_to_le32(country);
39862306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	ret = 1;
40262306a36Sopenharmony_ciout_free:
40362306a36Sopenharmony_ci	kfree(data);
40462306a36Sopenharmony_ci	return ret;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_tas);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ciint iwl_acpi_get_mcc(struct device *dev, char *mcc)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *data;
41162306a36Sopenharmony_ci	u32 mcc_val;
41262306a36Sopenharmony_ci	int ret, tbl_rev;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
41562306a36Sopenharmony_ci	if (IS_ERR(data))
41662306a36Sopenharmony_ci		return PTR_ERR(data);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE,
41962306a36Sopenharmony_ci					 &tbl_rev);
42062306a36Sopenharmony_ci	if (IS_ERR(wifi_pkg)) {
42162306a36Sopenharmony_ci		ret = PTR_ERR(wifi_pkg);
42262306a36Sopenharmony_ci		goto out_free;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
42662306a36Sopenharmony_ci	    tbl_rev != 0) {
42762306a36Sopenharmony_ci		ret = -EINVAL;
42862306a36Sopenharmony_ci		goto out_free;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	mcc_val = wifi_pkg->package.elements[1].integer.value;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	mcc[0] = (mcc_val >> 8) & 0xff;
43462306a36Sopenharmony_ci	mcc[1] = mcc_val & 0xff;
43562306a36Sopenharmony_ci	mcc[2] = '\0';
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	ret = 0;
43862306a36Sopenharmony_ciout_free:
43962306a36Sopenharmony_ci	kfree(data);
44062306a36Sopenharmony_ci	return ret;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_mcc);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ciu64 iwl_acpi_get_pwr_limit(struct device *dev)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	union acpi_object *data, *wifi_pkg;
44762306a36Sopenharmony_ci	u64 dflt_pwr_limit;
44862306a36Sopenharmony_ci	int tbl_rev;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
45162306a36Sopenharmony_ci	if (IS_ERR(data)) {
45262306a36Sopenharmony_ci		dflt_pwr_limit = 0;
45362306a36Sopenharmony_ci		goto out;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
45762306a36Sopenharmony_ci					 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
45862306a36Sopenharmony_ci	if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
45962306a36Sopenharmony_ci	    wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
46062306a36Sopenharmony_ci		dflt_pwr_limit = 0;
46162306a36Sopenharmony_ci		goto out_free;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
46562306a36Sopenharmony_ciout_free:
46662306a36Sopenharmony_ci	kfree(data);
46762306a36Sopenharmony_ciout:
46862306a36Sopenharmony_ci	return dflt_pwr_limit;
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ciint iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *data;
47562306a36Sopenharmony_ci	int ret, tbl_rev;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
47862306a36Sopenharmony_ci	if (IS_ERR(data))
47962306a36Sopenharmony_ci		return PTR_ERR(data);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE,
48262306a36Sopenharmony_ci					 &tbl_rev);
48362306a36Sopenharmony_ci	if (IS_ERR(wifi_pkg)) {
48462306a36Sopenharmony_ci		ret = PTR_ERR(wifi_pkg);
48562306a36Sopenharmony_ci		goto out_free;
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
48962306a36Sopenharmony_ci	    tbl_rev != 0) {
49062306a36Sopenharmony_ci		ret = -EINVAL;
49162306a36Sopenharmony_ci		goto out_free;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	*extl_clk = wifi_pkg->package.elements[1].integer.value;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	ret = 0;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ciout_free:
49962306a36Sopenharmony_ci	kfree(data);
50062306a36Sopenharmony_ci	return ret;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_eckv);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int iwl_sar_set_profile(union acpi_object *table,
50562306a36Sopenharmony_ci			       struct iwl_sar_profile *profile,
50662306a36Sopenharmony_ci			       bool enabled, u8 num_chains, u8 num_sub_bands)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	int i, j, idx = 0;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/*
51162306a36Sopenharmony_ci	 * The table from ACPI is flat, but we store it in a
51262306a36Sopenharmony_ci	 * structured array.
51362306a36Sopenharmony_ci	 */
51462306a36Sopenharmony_ci	for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV2; i++) {
51562306a36Sopenharmony_ci		for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS_REV2; j++) {
51662306a36Sopenharmony_ci			/* if we don't have the values, use the default */
51762306a36Sopenharmony_ci			if (i >= num_chains || j >= num_sub_bands) {
51862306a36Sopenharmony_ci				profile->chains[i].subbands[j] = 0;
51962306a36Sopenharmony_ci			} else {
52062306a36Sopenharmony_ci				if (table[idx].type != ACPI_TYPE_INTEGER ||
52162306a36Sopenharmony_ci				    table[idx].integer.value > U8_MAX)
52262306a36Sopenharmony_ci					return -EINVAL;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci				profile->chains[i].subbands[j] =
52562306a36Sopenharmony_ci					table[idx].integer.value;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci				idx++;
52862306a36Sopenharmony_ci			}
52962306a36Sopenharmony_ci		}
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* Only if all values were valid can the profile be enabled */
53362306a36Sopenharmony_ci	profile->enabled = enabled;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
53962306a36Sopenharmony_ci			      __le16 *per_chain, u32 n_subbands,
54062306a36Sopenharmony_ci			      int prof_a, int prof_b)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	int profs[ACPI_SAR_NUM_CHAINS_REV0] = { prof_a, prof_b };
54362306a36Sopenharmony_ci	int i, j;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV0; i++) {
54662306a36Sopenharmony_ci		struct iwl_sar_profile *prof;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		/* don't allow SAR to be disabled (profile 0 means disable) */
54962306a36Sopenharmony_ci		if (profs[i] == 0)
55062306a36Sopenharmony_ci			return -EPERM;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		/* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */
55362306a36Sopenharmony_ci		if (profs[i] > ACPI_SAR_PROFILE_NUM)
55462306a36Sopenharmony_ci			return -EINVAL;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci		/* profiles go from 1 to 4, so decrement to access the array */
55762306a36Sopenharmony_ci		prof = &fwrt->sar_profiles[profs[i] - 1];
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		/* if the profile is disabled, do nothing */
56062306a36Sopenharmony_ci		if (!prof->enabled) {
56162306a36Sopenharmony_ci			IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n",
56262306a36Sopenharmony_ci					profs[i]);
56362306a36Sopenharmony_ci			/*
56462306a36Sopenharmony_ci			 * if one of the profiles is disabled, we
56562306a36Sopenharmony_ci			 * ignore all of them and return 1 to
56662306a36Sopenharmony_ci			 * differentiate disabled from other failures.
56762306a36Sopenharmony_ci			 */
56862306a36Sopenharmony_ci			return 1;
56962306a36Sopenharmony_ci		}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci		IWL_DEBUG_INFO(fwrt,
57262306a36Sopenharmony_ci			       "SAR EWRD: chain %d profile index %d\n",
57362306a36Sopenharmony_ci			       i, profs[i]);
57462306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "  Chain[%d]:\n", i);
57562306a36Sopenharmony_ci		for (j = 0; j < n_subbands; j++) {
57662306a36Sopenharmony_ci			per_chain[i * n_subbands + j] =
57762306a36Sopenharmony_ci				cpu_to_le16(prof->chains[i].subbands[j]);
57862306a36Sopenharmony_ci			IWL_DEBUG_RADIO(fwrt, "    Band[%d] = %d * .125dBm\n",
57962306a36Sopenharmony_ci					j, prof->chains[i].subbands[j]);
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci	}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	return 0;
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ciint iwl_sar_select_profile(struct iwl_fw_runtime *fwrt,
58762306a36Sopenharmony_ci			   __le16 *per_chain, u32 n_tables, u32 n_subbands,
58862306a36Sopenharmony_ci			   int prof_a, int prof_b)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	int i, ret = 0;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	for (i = 0; i < n_tables; i++) {
59362306a36Sopenharmony_ci		ret = iwl_sar_fill_table(fwrt,
59462306a36Sopenharmony_ci			 &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAINS_REV0],
59562306a36Sopenharmony_ci			 n_subbands, prof_a, prof_b);
59662306a36Sopenharmony_ci		if (ret)
59762306a36Sopenharmony_ci			break;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	return ret;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_select_profile);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ciint iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *table, *data;
60762306a36Sopenharmony_ci	int ret, tbl_rev;
60862306a36Sopenharmony_ci	u32 flags;
60962306a36Sopenharmony_ci	u8 num_chains, num_sub_bands;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
61262306a36Sopenharmony_ci	if (IS_ERR(data))
61362306a36Sopenharmony_ci		return PTR_ERR(data);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* start by trying to read revision 2 */
61662306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
61762306a36Sopenharmony_ci					 ACPI_WRDS_WIFI_DATA_SIZE_REV2,
61862306a36Sopenharmony_ci					 &tbl_rev);
61962306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
62062306a36Sopenharmony_ci		if (tbl_rev != 2) {
62162306a36Sopenharmony_ci			ret = -EINVAL;
62262306a36Sopenharmony_ci			goto out_free;
62362306a36Sopenharmony_ci		}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		num_chains = ACPI_SAR_NUM_CHAINS_REV2;
62662306a36Sopenharmony_ci		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		goto read_table;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/* then try revision 1 */
63262306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
63362306a36Sopenharmony_ci					 ACPI_WRDS_WIFI_DATA_SIZE_REV1,
63462306a36Sopenharmony_ci					 &tbl_rev);
63562306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
63662306a36Sopenharmony_ci		if (tbl_rev != 1) {
63762306a36Sopenharmony_ci			ret = -EINVAL;
63862306a36Sopenharmony_ci			goto out_free;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		num_chains = ACPI_SAR_NUM_CHAINS_REV1;
64262306a36Sopenharmony_ci		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci		goto read_table;
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/* then finally revision 0 */
64862306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
64962306a36Sopenharmony_ci					 ACPI_WRDS_WIFI_DATA_SIZE_REV0,
65062306a36Sopenharmony_ci					 &tbl_rev);
65162306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
65262306a36Sopenharmony_ci		if (tbl_rev != 0) {
65362306a36Sopenharmony_ci			ret = -EINVAL;
65462306a36Sopenharmony_ci			goto out_free;
65562306a36Sopenharmony_ci		}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci		num_chains = ACPI_SAR_NUM_CHAINS_REV0;
65862306a36Sopenharmony_ci		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		goto read_table;
66162306a36Sopenharmony_ci	}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	ret = PTR_ERR(wifi_pkg);
66462306a36Sopenharmony_ci	goto out_free;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ciread_table:
66762306a36Sopenharmony_ci	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
66862306a36Sopenharmony_ci		ret = -EINVAL;
66962306a36Sopenharmony_ci		goto out_free;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	flags = wifi_pkg->package.elements[1].integer.value;
67562306a36Sopenharmony_ci	fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* position of the actual table */
67862306a36Sopenharmony_ci	table = &wifi_pkg->package.elements[2];
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	/* The profile from WRDS is officially profile 1, but goes
68162306a36Sopenharmony_ci	 * into sar_profiles[0] (because we don't have a profile 0).
68262306a36Sopenharmony_ci	 */
68362306a36Sopenharmony_ci	ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0],
68462306a36Sopenharmony_ci				  flags & IWL_SAR_ENABLE_MSK,
68562306a36Sopenharmony_ci				  num_chains, num_sub_bands);
68662306a36Sopenharmony_ciout_free:
68762306a36Sopenharmony_ci	kfree(data);
68862306a36Sopenharmony_ci	return ret;
68962306a36Sopenharmony_ci}
69062306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table);
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ciint iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *data;
69562306a36Sopenharmony_ci	bool enabled;
69662306a36Sopenharmony_ci	int i, n_profiles, tbl_rev, pos;
69762306a36Sopenharmony_ci	int ret = 0;
69862306a36Sopenharmony_ci	u8 num_chains, num_sub_bands;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
70162306a36Sopenharmony_ci	if (IS_ERR(data))
70262306a36Sopenharmony_ci		return PTR_ERR(data);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	/* start by trying to read revision 2 */
70562306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
70662306a36Sopenharmony_ci					 ACPI_EWRD_WIFI_DATA_SIZE_REV2,
70762306a36Sopenharmony_ci					 &tbl_rev);
70862306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
70962306a36Sopenharmony_ci		if (tbl_rev != 2) {
71062306a36Sopenharmony_ci			ret = -EINVAL;
71162306a36Sopenharmony_ci			goto out_free;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci		num_chains = ACPI_SAR_NUM_CHAINS_REV2;
71562306a36Sopenharmony_ci		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci		goto read_table;
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* then try revision 1 */
72162306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
72262306a36Sopenharmony_ci					 ACPI_EWRD_WIFI_DATA_SIZE_REV1,
72362306a36Sopenharmony_ci					 &tbl_rev);
72462306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
72562306a36Sopenharmony_ci		if (tbl_rev != 1) {
72662306a36Sopenharmony_ci			ret = -EINVAL;
72762306a36Sopenharmony_ci			goto out_free;
72862306a36Sopenharmony_ci		}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		num_chains = ACPI_SAR_NUM_CHAINS_REV1;
73162306a36Sopenharmony_ci		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		goto read_table;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* then finally revision 0 */
73762306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
73862306a36Sopenharmony_ci					 ACPI_EWRD_WIFI_DATA_SIZE_REV0,
73962306a36Sopenharmony_ci					 &tbl_rev);
74062306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
74162306a36Sopenharmony_ci		if (tbl_rev != 0) {
74262306a36Sopenharmony_ci			ret = -EINVAL;
74362306a36Sopenharmony_ci			goto out_free;
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci		num_chains = ACPI_SAR_NUM_CHAINS_REV0;
74762306a36Sopenharmony_ci		num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci		goto read_table;
75062306a36Sopenharmony_ci	}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	ret = PTR_ERR(wifi_pkg);
75362306a36Sopenharmony_ci	goto out_free;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ciread_table:
75662306a36Sopenharmony_ci	if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
75762306a36Sopenharmony_ci	    wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
75862306a36Sopenharmony_ci		ret = -EINVAL;
75962306a36Sopenharmony_ci		goto out_free;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	enabled = !!(wifi_pkg->package.elements[1].integer.value);
76362306a36Sopenharmony_ci	n_profiles = wifi_pkg->package.elements[2].integer.value;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	/*
76662306a36Sopenharmony_ci	 * Check the validity of n_profiles.  The EWRD profiles start
76762306a36Sopenharmony_ci	 * from index 1, so the maximum value allowed here is
76862306a36Sopenharmony_ci	 * ACPI_SAR_PROFILES_NUM - 1.
76962306a36Sopenharmony_ci	 */
77062306a36Sopenharmony_ci	if (n_profiles >= ACPI_SAR_PROFILE_NUM) {
77162306a36Sopenharmony_ci		ret = -EINVAL;
77262306a36Sopenharmony_ci		goto out_free;
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/* the tables start at element 3 */
77662306a36Sopenharmony_ci	pos = 3;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	for (i = 0; i < n_profiles; i++) {
77962306a36Sopenharmony_ci		/* The EWRD profiles officially go from 2 to 4, but we
78062306a36Sopenharmony_ci		 * save them in sar_profiles[1-3] (because we don't
78162306a36Sopenharmony_ci		 * have profile 0).  So in the array we start from 1.
78262306a36Sopenharmony_ci		 */
78362306a36Sopenharmony_ci		ret = iwl_sar_set_profile(&wifi_pkg->package.elements[pos],
78462306a36Sopenharmony_ci					  &fwrt->sar_profiles[i + 1], enabled,
78562306a36Sopenharmony_ci					  num_chains, num_sub_bands);
78662306a36Sopenharmony_ci		if (ret < 0)
78762306a36Sopenharmony_ci			break;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		/* go to the next table */
79062306a36Sopenharmony_ci		pos += num_chains * num_sub_bands;
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ciout_free:
79462306a36Sopenharmony_ci	kfree(data);
79562306a36Sopenharmony_ci	return ret;
79662306a36Sopenharmony_ci}
79762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ciint iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
80062306a36Sopenharmony_ci{
80162306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *data;
80262306a36Sopenharmony_ci	int i, j, k, ret, tbl_rev;
80362306a36Sopenharmony_ci	u8 num_bands, num_profiles;
80462306a36Sopenharmony_ci	static const struct {
80562306a36Sopenharmony_ci		u8 revisions;
80662306a36Sopenharmony_ci		u8 bands;
80762306a36Sopenharmony_ci		u8 profiles;
80862306a36Sopenharmony_ci		u8 min_profiles;
80962306a36Sopenharmony_ci	} rev_data[] = {
81062306a36Sopenharmony_ci		{
81162306a36Sopenharmony_ci			.revisions = BIT(3),
81262306a36Sopenharmony_ci			.bands = ACPI_GEO_NUM_BANDS_REV2,
81362306a36Sopenharmony_ci			.profiles = ACPI_NUM_GEO_PROFILES_REV3,
81462306a36Sopenharmony_ci			.min_profiles = 3,
81562306a36Sopenharmony_ci		},
81662306a36Sopenharmony_ci		{
81762306a36Sopenharmony_ci			.revisions = BIT(2),
81862306a36Sopenharmony_ci			.bands = ACPI_GEO_NUM_BANDS_REV2,
81962306a36Sopenharmony_ci			.profiles = ACPI_NUM_GEO_PROFILES,
82062306a36Sopenharmony_ci		},
82162306a36Sopenharmony_ci		{
82262306a36Sopenharmony_ci			.revisions = BIT(0) | BIT(1),
82362306a36Sopenharmony_ci			.bands = ACPI_GEO_NUM_BANDS_REV0,
82462306a36Sopenharmony_ci			.profiles = ACPI_NUM_GEO_PROFILES,
82562306a36Sopenharmony_ci		},
82662306a36Sopenharmony_ci	};
82762306a36Sopenharmony_ci	int idx;
82862306a36Sopenharmony_ci	/* start from one to skip the domain */
82962306a36Sopenharmony_ci	int entry_idx = 1;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);
83262306a36Sopenharmony_ci	BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
83562306a36Sopenharmony_ci	if (IS_ERR(data))
83662306a36Sopenharmony_ci		return PTR_ERR(data);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* read the highest revision we understand first */
83962306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {
84062306a36Sopenharmony_ci		/* min_profiles != 0 requires num_profiles header */
84162306a36Sopenharmony_ci		u32 hdr_size = 1 + !!rev_data[idx].min_profiles;
84262306a36Sopenharmony_ci		u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *
84362306a36Sopenharmony_ci				   rev_data[idx].bands;
84462306a36Sopenharmony_ci		u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;
84562306a36Sopenharmony_ci		u32 min_size;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci		if (!rev_data[idx].min_profiles)
84862306a36Sopenharmony_ci			min_size = max_size;
84962306a36Sopenharmony_ci		else
85062306a36Sopenharmony_ci			min_size = hdr_size +
85162306a36Sopenharmony_ci				   profile_size * rev_data[idx].min_profiles;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci		wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,
85462306a36Sopenharmony_ci						       min_size, max_size,
85562306a36Sopenharmony_ci						       &tbl_rev);
85662306a36Sopenharmony_ci		if (!IS_ERR(wifi_pkg)) {
85762306a36Sopenharmony_ci			if (!(BIT(tbl_rev) & rev_data[idx].revisions))
85862306a36Sopenharmony_ci				continue;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci			num_bands = rev_data[idx].bands;
86162306a36Sopenharmony_ci			num_profiles = rev_data[idx].profiles;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci			if (rev_data[idx].min_profiles) {
86462306a36Sopenharmony_ci				/* read header that says # of profiles */
86562306a36Sopenharmony_ci				union acpi_object *entry;
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci				entry = &wifi_pkg->package.elements[entry_idx];
86862306a36Sopenharmony_ci				entry_idx++;
86962306a36Sopenharmony_ci				if (entry->type != ACPI_TYPE_INTEGER ||
87062306a36Sopenharmony_ci				    entry->integer.value > num_profiles) {
87162306a36Sopenharmony_ci					ret = -EINVAL;
87262306a36Sopenharmony_ci					goto out_free;
87362306a36Sopenharmony_ci				}
87462306a36Sopenharmony_ci				num_profiles = entry->integer.value;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci				/*
87762306a36Sopenharmony_ci				 * this also validates >= min_profiles since we
87862306a36Sopenharmony_ci				 * otherwise wouldn't have gotten the data when
87962306a36Sopenharmony_ci				 * looking up in ACPI
88062306a36Sopenharmony_ci				 */
88162306a36Sopenharmony_ci				if (wifi_pkg->package.count !=
88262306a36Sopenharmony_ci				    hdr_size + profile_size * num_profiles) {
88362306a36Sopenharmony_ci					ret = -EINVAL;
88462306a36Sopenharmony_ci					goto out_free;
88562306a36Sopenharmony_ci				}
88662306a36Sopenharmony_ci			}
88762306a36Sopenharmony_ci			goto read_table;
88862306a36Sopenharmony_ci		}
88962306a36Sopenharmony_ci	}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	if (idx < ARRAY_SIZE(rev_data))
89262306a36Sopenharmony_ci		ret = PTR_ERR(wifi_pkg);
89362306a36Sopenharmony_ci	else
89462306a36Sopenharmony_ci		ret = -ENOENT;
89562306a36Sopenharmony_ci	goto out_free;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ciread_table:
89862306a36Sopenharmony_ci	fwrt->geo_rev = tbl_rev;
89962306a36Sopenharmony_ci	for (i = 0; i < num_profiles; i++) {
90062306a36Sopenharmony_ci		for (j = 0; j < ACPI_GEO_NUM_BANDS_REV2; j++) {
90162306a36Sopenharmony_ci			union acpi_object *entry;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci			/*
90462306a36Sopenharmony_ci			 * num_bands is either 2 or 3, if it's only 2 then
90562306a36Sopenharmony_ci			 * fill the third band (6 GHz) with the values from
90662306a36Sopenharmony_ci			 * 5 GHz (second band)
90762306a36Sopenharmony_ci			 */
90862306a36Sopenharmony_ci			if (j >= num_bands) {
90962306a36Sopenharmony_ci				fwrt->geo_profiles[i].bands[j].max =
91062306a36Sopenharmony_ci					fwrt->geo_profiles[i].bands[1].max;
91162306a36Sopenharmony_ci			} else {
91262306a36Sopenharmony_ci				entry = &wifi_pkg->package.elements[entry_idx];
91362306a36Sopenharmony_ci				entry_idx++;
91462306a36Sopenharmony_ci				if (entry->type != ACPI_TYPE_INTEGER ||
91562306a36Sopenharmony_ci				    entry->integer.value > U8_MAX) {
91662306a36Sopenharmony_ci					ret = -EINVAL;
91762306a36Sopenharmony_ci					goto out_free;
91862306a36Sopenharmony_ci				}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci				fwrt->geo_profiles[i].bands[j].max =
92162306a36Sopenharmony_ci					entry->integer.value;
92262306a36Sopenharmony_ci			}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci			for (k = 0; k < ACPI_GEO_NUM_CHAINS; k++) {
92562306a36Sopenharmony_ci				/* same here as above */
92662306a36Sopenharmony_ci				if (j >= num_bands) {
92762306a36Sopenharmony_ci					fwrt->geo_profiles[i].bands[j].chains[k] =
92862306a36Sopenharmony_ci						fwrt->geo_profiles[i].bands[1].chains[k];
92962306a36Sopenharmony_ci				} else {
93062306a36Sopenharmony_ci					entry = &wifi_pkg->package.elements[entry_idx];
93162306a36Sopenharmony_ci					entry_idx++;
93262306a36Sopenharmony_ci					if (entry->type != ACPI_TYPE_INTEGER ||
93362306a36Sopenharmony_ci					    entry->integer.value > U8_MAX) {
93462306a36Sopenharmony_ci						ret = -EINVAL;
93562306a36Sopenharmony_ci						goto out_free;
93662306a36Sopenharmony_ci					}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci					fwrt->geo_profiles[i].bands[j].chains[k] =
93962306a36Sopenharmony_ci						entry->integer.value;
94062306a36Sopenharmony_ci				}
94162306a36Sopenharmony_ci			}
94262306a36Sopenharmony_ci		}
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	fwrt->geo_num_profiles = num_profiles;
94662306a36Sopenharmony_ci	fwrt->geo_enabled = true;
94762306a36Sopenharmony_ci	ret = 0;
94862306a36Sopenharmony_ciout_free:
94962306a36Sopenharmony_ci	kfree(data);
95062306a36Sopenharmony_ci	return ret;
95162306a36Sopenharmony_ci}
95262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cibool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	/*
95762306a36Sopenharmony_ci	 * The PER_CHAIN_LIMIT_OFFSET_CMD command is not supported on
95862306a36Sopenharmony_ci	 * earlier firmware versions.  Unfortunately, we don't have a
95962306a36Sopenharmony_ci	 * TLV API flag to rely on, so rely on the major version which
96062306a36Sopenharmony_ci	 * is in the first byte of ucode_ver.  This was implemented
96162306a36Sopenharmony_ci	 * initially on version 38 and then backported to 17.  It was
96262306a36Sopenharmony_ci	 * also backported to 29, but only for 7265D devices.  The
96362306a36Sopenharmony_ci	 * intention was to have it in 36 as well, but not all 8000
96462306a36Sopenharmony_ci	 * family got this feature enabled.  The 8000 family is the
96562306a36Sopenharmony_ci	 * only one using version 36, so skip this version entirely.
96662306a36Sopenharmony_ci	 */
96762306a36Sopenharmony_ci	return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 ||
96862306a36Sopenharmony_ci		(IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 &&
96962306a36Sopenharmony_ci		 fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) ||
97062306a36Sopenharmony_ci		(IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 &&
97162306a36Sopenharmony_ci		 ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) ==
97262306a36Sopenharmony_ci		  CSR_HW_REV_TYPE_7265D));
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_geo_support);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ciint iwl_sar_geo_init(struct iwl_fw_runtime *fwrt,
97762306a36Sopenharmony_ci		     struct iwl_per_chain_offset *table,
97862306a36Sopenharmony_ci		     u32 n_bands, u32 n_profiles)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	int i, j;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	if (!fwrt->geo_enabled)
98362306a36Sopenharmony_ci		return -ENODATA;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	if (!iwl_sar_geo_support(fwrt))
98662306a36Sopenharmony_ci		return -EOPNOTSUPP;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	for (i = 0; i < n_profiles; i++) {
98962306a36Sopenharmony_ci		for (j = 0; j < n_bands; j++) {
99062306a36Sopenharmony_ci			struct iwl_per_chain_offset *chain =
99162306a36Sopenharmony_ci				&table[i * n_bands + j];
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci			chain->max_tx_power =
99462306a36Sopenharmony_ci				cpu_to_le16(fwrt->geo_profiles[i].bands[j].max);
99562306a36Sopenharmony_ci			chain->chain_a = fwrt->geo_profiles[i].bands[j].chains[0];
99662306a36Sopenharmony_ci			chain->chain_b = fwrt->geo_profiles[i].bands[j].chains[1];
99762306a36Sopenharmony_ci			IWL_DEBUG_RADIO(fwrt,
99862306a36Sopenharmony_ci					"SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n",
99962306a36Sopenharmony_ci					i, j,
100062306a36Sopenharmony_ci					fwrt->geo_profiles[i].bands[j].chains[0],
100162306a36Sopenharmony_ci					fwrt->geo_profiles[i].bands[j].chains[1],
100262306a36Sopenharmony_ci					fwrt->geo_profiles[i].bands[j].max);
100362306a36Sopenharmony_ci		}
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	return 0;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_sar_geo_init);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci__le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	int ret;
101362306a36Sopenharmony_ci	u8 value;
101462306a36Sopenharmony_ci	__le32 config_bitmap = 0;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci	/*
101762306a36Sopenharmony_ci	 ** Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2'
101862306a36Sopenharmony_ci	 */
101962306a36Sopenharmony_ci	ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
102062306a36Sopenharmony_ci				  DSM_FUNC_ENABLE_INDONESIA_5G2,
102162306a36Sopenharmony_ci				  &iwl_guid, &value);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	if (!ret && value == DSM_VALUE_INDONESIA_ENABLE)
102462306a36Sopenharmony_ci		config_bitmap |=
102562306a36Sopenharmony_ci			cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	/*
102862306a36Sopenharmony_ci	 ** Evaluate func 'DSM_FUNC_DISABLE_SRD'
102962306a36Sopenharmony_ci	 */
103062306a36Sopenharmony_ci	ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
103162306a36Sopenharmony_ci				  DSM_FUNC_DISABLE_SRD,
103262306a36Sopenharmony_ci				  &iwl_guid, &value);
103362306a36Sopenharmony_ci	if (!ret) {
103462306a36Sopenharmony_ci		if (value == DSM_VALUE_SRD_PASSIVE)
103562306a36Sopenharmony_ci			config_bitmap |=
103662306a36Sopenharmony_ci				cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK);
103762306a36Sopenharmony_ci		else if (value == DSM_VALUE_SRD_DISABLE)
103862306a36Sopenharmony_ci			config_bitmap |=
103962306a36Sopenharmony_ci				cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK);
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	return config_bitmap;
104362306a36Sopenharmony_ci}
104462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ciint iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
104762306a36Sopenharmony_ci{
104862306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *data, *flags;
104962306a36Sopenharmony_ci	int i, j, ret, tbl_rev, num_sub_bands = 0;
105062306a36Sopenharmony_ci	int idx = 2;
105162306a36Sopenharmony_ci	u8 cmd_ver;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	fwrt->ppag_flags = 0;
105462306a36Sopenharmony_ci	fwrt->ppag_table_valid = false;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);
105762306a36Sopenharmony_ci	if (IS_ERR(data))
105862306a36Sopenharmony_ci		return PTR_ERR(data);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/* try to read ppag table rev 2 or 1 (both have the same data size) */
106162306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
106262306a36Sopenharmony_ci				ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
106562306a36Sopenharmony_ci		if (tbl_rev == 1 || tbl_rev == 2) {
106662306a36Sopenharmony_ci			num_sub_bands = IWL_NUM_SUB_BANDS_V2;
106762306a36Sopenharmony_ci			IWL_DEBUG_RADIO(fwrt,
106862306a36Sopenharmony_ci					"Reading PPAG table v2 (tbl_rev=%d)\n",
106962306a36Sopenharmony_ci					tbl_rev);
107062306a36Sopenharmony_ci			goto read_table;
107162306a36Sopenharmony_ci		} else {
107262306a36Sopenharmony_ci			ret = -EINVAL;
107362306a36Sopenharmony_ci			goto out_free;
107462306a36Sopenharmony_ci		}
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* try to read ppag table revision 0 */
107862306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
107962306a36Sopenharmony_ci			ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (!IS_ERR(wifi_pkg)) {
108262306a36Sopenharmony_ci		if (tbl_rev != 0) {
108362306a36Sopenharmony_ci			ret = -EINVAL;
108462306a36Sopenharmony_ci			goto out_free;
108562306a36Sopenharmony_ci		}
108662306a36Sopenharmony_ci		num_sub_bands = IWL_NUM_SUB_BANDS_V1;
108762306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");
108862306a36Sopenharmony_ci		goto read_table;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	ret = PTR_ERR(wifi_pkg);
109262306a36Sopenharmony_ci	goto out_free;
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ciread_table:
109562306a36Sopenharmony_ci	fwrt->ppag_ver = tbl_rev;
109662306a36Sopenharmony_ci	flags = &wifi_pkg->package.elements[1];
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (flags->type != ACPI_TYPE_INTEGER) {
109962306a36Sopenharmony_ci		ret = -EINVAL;
110062306a36Sopenharmony_ci		goto out_free;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	fwrt->ppag_flags = flags->integer.value & ACPI_PPAG_MASK;
110462306a36Sopenharmony_ci	cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
110562306a36Sopenharmony_ci					WIDE_ID(PHY_OPS_GROUP,
110662306a36Sopenharmony_ci						PER_PLATFORM_ANT_GAIN_CMD),
110762306a36Sopenharmony_ci					IWL_FW_CMD_VER_UNKNOWN);
110862306a36Sopenharmony_ci	if (cmd_ver == IWL_FW_CMD_VER_UNKNOWN) {
110962306a36Sopenharmony_ci		ret = -EINVAL;
111062306a36Sopenharmony_ci		goto out_free;
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci	if (!fwrt->ppag_flags && cmd_ver <= 3) {
111362306a36Sopenharmony_ci		ret = 0;
111462306a36Sopenharmony_ci		goto out_free;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	/*
111862306a36Sopenharmony_ci	 * read, verify gain values and save them into the PPAG table.
111962306a36Sopenharmony_ci	 * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
112062306a36Sopenharmony_ci	 * following sub-bands to High-Band (5GHz).
112162306a36Sopenharmony_ci	 */
112262306a36Sopenharmony_ci	for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
112362306a36Sopenharmony_ci		for (j = 0; j < num_sub_bands; j++) {
112462306a36Sopenharmony_ci			union acpi_object *ent;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci			ent = &wifi_pkg->package.elements[idx++];
112762306a36Sopenharmony_ci			if (ent->type != ACPI_TYPE_INTEGER) {
112862306a36Sopenharmony_ci				ret = -EINVAL;
112962306a36Sopenharmony_ci				goto out_free;
113062306a36Sopenharmony_ci			}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci			fwrt->ppag_chains[i].subbands[j] = ent->integer.value;
113362306a36Sopenharmony_ci			/* from ver 4 the fw deals with out of range values */
113462306a36Sopenharmony_ci			if (cmd_ver >= 4)
113562306a36Sopenharmony_ci				continue;
113662306a36Sopenharmony_ci			if ((j == 0 &&
113762306a36Sopenharmony_ci				(fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_LB ||
113862306a36Sopenharmony_ci				 fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_LB)) ||
113962306a36Sopenharmony_ci				(j != 0 &&
114062306a36Sopenharmony_ci				(fwrt->ppag_chains[i].subbands[j] > ACPI_PPAG_MAX_HB ||
114162306a36Sopenharmony_ci				fwrt->ppag_chains[i].subbands[j] < ACPI_PPAG_MIN_HB))) {
114262306a36Sopenharmony_ci					ret = -EINVAL;
114362306a36Sopenharmony_ci					goto out_free;
114462306a36Sopenharmony_ci				}
114562306a36Sopenharmony_ci		}
114662306a36Sopenharmony_ci	}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	fwrt->ppag_table_valid = true;
114962306a36Sopenharmony_ci	ret = 0;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ciout_free:
115262306a36Sopenharmony_ci	kfree(data);
115362306a36Sopenharmony_ci	return ret;
115462306a36Sopenharmony_ci}
115562306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_ppag_table);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ciint iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *cmd,
115862306a36Sopenharmony_ci			int *cmd_size)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci        u8 cmd_ver;
116162306a36Sopenharmony_ci        int i, j, num_sub_bands;
116262306a36Sopenharmony_ci        s8 *gain;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	/* many firmware images for JF lie about this */
116562306a36Sopenharmony_ci	if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) ==
116662306a36Sopenharmony_ci	    CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
116762306a36Sopenharmony_ci		return -EOPNOTSUPP;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci        if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
117062306a36Sopenharmony_ci                IWL_DEBUG_RADIO(fwrt,
117162306a36Sopenharmony_ci                                "PPAG capability not supported by FW, command not sent.\n");
117262306a36Sopenharmony_ci                return -EINVAL;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	cmd_ver = iwl_fw_lookup_cmd_ver(fwrt->fw,
117662306a36Sopenharmony_ci					WIDE_ID(PHY_OPS_GROUP,
117762306a36Sopenharmony_ci						PER_PLATFORM_ANT_GAIN_CMD),
117862306a36Sopenharmony_ci					IWL_FW_CMD_VER_UNKNOWN);
117962306a36Sopenharmony_ci	if (!fwrt->ppag_table_valid || (cmd_ver <= 3 && !fwrt->ppag_flags)) {
118062306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "PPAG not enabled, command not sent.\n");
118162306a36Sopenharmony_ci		return -EINVAL;
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci        /* The 'flags' field is the same in v1 and in v2 so we can just
118562306a36Sopenharmony_ci         * use v1 to access it.
118662306a36Sopenharmony_ci         */
118762306a36Sopenharmony_ci        cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver);
119062306a36Sopenharmony_ci	if (cmd_ver == 1) {
119162306a36Sopenharmony_ci                num_sub_bands = IWL_NUM_SUB_BANDS_V1;
119262306a36Sopenharmony_ci                gain = cmd->v1.gain[0];
119362306a36Sopenharmony_ci                *cmd_size = sizeof(cmd->v1);
119462306a36Sopenharmony_ci                if (fwrt->ppag_ver == 1 || fwrt->ppag_ver == 2) {
119562306a36Sopenharmony_ci			/* in this case FW supports revision 0 */
119662306a36Sopenharmony_ci                        IWL_DEBUG_RADIO(fwrt,
119762306a36Sopenharmony_ci					"PPAG table rev is %d, send truncated table\n",
119862306a36Sopenharmony_ci                                        fwrt->ppag_ver);
119962306a36Sopenharmony_ci		}
120062306a36Sopenharmony_ci	} else if (cmd_ver >= 2 && cmd_ver <= 4) {
120162306a36Sopenharmony_ci                num_sub_bands = IWL_NUM_SUB_BANDS_V2;
120262306a36Sopenharmony_ci                gain = cmd->v2.gain[0];
120362306a36Sopenharmony_ci                *cmd_size = sizeof(cmd->v2);
120462306a36Sopenharmony_ci                if (fwrt->ppag_ver == 0) {
120562306a36Sopenharmony_ci			/* in this case FW supports revisions 1 or 2 */
120662306a36Sopenharmony_ci                        IWL_DEBUG_RADIO(fwrt,
120762306a36Sopenharmony_ci					"PPAG table rev is 0, send padded table\n");
120862306a36Sopenharmony_ci                }
120962306a36Sopenharmony_ci        } else {
121062306a36Sopenharmony_ci                IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n");
121162306a36Sopenharmony_ci                return -EINVAL;
121262306a36Sopenharmony_ci        }
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/* ppag mode */
121562306a36Sopenharmony_ci	IWL_DEBUG_RADIO(fwrt,
121662306a36Sopenharmony_ci			"PPAG MODE bits were read from bios: %d\n",
121762306a36Sopenharmony_ci			cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK));
121862306a36Sopenharmony_ci	if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa,
121962306a36Sopenharmony_ci					  IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) ||
122062306a36Sopenharmony_ci	    (cmd_ver == 2 && fwrt->ppag_ver == 2)) {
122162306a36Sopenharmony_ci		cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK);
122262306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n");
122362306a36Sopenharmony_ci	} else {
122462306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n");
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	IWL_DEBUG_RADIO(fwrt,
122862306a36Sopenharmony_ci			"PPAG MODE bits going to be sent: %d\n",
122962306a36Sopenharmony_ci			cmd->v1.flags & cpu_to_le32(ACPI_PPAG_MASK));
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
123262306a36Sopenharmony_ci                for (j = 0; j < num_sub_bands; j++) {
123362306a36Sopenharmony_ci                        gain[i * num_sub_bands + j] =
123462306a36Sopenharmony_ci                                fwrt->ppag_chains[i].subbands[j];
123562306a36Sopenharmony_ci                        IWL_DEBUG_RADIO(fwrt,
123662306a36Sopenharmony_ci                                        "PPAG table: chain[%d] band[%d]: gain = %d\n",
123762306a36Sopenharmony_ci                                        i, j, gain[i * num_sub_bands + j]);
123862306a36Sopenharmony_ci                }
123962306a36Sopenharmony_ci        }
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return 0;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read_ppag_table);
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cibool iwl_acpi_is_ppag_approved(struct iwl_fw_runtime *fwrt)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (!dmi_check_system(dmi_ppag_approved_list)) {
124962306a36Sopenharmony_ci		IWL_DEBUG_RADIO(fwrt,
125062306a36Sopenharmony_ci			"System vendor '%s' is not in the approved list, disabling PPAG.\n",
125162306a36Sopenharmony_ci			dmi_get_system_info(DMI_SYS_VENDOR));
125262306a36Sopenharmony_ci			fwrt->ppag_flags = 0;
125362306a36Sopenharmony_ci			return false;
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	return true;
125762306a36Sopenharmony_ci}
125862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_is_ppag_approved);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_civoid iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt,
126162306a36Sopenharmony_ci			      struct iwl_phy_specific_cfg *filters)
126262306a36Sopenharmony_ci{
126362306a36Sopenharmony_ci	struct iwl_phy_specific_cfg tmp = {};
126462306a36Sopenharmony_ci	union acpi_object *wifi_pkg, *data;
126562306a36Sopenharmony_ci	int tbl_rev, i;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);
126862306a36Sopenharmony_ci	if (IS_ERR(data))
126962306a36Sopenharmony_ci		return;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
127262306a36Sopenharmony_ci					 ACPI_WPFC_WIFI_DATA_SIZE,
127362306a36Sopenharmony_ci					 &tbl_rev);
127462306a36Sopenharmony_ci	if (IS_ERR(wifi_pkg))
127562306a36Sopenharmony_ci		goto out_free;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	if (tbl_rev != 0)
127862306a36Sopenharmony_ci		goto out_free;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
128162306a36Sopenharmony_ci		     ACPI_WPFC_WIFI_DATA_SIZE - 1);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
128462306a36Sopenharmony_ci		if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)
128562306a36Sopenharmony_ci			goto out_free;
128662306a36Sopenharmony_ci		tmp.filter_cfg_chains[i] =
128762306a36Sopenharmony_ci			cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);
128862306a36Sopenharmony_ci	}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");
129162306a36Sopenharmony_ci	*filters = tmp;
129262306a36Sopenharmony_ciout_free:
129362306a36Sopenharmony_ci	kfree(data);
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);
1296