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