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"
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 static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
83 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)))
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 ret = iwl_acpi_get_dsm_integer(fwrt->dev, ACPI_DSM_REV, func,
182 &iwl_guid, &tmp, expected_size);
183 if (ret)
184 return ret;
185
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;
191
192 return 0;
193 }
194
195 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)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)
201 {
202 int i;
203 union acpi_object *wifi_pkg;
204
205 /*
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.
209 */
210 if (WARN_ON_ONCE(min_data_size < 2 || min_data_size > max_data_size))
211 return ERR_PTR(-EINVAL);
212
213 /*
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)).
218 */
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);
224 }
225
226 *tbl_rev = data->package.elements[0].integer.value;
227
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;
231
232 wifi_pkg = &data->package.elements[i];
233
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;
239
240 domain = &wifi_pkg->package.elements[0];
241 if (domain->type == ACPI_TYPE_INTEGER &&
242 domain->integer.value == ACPI_WIFI_DOMAIN)
243 goto found;
244 }
245
246 return ERR_PTR(-ENOENT);
247
248 found:
249 return wifi_pkg;
250 }
251
252 static union acpi_object *
iwl_acpi_get_wifi_pkg(struct device * dev,union acpi_object * data,int data_size,int * tbl_rev)253 iwl_acpi_get_wifi_pkg(struct device *dev,
254 union acpi_object *data,
255 int data_size, int *tbl_rev)
256 {
257 return iwl_acpi_get_wifi_pkg_range(dev, data, data_size, data_size,
258 tbl_rev);
259 }
260
iwl_acpi_get_tas_table(struct iwl_fw_runtime * fwrt,struct iwl_tas_data * tas_data)261 int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt,
262 struct iwl_tas_data *tas_data)
263 {
264 union acpi_object *wifi_pkg, *data;
265 int ret, tbl_rev, i, block_list_size, enabled;
266
267 data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
268 if (IS_ERR(data))
269 return PTR_ERR(data);
270
271 /* try to read wtas table revision 1 or revision 0*/
272 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
273 ACPI_WTAS_WIFI_DATA_SIZE,
274 &tbl_rev);
275 if (IS_ERR(wifi_pkg)) {
276 ret = PTR_ERR(wifi_pkg);
277 goto out_free;
278 }
279
280 if (tbl_rev == 1 && wifi_pkg->package.elements[1].type ==
281 ACPI_TYPE_INTEGER) {
282 u32 tas_selection =
283 (u32)wifi_pkg->package.elements[1].integer.value;
284
285 enabled = iwl_parse_tas_selection(fwrt, tas_data,
286 tas_selection);
287
288 } else if (tbl_rev == 0 &&
289 wifi_pkg->package.elements[1].type == ACPI_TYPE_INTEGER) {
290 enabled = !!wifi_pkg->package.elements[1].integer.value;
291 } else {
292 ret = -EINVAL;
293 goto out_free;
294 }
295
296 if (!enabled) {
297 IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");
298 ret = 0;
299 goto out_free;
300 }
301
302 IWL_DEBUG_RADIO(fwrt, "Reading TAS table revision %d\n", tbl_rev);
303 if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
304 wifi_pkg->package.elements[2].integer.value >
305 IWL_WTAS_BLACK_LIST_MAX) {
306 IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
307 wifi_pkg->package.elements[2].integer.value);
308 ret = -EINVAL;
309 goto out_free;
310 }
311 block_list_size = wifi_pkg->package.elements[2].integer.value;
312 tas_data->block_list_size = cpu_to_le32(block_list_size);
313
314 IWL_DEBUG_RADIO(fwrt, "TAS array size %u\n", block_list_size);
315
316 for (i = 0; i < block_list_size; i++) {
317 u32 country;
318
319 if (wifi_pkg->package.elements[3 + i].type !=
320 ACPI_TYPE_INTEGER) {
321 IWL_DEBUG_RADIO(fwrt,
322 "TAS invalid array elem %d\n", 3 + i);
323 ret = -EINVAL;
324 goto out_free;
325 }
326
327 country = wifi_pkg->package.elements[3 + i].integer.value;
328 tas_data->block_list_array[i] = cpu_to_le32(country);
329 IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
330 }
331
332 ret = 1;
333 out_free:
334 kfree(data);
335 return ret;
336 }
337
iwl_acpi_get_mcc(struct iwl_fw_runtime * fwrt,char * mcc)338 int iwl_acpi_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc)
339 {
340 union acpi_object *wifi_pkg, *data;
341 u32 mcc_val;
342 int ret, tbl_rev;
343
344 data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDD_METHOD);
345 if (IS_ERR(data))
346 return PTR_ERR(data);
347
348 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
349 ACPI_WRDD_WIFI_DATA_SIZE,
350 &tbl_rev);
351 if (IS_ERR(wifi_pkg)) {
352 ret = PTR_ERR(wifi_pkg);
353 goto out_free;
354 }
355
356 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
357 tbl_rev != 0) {
358 ret = -EINVAL;
359 goto out_free;
360 }
361
362 mcc_val = wifi_pkg->package.elements[1].integer.value;
363 if (mcc_val != BIOS_MCC_CHINA) {
364 ret = -EINVAL;
365 IWL_DEBUG_RADIO(fwrt, "ACPI WRDD is supported only for CN\n");
366 goto out_free;
367 }
368
369 mcc[0] = (mcc_val >> 8) & 0xff;
370 mcc[1] = mcc_val & 0xff;
371 mcc[2] = '\0';
372
373 ret = 0;
374 out_free:
375 kfree(data);
376 return ret;
377 }
378
iwl_acpi_get_pwr_limit(struct iwl_fw_runtime * fwrt,u64 * dflt_pwr_limit)379 int iwl_acpi_get_pwr_limit(struct iwl_fw_runtime *fwrt, u64 *dflt_pwr_limit)
380 {
381 union acpi_object *data, *wifi_pkg;
382 int tbl_rev, ret = -EINVAL;
383
384 *dflt_pwr_limit = 0;
385 data = iwl_acpi_get_object(fwrt->dev, ACPI_SPLC_METHOD);
386 if (IS_ERR(data))
387 goto out;
388
389 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
390 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
391 if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
392 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER)
393 goto out_free;
394
395 *dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
396 ret = 0;
397 out_free:
398 kfree(data);
399 out:
400 return ret;
401 }
402
iwl_acpi_get_eckv(struct iwl_fw_runtime * fwrt,u32 * extl_clk)403 int iwl_acpi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk)
404 {
405 union acpi_object *wifi_pkg, *data;
406 int ret, tbl_rev;
407
408 data = iwl_acpi_get_object(fwrt->dev, ACPI_ECKV_METHOD);
409 if (IS_ERR(data))
410 return PTR_ERR(data);
411
412 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
413 ACPI_ECKV_WIFI_DATA_SIZE,
414 &tbl_rev);
415 if (IS_ERR(wifi_pkg)) {
416 ret = PTR_ERR(wifi_pkg);
417 goto out_free;
418 }
419
420 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
421 tbl_rev != 0) {
422 ret = -EINVAL;
423 goto out_free;
424 }
425
426 *extl_clk = wifi_pkg->package.elements[1].integer.value;
427
428 ret = 0;
429
430 out_free:
431 kfree(data);
432 return ret;
433 }
434
435 static int
iwl_acpi_parse_chains_table(union acpi_object * table,struct iwl_sar_profile_chain * chains,u8 num_chains,u8 num_sub_bands)436 iwl_acpi_parse_chains_table(union acpi_object *table,
437 struct iwl_sar_profile_chain *chains,
438 u8 num_chains, u8 num_sub_bands)
439 {
440 for (u8 chain = 0; chain < num_chains; chain++) {
441 for (u8 subband = 0; subband < BIOS_SAR_MAX_SUB_BANDS_NUM;
442 subband++) {
443 /* if we don't have the values, use the default */
444 if (subband >= num_sub_bands) {
445 chains[chain].subbands[subband] = 0;
446 } else if (table->type != ACPI_TYPE_INTEGER ||
447 table->integer.value > U8_MAX) {
448 return -EINVAL;
449 } else {
450 chains[chain].subbands[subband] =
451 table->integer.value;
452 table++;
453 }
454 }
455 }
456
457 return 0;
458 }
459
iwl_acpi_get_wrds_table(struct iwl_fw_runtime * fwrt)460 int iwl_acpi_get_wrds_table(struct iwl_fw_runtime *fwrt)
461 {
462 union acpi_object *wifi_pkg, *table, *data;
463 int ret, tbl_rev;
464 u32 flags;
465 u8 num_chains, num_sub_bands;
466
467 data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
468 if (IS_ERR(data))
469 return PTR_ERR(data);
470
471 /* start by trying to read revision 2 */
472 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
473 ACPI_WRDS_WIFI_DATA_SIZE_REV2,
474 &tbl_rev);
475 if (!IS_ERR(wifi_pkg)) {
476 if (tbl_rev != 2) {
477 ret = -EINVAL;
478 goto out_free;
479 }
480
481 num_chains = ACPI_SAR_NUM_CHAINS_REV2;
482 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
483
484 goto read_table;
485 }
486
487 /* then try revision 1 */
488 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
489 ACPI_WRDS_WIFI_DATA_SIZE_REV1,
490 &tbl_rev);
491 if (!IS_ERR(wifi_pkg)) {
492 if (tbl_rev != 1) {
493 ret = -EINVAL;
494 goto out_free;
495 }
496
497 num_chains = ACPI_SAR_NUM_CHAINS_REV1;
498 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
499
500 goto read_table;
501 }
502
503 /* then finally revision 0 */
504 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
505 ACPI_WRDS_WIFI_DATA_SIZE_REV0,
506 &tbl_rev);
507 if (!IS_ERR(wifi_pkg)) {
508 if (tbl_rev != 0) {
509 ret = -EINVAL;
510 goto out_free;
511 }
512
513 num_chains = ACPI_SAR_NUM_CHAINS_REV0;
514 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
515
516 goto read_table;
517 }
518
519 ret = PTR_ERR(wifi_pkg);
520 goto out_free;
521
522 read_table:
523 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
524 ret = -EINVAL;
525 goto out_free;
526 }
527
528 IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
529
530 flags = wifi_pkg->package.elements[1].integer.value;
531 fwrt->reduced_power_flags = flags >> IWL_REDUCE_POWER_FLAGS_POS;
532
533 /* position of the actual table */
534 table = &wifi_pkg->package.elements[2];
535
536 /* The profile from WRDS is officially profile 1, but goes
537 * into sar_profiles[0] (because we don't have a profile 0).
538 */
539 ret = iwl_acpi_parse_chains_table(table, fwrt->sar_profiles[0].chains,
540 num_chains, num_sub_bands);
541 if (!ret && flags & IWL_SAR_ENABLE_MSK)
542 fwrt->sar_profiles[0].enabled = true;
543
544 out_free:
545 kfree(data);
546 return ret;
547 }
548
iwl_acpi_get_ewrd_table(struct iwl_fw_runtime * fwrt)549 int iwl_acpi_get_ewrd_table(struct iwl_fw_runtime *fwrt)
550 {
551 union acpi_object *wifi_pkg, *data;
552 bool enabled;
553 int i, n_profiles, tbl_rev, pos;
554 int ret = 0;
555 u8 num_sub_bands;
556
557 data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
558 if (IS_ERR(data))
559 return PTR_ERR(data);
560
561 /* start by trying to read revision 2 */
562 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
563 ACPI_EWRD_WIFI_DATA_SIZE_REV2,
564 &tbl_rev);
565 if (!IS_ERR(wifi_pkg)) {
566 if (tbl_rev != 2) {
567 ret = -EINVAL;
568 goto out_free;
569 }
570
571 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
572
573 goto read_table;
574 }
575
576 /* then try revision 1 */
577 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
578 ACPI_EWRD_WIFI_DATA_SIZE_REV1,
579 &tbl_rev);
580 if (!IS_ERR(wifi_pkg)) {
581 if (tbl_rev != 1) {
582 ret = -EINVAL;
583 goto out_free;
584 }
585
586 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
587
588 goto read_table;
589 }
590
591 /* then finally revision 0 */
592 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
593 ACPI_EWRD_WIFI_DATA_SIZE_REV0,
594 &tbl_rev);
595 if (!IS_ERR(wifi_pkg)) {
596 if (tbl_rev != 0) {
597 ret = -EINVAL;
598 goto out_free;
599 }
600
601 num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
602
603 goto read_table;
604 }
605
606 ret = PTR_ERR(wifi_pkg);
607 goto out_free;
608
609 read_table:
610 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
611 wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
612 ret = -EINVAL;
613 goto out_free;
614 }
615
616 enabled = !!(wifi_pkg->package.elements[1].integer.value);
617 n_profiles = wifi_pkg->package.elements[2].integer.value;
618
619 /*
620 * Check the validity of n_profiles. The EWRD profiles start
621 * from index 1, so the maximum value allowed here is
622 * ACPI_SAR_PROFILES_NUM - 1.
623 */
624 if (n_profiles >= BIOS_SAR_MAX_PROFILE_NUM) {
625 ret = -EINVAL;
626 goto out_free;
627 }
628
629 /* the tables start at element 3 */
630 pos = 3;
631
632 BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV0 != ACPI_SAR_NUM_CHAINS_REV1);
633 BUILD_BUG_ON(ACPI_SAR_NUM_CHAINS_REV2 != 2 * ACPI_SAR_NUM_CHAINS_REV0);
634
635 /* parse non-cdb chains for all profiles */
636 for (i = 0; i < n_profiles; i++) {
637 union acpi_object *table = &wifi_pkg->package.elements[pos];
638
639 /* The EWRD profiles officially go from 2 to 4, but we
640 * save them in sar_profiles[1-3] (because we don't
641 * have profile 0). So in the array we start from 1.
642 */
643 ret = iwl_acpi_parse_chains_table(table,
644 fwrt->sar_profiles[i + 1].chains,
645 ACPI_SAR_NUM_CHAINS_REV0,
646 num_sub_bands);
647 if (ret < 0)
648 goto out_free;
649
650 /* go to the next table */
651 pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
652 }
653
654 /* non-cdb table revisions */
655 if (tbl_rev < 2)
656 goto set_enabled;
657
658 /* parse cdb chains for all profiles */
659 for (i = 0; i < n_profiles; i++) {
660 struct iwl_sar_profile_chain *chains;
661 union acpi_object *table;
662
663 table = &wifi_pkg->package.elements[pos];
664 chains = &fwrt->sar_profiles[i + 1].chains[ACPI_SAR_NUM_CHAINS_REV0];
665 ret = iwl_acpi_parse_chains_table(table,
666 chains,
667 ACPI_SAR_NUM_CHAINS_REV0,
668 num_sub_bands);
669 if (ret < 0)
670 goto out_free;
671
672 /* go to the next table */
673 pos += ACPI_SAR_NUM_CHAINS_REV0 * num_sub_bands;
674 }
675
676 set_enabled:
677 for (i = 0; i < n_profiles; i++)
678 fwrt->sar_profiles[i + 1].enabled = enabled;
679
680 out_free:
681 kfree(data);
682 return ret;
683 }
684
iwl_acpi_get_wgds_table(struct iwl_fw_runtime * fwrt)685 int iwl_acpi_get_wgds_table(struct iwl_fw_runtime *fwrt)
686 {
687 union acpi_object *wifi_pkg, *data;
688 int i, j, k, ret, tbl_rev;
689 u8 num_bands, num_profiles;
690 static const struct {
691 u8 revisions;
692 u8 bands;
693 u8 profiles;
694 u8 min_profiles;
695 } rev_data[] = {
696 {
697 .revisions = BIT(3),
698 .bands = ACPI_GEO_NUM_BANDS_REV2,
699 .profiles = ACPI_NUM_GEO_PROFILES_REV3,
700 .min_profiles = BIOS_GEO_MIN_PROFILE_NUM,
701 },
702 {
703 .revisions = BIT(2),
704 .bands = ACPI_GEO_NUM_BANDS_REV2,
705 .profiles = ACPI_NUM_GEO_PROFILES,
706 },
707 {
708 .revisions = BIT(0) | BIT(1),
709 .bands = ACPI_GEO_NUM_BANDS_REV0,
710 .profiles = ACPI_NUM_GEO_PROFILES,
711 },
712 };
713 int idx;
714 /* start from one to skip the domain */
715 int entry_idx = 1;
716
717 BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES_REV3 != IWL_NUM_GEO_PROFILES_V3);
718 BUILD_BUG_ON(ACPI_NUM_GEO_PROFILES != IWL_NUM_GEO_PROFILES);
719
720 data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
721 if (IS_ERR(data))
722 return PTR_ERR(data);
723
724 /* read the highest revision we understand first */
725 for (idx = 0; idx < ARRAY_SIZE(rev_data); idx++) {
726 /* min_profiles != 0 requires num_profiles header */
727 u32 hdr_size = 1 + !!rev_data[idx].min_profiles;
728 u32 profile_size = ACPI_GEO_PER_CHAIN_SIZE *
729 rev_data[idx].bands;
730 u32 max_size = hdr_size + profile_size * rev_data[idx].profiles;
731 u32 min_size;
732
733 if (!rev_data[idx].min_profiles)
734 min_size = max_size;
735 else
736 min_size = hdr_size +
737 profile_size * rev_data[idx].min_profiles;
738
739 wifi_pkg = iwl_acpi_get_wifi_pkg_range(fwrt->dev, data,
740 min_size, max_size,
741 &tbl_rev);
742 if (!IS_ERR(wifi_pkg)) {
743 if (!(BIT(tbl_rev) & rev_data[idx].revisions))
744 continue;
745
746 num_bands = rev_data[idx].bands;
747 num_profiles = rev_data[idx].profiles;
748
749 if (rev_data[idx].min_profiles) {
750 /* read header that says # of profiles */
751 union acpi_object *entry;
752
753 entry = &wifi_pkg->package.elements[entry_idx];
754 entry_idx++;
755 if (entry->type != ACPI_TYPE_INTEGER ||
756 entry->integer.value > num_profiles ||
757 entry->integer.value <
758 rev_data[idx].min_profiles) {
759 ret = -EINVAL;
760 goto out_free;
761 }
762
763 /*
764 * Check to see if we received package count
765 * same as max # of profiles
766 */
767 if (wifi_pkg->package.count !=
768 hdr_size + profile_size * num_profiles) {
769 ret = -EINVAL;
770 goto out_free;
771 }
772
773 /* Number of valid profiles */
774 num_profiles = entry->integer.value;
775 }
776 goto read_table;
777 }
778 }
779
780 if (idx < ARRAY_SIZE(rev_data))
781 ret = PTR_ERR(wifi_pkg);
782 else
783 ret = -ENOENT;
784 goto out_free;
785
786 read_table:
787 fwrt->geo_rev = tbl_rev;
788 for (i = 0; i < num_profiles; i++) {
789 for (j = 0; j < BIOS_GEO_MAX_NUM_BANDS; j++) {
790 union acpi_object *entry;
791
792 /*
793 * num_bands is either 2 or 3, if it's only 2 then
794 * fill the third band (6 GHz) with the values from
795 * 5 GHz (second band)
796 */
797 if (j >= num_bands) {
798 fwrt->geo_profiles[i].bands[j].max =
799 fwrt->geo_profiles[i].bands[1].max;
800 } else {
801 entry = &wifi_pkg->package.elements[entry_idx];
802 entry_idx++;
803 if (entry->type != ACPI_TYPE_INTEGER ||
804 entry->integer.value > U8_MAX) {
805 ret = -EINVAL;
806 goto out_free;
807 }
808
809 fwrt->geo_profiles[i].bands[j].max =
810 entry->integer.value;
811 }
812
813 for (k = 0; k < BIOS_GEO_NUM_CHAINS; k++) {
814 /* same here as above */
815 if (j >= num_bands) {
816 fwrt->geo_profiles[i].bands[j].chains[k] =
817 fwrt->geo_profiles[i].bands[1].chains[k];
818 } else {
819 entry = &wifi_pkg->package.elements[entry_idx];
820 entry_idx++;
821 if (entry->type != ACPI_TYPE_INTEGER ||
822 entry->integer.value > U8_MAX) {
823 ret = -EINVAL;
824 goto out_free;
825 }
826
827 fwrt->geo_profiles[i].bands[j].chains[k] =
828 entry->integer.value;
829 }
830 }
831 }
832 }
833
834 fwrt->geo_num_profiles = num_profiles;
835 fwrt->geo_enabled = true;
836 ret = 0;
837 out_free:
838 kfree(data);
839 return ret;
840 }
841
iwl_acpi_get_ppag_table(struct iwl_fw_runtime * fwrt)842 int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt)
843 {
844 union acpi_object *wifi_pkg, *data, *flags;
845 int i, j, ret, tbl_rev, num_sub_bands = 0;
846 int idx = 2;
847
848 data = iwl_acpi_get_object(fwrt->dev, ACPI_PPAG_METHOD);
849 if (IS_ERR(data))
850 return PTR_ERR(data);
851
852 /* try to read ppag table rev 3, 2 or 1 (all have the same data size) */
853 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
854 ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev);
855
856 if (!IS_ERR(wifi_pkg)) {
857 if (tbl_rev >= 1 && tbl_rev <= 3) {
858 num_sub_bands = IWL_NUM_SUB_BANDS_V2;
859 IWL_DEBUG_RADIO(fwrt,
860 "Reading PPAG table (tbl_rev=%d)\n",
861 tbl_rev);
862 goto read_table;
863 } else {
864 ret = -EINVAL;
865 goto out_free;
866 }
867 }
868
869 /* try to read ppag table revision 0 */
870 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
871 ACPI_PPAG_WIFI_DATA_SIZE_V1, &tbl_rev);
872
873 if (!IS_ERR(wifi_pkg)) {
874 if (tbl_rev != 0) {
875 ret = -EINVAL;
876 goto out_free;
877 }
878 num_sub_bands = IWL_NUM_SUB_BANDS_V1;
879 IWL_DEBUG_RADIO(fwrt, "Reading PPAG table v1 (tbl_rev=0)\n");
880 goto read_table;
881 }
882
883 ret = PTR_ERR(wifi_pkg);
884 goto out_free;
885
886 read_table:
887 fwrt->ppag_ver = tbl_rev;
888 flags = &wifi_pkg->package.elements[1];
889
890 if (flags->type != ACPI_TYPE_INTEGER) {
891 ret = -EINVAL;
892 goto out_free;
893 }
894
895 fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value,
896 fwrt->ppag_ver);
897
898 /*
899 * read, verify gain values and save them into the PPAG table.
900 * first sub-band (j=0) corresponds to Low-Band (2.4GHz), and the
901 * following sub-bands to High-Band (5GHz).
902 */
903 for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) {
904 for (j = 0; j < num_sub_bands; j++) {
905 union acpi_object *ent;
906
907 ent = &wifi_pkg->package.elements[idx++];
908 if (ent->type != ACPI_TYPE_INTEGER) {
909 ret = -EINVAL;
910 goto out_free;
911 }
912
913 fwrt->ppag_chains[i].subbands[j] = ent->integer.value;
914 }
915 }
916
917 ret = 0;
918
919 out_free:
920 kfree(data);
921 return ret;
922 }
923
iwl_acpi_get_phy_filters(struct iwl_fw_runtime * fwrt,struct iwl_phy_specific_cfg * filters)924 void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt,
925 struct iwl_phy_specific_cfg *filters)
926 {
927 struct iwl_phy_specific_cfg tmp = {};
928 union acpi_object *wifi_pkg, *data;
929 int tbl_rev, i;
930
931 data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD);
932 if (IS_ERR(data))
933 return;
934
935 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
936 ACPI_WPFC_WIFI_DATA_SIZE,
937 &tbl_rev);
938 if (IS_ERR(wifi_pkg))
939 goto out_free;
940
941 if (tbl_rev != 0)
942 goto out_free;
943
944 BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) !=
945 ACPI_WPFC_WIFI_DATA_SIZE - 1);
946
947 for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) {
948 if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER)
949 goto out_free;
950 tmp.filter_cfg_chains[i] =
951 cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value);
952 }
953
954 IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n");
955 *filters = tmp;
956 out_free:
957 kfree(data);
958 }
959 IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters);
960
iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime * fwrt)961 void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt)
962 {
963 union acpi_object *wifi_pkg, *data;
964 int tbl_rev;
965
966 data = iwl_acpi_get_object(fwrt->dev, ACPI_GLAI_METHOD);
967 if (IS_ERR(data))
968 return;
969
970 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
971 ACPI_GLAI_WIFI_DATA_SIZE,
972 &tbl_rev);
973 if (IS_ERR(wifi_pkg))
974 goto out_free;
975
976 if (tbl_rev != 0) {
977 IWL_DEBUG_RADIO(fwrt, "Invalid GLAI revision: %d\n", tbl_rev);
978 goto out_free;
979 }
980
981 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
982 wifi_pkg->package.elements[1].integer.value > ACPI_GLAI_MAX_STATUS)
983 goto out_free;
984
985 fwrt->uefi_tables_lock_status =
986 wifi_pkg->package.elements[1].integer.value;
987
988 IWL_DEBUG_RADIO(fwrt,
989 "Loaded UEFI WIFI GUID lock status: %d from ACPI\n",
990 fwrt->uefi_tables_lock_status);
991 out_free:
992 kfree(data);
993 }
994 IWL_EXPORT_SYMBOL(iwl_acpi_get_guid_lock_status);
995
iwl_acpi_get_wbem(struct iwl_fw_runtime * fwrt,u32 * value)996 int iwl_acpi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value)
997 {
998 union acpi_object *wifi_pkg, *data;
999 int ret = -ENOENT;
1000 int tbl_rev;
1001
1002 data = iwl_acpi_get_object(fwrt->dev, ACPI_WBEM_METHOD);
1003 if (IS_ERR(data))
1004 return ret;
1005
1006 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
1007 ACPI_WBEM_WIFI_DATA_SIZE,
1008 &tbl_rev);
1009 if (IS_ERR(wifi_pkg))
1010 goto out_free;
1011
1012 if (tbl_rev != IWL_ACPI_WBEM_REVISION) {
1013 IWL_DEBUG_RADIO(fwrt, "Unsupported ACPI WBEM revision:%d\n",
1014 tbl_rev);
1015 goto out_free;
1016 }
1017
1018 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER)
1019 goto out_free;
1020
1021 *value = wifi_pkg->package.elements[1].integer.value &
1022 IWL_ACPI_WBEM_REV0_MASK;
1023 IWL_DEBUG_RADIO(fwrt, "Loaded WBEM config from ACPI\n");
1024 ret = 0;
1025 out_free:
1026 kfree(data);
1027 return ret;
1028 }
1029