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