drm/virtio: Add drm_panic support
[drm/drm-misc.git] / drivers / net / wireless / intel / iwlwifi / fw / acpi.c
blobefa7b673ebc73f04909d7a1a00e88346fe1b2e73
1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /*
3 * Copyright (C) 2017 Intel Deutschland GmbH
4 * Copyright (C) 2019-2024 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"
12 const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
13 0xA5, 0xB3, 0x1F, 0x73,
14 0x8E, 0x28, 0x5A, 0xDE);
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),
33 static int iwl_acpi_get_handle(struct device *dev, acpi_string method,
34 acpi_handle *ret_handle)
36 acpi_handle root_handle;
37 acpi_status status;
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;
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;
52 return 0;
55 static void *iwl_acpi_get_object(struct device *dev, acpi_string method)
57 struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
58 acpi_handle handle;
59 acpi_status status;
60 int ret;
62 ret = iwl_acpi_get_handle(dev, method, &handle);
63 if (ret)
64 return ERR_PTR(-ENOENT);
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);
74 return buf.pointer;
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.
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)
86 union acpi_object *obj;
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);
96 return obj;
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.
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)
110 union acpi_object *obj;
111 int ret;
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;
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;
126 if (WARN_ON_ONCE(expected_size > sizeof(le_value))) {
127 ret = -EINVAL;
128 goto out;
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);
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;
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;
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.
163 int iwl_acpi_get_dsm(struct iwl_fw_runtime *fwrt,
164 enum iwl_dsm_funcs func, u32 *value)
166 size_t expected_size;
167 u64 tmp;
168 int ret;
170 BUILD_BUG_ON(ARRAY_SIZE(acpi_dsm_size) != DSM_FUNC_NUM_FUNCS);
172 if (WARN_ON(func >= ARRAY_SIZE(acpi_dsm_size)))
173 return -EINVAL;
175 expected_size = acpi_dsm_size[func];
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;
181 ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func,
182 &iwl_guid, &tmp, expected_size);
183 if (ret)
184 return ret;
186 if ((expected_size == sizeof(u8) && tmp != (u8)tmp) ||
187 (expected_size == sizeof(u32) && tmp != (u32)tmp))
188 IWL_DEBUG_RADIO(fwrt,
189 "DSM value overflows the expected size, truncating\n");
190 *value = (u32)tmp;
192 return 0;
195 static union acpi_object *
196 iwl_acpi_get_wifi_pkg_range(struct device *dev,
197 union acpi_object *data,
198 int min_data_size,
199 int max_data_size,
200 int *tbl_rev)
202 int i;
203 union acpi_object *wifi_pkg;
206 * We need at least one entry in the wifi package that
207 * describes the domain, and one more entry, otherwise there's
208 * no point in reading it.
210 if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))
211 return ERR_PTR(-EINVAL);
214 * We need at least two packages, one for the revision and one
215 * for the data itself. Also check that the revision is valid
216 * (i.e. it is an integer (each caller has to check by itself
217 * if the returned revision is supported)).
219 if (data->type != ACPI_TYPE_PACKAGE ||
220 data->package.count < 2 ||
221 data->package.elements[0].type != ACPI_TYPE_INTEGER) {
222 IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");
223 return ERR_PTR(-EINVAL);
226 *tbl_rev = data->package.elements[0].integer.value;
228 /* loop through all the packages to find the one for WiFi */
229 for (i = 1; i < data->package.count; i++) {
230 union acpi_object *domain;
232 wifi_pkg = &data->package.elements[i];
234 /* skip entries that are not a package with the right size */
235 if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
236 wifi_pkg->package.count < min_data_size ||
237 wifi_pkg->package.count > max_data_size)
238 continue;
240 domain = &wifi_pkg->package.elements[0];
241 if (domain->type == ACPI_TYPE_INTEGER &&
242 domain->integer.value == ACPI_WIFI_DOMAIN)
243 goto found;
246 return ERR_PTR(-ENOENT);
248 found:
249 return wifi_pkg;
252 static union acpi_object *
253 iwl_acpi_get_wifi_pkg(struct device *dev,
254 union acpi_object *data,
255 int data_size, int *tbl_rev)
257 return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size,
258 tbl_rev);
261 int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt,
262 struct iwl_tas_data *tas_data)
264 union acpi_object *wifi_pkg, *data;
265 int ret, tbl_rev, block_list_size, enabled;
266 u32 tas_selection;
268 data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
269 if (IS_ERR(data))
270 return PTR_ERR(data);
272 /* try to read wtas table */
273 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
274 ACPI_WTAS_WIFI_DATA_SIZE,
275 &tbl_rev);
276 if (IS_ERR(wifi_pkg)) {
277 ret = PTR_ERR(wifi_pkg);
278 goto out_free;
281 if (tbl_rev < 0 || tbl_rev > 2 ||
282 wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
283 ret = -EINVAL;
284 goto out_free;
287 tas_selection = (u32)wifi_pkg->package.elements[1].integer.value;
288 enabled = tas_selection & IWL_WTAS_ENABLED_MSK;
290 IWL_DEBUG_RADIO(fwrt, "TAS selection as read from BIOS: 0x%x\n",
291 tas_selection);
292 tas_data->table_source = BIOS_SOURCE_ACPI;
293 tas_data->table_revision = tbl_rev;
294 tas_data->tas_selection = tas_selection;
296 IWL_DEBUG_RADIO(fwrt, "TAS %s enabled\n",
297 enabled ? "is" : "not");
299 IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);
300 if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
301 wifi_pkg->package.elements[2].integer.value >
302 IWL_WTAS_BLACK_LIST_MAX) {
303 IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
304 wifi_pkg->package.elements[2].integer.value);
305 ret = -EINVAL;
306 goto out_free;
309 block_list_size = wifi_pkg->package.elements[2].integer.value;
310 tas_data->block_list_size = block_list_size;
312 IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);
314 for (int i = 0; i < block_list_size; i++) {
315 u16 country;
317 if (wifi_pkg->package.elements[3 + i].type !=
318 ACPI_TYPE_INTEGER) {
319 IWL_DEBUG_RADIO(fwrt,
320 "TAS invalid array elem %d\n", 3 + i);
321 ret = -EINVAL;
322 goto out_free;
325 country = wifi_pkg->package.elements[3 + i].integer.value;
326 tas_data->block_list_array[i] = country;
327 IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
330 ret = enabled;
331 out_free:
332 kfree(data);
333 return ret;
336 int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)
338 union acpi_object *wifi_pkg, *data;
339 u32 mcc_val;
340 int ret, tbl_rev;
342 data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDD_METHOD);
343 if (IS_ERR(data))
344 return PTR_ERR(data);
346 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
347 ACPI_WRDD_WIFI_DATA_SIZE,
348 &tbl_rev);
349 if (IS_ERR(wifi_pkg)) {
350 ret = PTR_ERR(wifi_pkg);
351 goto out_free;
354 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
355 tbl_rev != 0) {
356 ret = -EINVAL;
357 goto out_free;
360 mcc_val = wifi_pkg->package.elements[1].integer.value;
361 if (mcc_val != BIOS_MCC_CHINA) {
362 ret = -EINVAL;
363 IWL_DEBUG_RADIO(fwrt, "ACPI WRDD is supported only for CN\n");
364 goto out_free;
367 mcc[0] = (mcc_val >> 8) & 0xff;
368 mcc[1] = mcc_val & 0xff;
369 mcc[2] = '\0';
371 ret = 0;
372 out_free:
373 kfree(data);
374 return ret;
377 int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit)
379 union acpi_object *data, *wifi_pkg;
380 int tbl_rev, ret = -EINVAL;
382 *dflt_pwr_limit = 0;
383 data = iwl_acpi_get_object(fwrt->dev, ACPI_SPLC_METHOD);
384 if (IS_ERR(data))
385 goto out;
387 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
388 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
389 if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
390 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER)
391 goto out_free;
393 *dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
394 ret = 0;
395 out_free:
396 kfree(data);
397 out:
398 return ret;
401 int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)
403 union acpi_object *wifi_pkg, *data;
404 int ret, tbl_rev;
406 data = iwl_acpi_get_object(fwrt->dev, ACPI_ECKV_METHOD);
407 if (IS_ERR(data))
408 return PTR_ERR(data);
410 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
411 ACPI_ECKV_WIFI_DATA_SIZE,
412 &tbl_rev);
413 if (IS_ERR(wifi_pkg)) {
414 ret = PTR_ERR(wifi_pkg);
415 goto out_free;
418 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
419 tbl_rev != 0) {
420 ret = -EINVAL;
421 goto out_free;
424 *extl_clk = wifi_pkg->package.elements[1].integer.value;
426 ret = 0;
428 out_free:
429 kfree(data);
430 return ret;
433 static int
434 iwl_acpi_parse_chains_table(union acpi_object *table,
435 struct iwl_sar_profile_chain *chains,
436 u8 num_chains, u8 num_sub_bands)
438 for (u8 chain = 0; chain < num_chains; chain++) {
439 for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM;
440 subband++) {
441 /* if we don't have the values, use the default */
442 if (subband >= num_sub_bands) {
443 chains[chain].subbands[subband] = 0;
444 } else if (table->type != ACPI_TYPE_INTEGER ||
445 table->integer.value > U8_MAX) {
446 return -EINVAL;
447 } else {
448 chains[chain].subbands[subband] =
449 table->integer.value;
450 table++;
455 return 0;
458 int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
460 union acpi_object *wifi_pkg, *table, *data;
461 int ret, tbl_rev;
462 u32 flags;
463 u8 num_chains, num_sub_bands;
465 data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
466 if (IS_ERR(data))
467 return PTR_ERR(data);
469 /* start by trying to read revision 2 */
470 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
471 ACPI_WRDS_WIFI_DATA_SIZE_REV2,
472 &tbl_rev);
473 if (!IS_ERR(wifi_pkg)) {
474 if (tbl_rev != 2) {
475 ret = -EINVAL;
476 goto out_free;
479 num_chains = ACPI_SAR_NUM_CHAINS_REV2;
480 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
482 goto read_table;
485 /* then try revision 1 */
486 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
487 ACPI_WRDS_WIFI_DATA_SIZE_REV1,
488 &tbl_rev);
489 if (!IS_ERR(wifi_pkg)) {
490 if (tbl_rev != 1) {
491 ret = -EINVAL;
492 goto out_free;
495 num_chains = ACPI_SAR_NUM_CHAINS_REV1;
496 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
498 goto read_table;
501 /* then finally revision 0 */
502 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
503 ACPI_WRDS_WIFI_DATA_SIZE_REV0,
504 &tbl_rev);
505 if (!IS_ERR(wifi_pkg)) {
506 if (tbl_rev != 0) {
507 ret = -EINVAL;
508 goto out_free;
511 num_chains = ACPI_SAR_NUM_CHAINS_REV0;
512 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
514 goto read_table;
517 ret = PTR_ERR(wifi_pkg);
518 goto out_free;
520 read_table:
521 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
522 ret = -EINVAL;
523 goto out_free;
526 IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
528 flags = wifi_pkg->package.elements[1].integer.value;
529 fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;
531 /* position of the actual table */
532 table = &wifi_pkg->package.elements[2];
534 /* The profile from WRDS is officially profile 1, but goes
535 * into sar_profiles[0] (because we don't have a profile 0).
537 ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains,
538 num_chains, num_sub_bands);
539 if (!ret && flags & IWL_SAR_ENABLE_MSK)
540 fwrt->sar_profiles[0].enabled = true;
542 out_free:
543 kfree(data);
544 return ret;
547 int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
549 union acpi_object *wifi_pkg, *data;
550 bool enabled;
551 int i, n_profiles, tbl_rev, pos;
552 int ret = 0;
553 u8 num_sub_bands;
555 data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
556 if (IS_ERR(data))
557 return PTR_ERR(data);
559 /* start by trying to read revision 2 */
560 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
561 ACPI_EWRD_WIFI_DATA_SIZE_REV2,
562 &tbl_rev);
563 if (!IS_ERR(wifi_pkg)) {
564 if (tbl_rev != 2) {
565 ret = -EINVAL;
566 goto out_free;
569 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
571 goto read_table;
574 /* then try revision 1 */
575 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
576 ACPI_EWRD_WIFI_DATA_SIZE_REV1,
577 &tbl_rev);
578 if (!IS_ERR(wifi_pkg)) {
579 if (tbl_rev != 1) {
580 ret = -EINVAL;
581 goto out_free;
584 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
586 goto read_table;
589 /* then finally revision 0 */
590 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
591 ACPI_EWRD_WIFI_DATA_SIZE_REV0,
592 &tbl_rev);
593 if (!IS_ERR(wifi_pkg)) {
594 if (tbl_rev != 0) {
595 ret = -EINVAL;
596 goto out_free;
599 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
601 goto read_table;
604 ret = PTR_ERR(wifi_pkg);
605 goto out_free;
607 read_table:
608 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
609 wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
610 ret = -EINVAL;
611 goto out_free;
614 enabled = !!(wifi_pkg->package.elements[1].integer.value);
615 n_profiles = wifi_pkg->package.elements[2].integer.value;
618 * Check the validity of n_profiles. The EWRD profiles start
619 * from index 1, so the maximum value allowed here is
620 * ACPI_SAR_PROFILES_NUM - 1.
622 if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
623 ret = -EINVAL;
624 goto out_free;
627 /* the tables start at element 3 */
628 pos = 3;
630 BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1);
631 BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0);
633 /* parse non-cdb chains for all profiles */
634 for (i = 0; i < n_profiles; i++) {
635 union acpi_object *table = &wifi_pkg->package.elements[pos];
637 /* The EWRD profiles officially go from 2 to 4, but we
638 * save them in sar_profiles[1-3] (because we don't
639 * have profile 0). So in the array we start from 1.
641 ret = iwl_acpi_parse_chains_table(table,
642 fwrt->sar_profiles[i + 1].chains,
643 ACPI_SAR_NUM_CHAINS_REV0,
644 num_sub_bands);
645 if (ret < 0)
646 goto out_free;
648 /* go to the next table */
649 pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
652 /* non-cdb table revisions */
653 if (tbl_rev < 2)
654 goto set_enabled;
656 /* parse cdb chains for all profiles */
657 for (i = 0; i < n_profiles; i++) {
658 struct iwl_sar_profile_chain *chains;
659 union acpi_object *table;
661 table = &wifi_pkg->package.elements[pos];
662 chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0];
663 ret = iwl_acpi_parse_chains_table(table,
664 chains,
665 ACPI_SAR_NUM_CHAINS_REV0,
666 num_sub_bands);
667 if (ret < 0)
668 goto out_free;
670 /* go to the next table */
671 pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
674 set_enabled:
675 for (i = 0; i < n_profiles; i++)
676 fwrt->sar_profiles[i + 1].enabled = enabled;
678 out_free:
679 kfree(data);
680 return ret;
683 int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
685 union acpi_object *wifi_pkg, *data;
686 int i, j, k, ret, tbl_rev;
687 u8 num_bands, num_profiles;
688 static const struct {
689 u8 revisions;
690 u8 bands;
691 u8 profiles;
692 u8 min_profiles;
693 } rev_data[] = {
695 .revisions = BIT(3),
696 .bands = ACPI_GEO_NUM_BANDS_REV2,
697 .profiles = ACPI_NUM_GEO_PROFILES_REV3,
698 .min_profiles = BIOS_GEO_MIN_PROFILE_NUM,
701 .revisions = BIT(2),
702 .bands = ACPI_GEO_NUM_BANDS_REV2,
703 .profiles = ACPI_NUM_GEO_PROFILES,
706 .revisions = BIT(0) | BIT(1),
707 .bands = ACPI_GEO_NUM_BANDS_REV0,
708 .profiles = ACPI_NUM_GEO_PROFILES,
711 int idx;
712 /* start from one to skip the domain */
713 int entry_idx = 1;
715 BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);
716 BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);
718 data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
719 if (IS_ERR(data))
720 return PTR_ERR(data);
722 /* read the highest revision we understand first */
723 for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {
724 /* min_profiles != 0 requires num_profiles header */
725 u32 hdr_size = 1 + !!rev_data[idx].min_profiles;
726 u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *
727 rev_data[idx].bands;
728 u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;
729 u32 min_size;
731 if (!rev_data[idx].min_profiles)
732 min_size = max_size;
733 else
734 min_size = hdr_size +
735 profile_size * rev_data[idx].min_profiles;
737 wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,
738 min_size, max_size,
739 &tbl_rev);
740 if (!IS_ERR(wifi_pkg)) {
741 if (!(BIT(tbl_rev) & rev_data[idx].revisions))
742 continue;
744 num_bands = rev_data[idx].bands;
745 num_profiles = rev_data[idx].profiles;
747 if (rev_data[idx].min_profiles) {
748 /* read header that says # of profiles */
749 union acpi_object *entry;
751 entry = &wifi_pkg->package.elements[entry_idx];
752 entry_idx++;
753 if (entry->type != ACPI_TYPE_INTEGER ||
754 entry->integer.value > num_profiles ||
755 entry->integer.value <
756 rev_data[idx].min_profiles) {
757 ret = -EINVAL;
758 goto out_free;
762 * Check to see if we received package count
763 * same as max # of profiles
765 if (wifi_pkg->package.count !=
766 hdr_size + profile_size * num_profiles) {
767 ret = -EINVAL;
768 goto out_free;
771 /* Number of valid profiles */
772 num_profiles = entry->integer.value;
774 goto read_table;
778 if (idx < ARRAY_SIZE(rev_data))
779 ret = PTR_ERR(wifi_pkg);
780 else
781 ret = -ENOENT;
782 goto out_free;
784 read_table:
785 fwrt->geo_rev = tbl_rev;
786 for (i = 0; i < num_profiles; i++) {
787 for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) {
788 union acpi_object *entry;
791 * num_bands is either 2 or 3, if it's only 2 then
792 * fill the third band (6 GHz) with the values from
793 * 5 GHz (second band)
795 if (j >= num_bands) {
796 fwrt->geo_profiles[i].bands[j].max =
797 fwrt->geo_profiles[i].bands[1].max;
798 } else {
799 entry = &wifi_pkg->package.elements[entry_idx];
800 entry_idx++;
801 if (entry->type != ACPI_TYPE_INTEGER ||
802 entry->integer.value > U8_MAX) {
803 ret = -EINVAL;
804 goto out_free;
807 fwrt->geo_profiles[i].bands[j].max =
808 entry->integer.value;
811 for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) {
812 /* same here as above */
813 if (j >= num_bands) {
814 fwrt->geo_profiles[i].bands[j].chains[k] =
815 fwrt->geo_profiles[i].bands[1].chains[k];
816 } else {
817 entry = &wifi_pkg->package.elements[entry_idx];
818 entry_idx++;
819 if (entry->type != ACPI_TYPE_INTEGER ||
820 entry->integer.value > U8_MAX) {
821 ret = -EINVAL;
822 goto out_free;
825 fwrt->geo_profiles[i].bands[j].chains[k] =
826 entry->integer.value;
832 fwrt->geo_num_profiles = num_profiles;
833 fwrt->geo_enabled = true;
834 ret = 0;
835 out_free:
836 kfree(data);
837 return ret;
840 int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
842 union acpi_object *wifi_pkg, *data, *flags;
843 int i, j, ret, tbl_rev, num_sub_bands = 0;
844 int idx = 2;
846 data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);
847 if (IS_ERR(data))
848 return PTR_ERR(data);
850 /* try to read ppag table rev 3, 2 or 1 (all have the same data size) */
851 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
852 ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
854 if (!IS_ERR(wifi_pkg)) {
855 if (tbl_rev >= 1 && tbl_rev <= 3) {
856 num_sub_bands = IWL_NUM_SUB_BANDS_V2;
857 IWL_DEBUG_RADIO(fwrt,
858 "Reading PPAG table (tbl_rev=%d)\n",
859 tbl_rev);
860 goto read_table;
861 } else {
862 ret = -EINVAL;
863 goto out_free;
867 /* try to read ppag table revision 0 */
868 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
869 ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);
871 if (!IS_ERR(wifi_pkg)) {
872 if (tbl_rev != 0) {
873 ret = -EINVAL;
874 goto out_free;
876 num_sub_bands = IWL_NUM_SUB_BANDS_V1;
877 IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");
878 goto read_table;
881 ret = PTR_ERR(wifi_pkg);
882 goto out_free;
884 read_table:
885 fwrt->ppag_ver = tbl_rev;
886 flags = &wifi_pkg->package.elements[1];
888 if (flags->type != ACPI_TYPE_INTEGER) {
889 ret = -EINVAL;
890 goto out_free;
893 fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value,
894 fwrt->ppag_ver);
897 * read, verify gain values and save them into the PPAG table.
898 * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
899 * following sub-bands to High-Band (5GHz).
901 for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
902 for (j = 0; j < num_sub_bands; j++) {
903 union acpi_object *ent;
905 ent = &wifi_pkg->package.elements[idx++];
906 if (ent->type != ACPI_TYPE_INTEGER) {
907 ret = -EINVAL;
908 goto out_free;
911 fwrt->ppag_chains[i].subbands[j] = ent->integer.value;
915 ret = 0;
917 out_free:
918 kfree(data);
919 return ret;
922 void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt,
923 struct iwl_phy_specific_cfg *filters)
925 struct iwl_phy_specific_cfg tmp = {};
926 union acpi_object *wifi_pkg, *data;
927 int tbl_rev, i;
929 data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);
930 if (IS_ERR(data))
931 return;
933 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
934 ACPI_WPFC_WIFI_DATA_SIZE,
935 &tbl_rev);
936 if (IS_ERR(wifi_pkg))
937 goto out_free;
939 if (tbl_rev != 0)
940 goto out_free;
942 BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
943 ACPI_WPFC_WIFI_DATA_SIZE - 1);
945 for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
946 if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)
947 goto out_free;
948 tmp.filter_cfg_chains[i] =
949 cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);
952 IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");
953 *filters = tmp;
954 out_free:
955 kfree(data);
957 IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);
959 void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt)
961 union acpi_object *wifi_pkg, *data;
962 int tbl_rev;
964 data = iwl_acpi_get_object(fwrt->dev, ACPI_GLAI_METHOD);
965 if (IS_ERR(data))
966 return;
968 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
969 ACPI_GLAI_WIFI_DATA_SIZE,
970 &tbl_rev);
971 if (IS_ERR(wifi_pkg))
972 goto out_free;
974 if (tbl_rev != 0) {
975 IWL_DEBUG_RADIO(fwrt, "Invalid GLAI revision: %d\n", tbl_rev);
976 goto out_free;
979 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
980 wifi_pkg->package.elements[1].integer.value > ACPI_GLAI_MAX_STATUS)
981 goto out_free;
983 fwrt->uefi_tables_lock_status =
984 wifi_pkg->package.elements[1].integer.value;
986 IWL_DEBUG_RADIO(fwrt,
987 "Loaded UEFI WIFI GUID lock status: %d from ACPI\n",
988 fwrt->uefi_tables_lock_status);
989 out_free:
990 kfree(data);
992 IWL_EXPORT_SYMBOL(iwl_acpi_get_guid_lock_status);
994 int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)
996 union acpi_object *wifi_pkg, *data;
997 int ret = -ENOENT;
998 int tbl_rev;
1000 data = iwl_acpi_get_object(fwrt->dev, ACPI_WBEM_METHOD);
1001 if (IS_ERR(data))
1002 return ret;
1004 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1005 ACPI_WBEM_WIFI_DATA_SIZE,
1006 &tbl_rev);
1007 if (IS_ERR(wifi_pkg))
1008 goto out_free;
1010 if (tbl_rev != IWL_ACPI_WBEM_REVISION) {
1011 IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI WBEM revision:%d\n",
1012 tbl_rev);
1013 goto out_free;
1016 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
1017 goto out_free;
1019 *value = wifi_pkg->package.elements[1].integer.value &
1020 IWL_ACPI_WBEM_REV0_MASK;
1021 IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from ACPI\n");
1022 ret = 0;
1023 out_free:
1024 kfree(data);
1025 return ret;
1028 int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value)
1030 union acpi_object *wifi_pkg, *data;
1031 int ret = -ENOENT;
1032 int tbl_rev;
1034 data = iwl_acpi_get_object(fwrt->dev, ACPI_DSBR_METHOD);
1035 if (IS_ERR(data))
1036 return ret;
1038 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1039 ACPI_DSBR_WIFI_DATA_SIZE,
1040 &tbl_rev);
1041 if (IS_ERR(wifi_pkg))
1042 goto out_free;
1044 if (tbl_rev != ACPI_DSBR_WIFI_DATA_REV) {
1045 IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI DSBR revision:%d\n",
1046 tbl_rev);
1047 goto out_free;
1050 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
1051 goto out_free;
1053 *value = wifi_pkg->package.elements[1].integer.value;
1054 IWL_DEBUG_RADIO(fwrt, "Loaded DSBR config from ACPI value: 0x%x\n",
1055 *value);
1056 ret = 0;
1057 out_free:
1058 kfree(data);
1059 return ret;