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