Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/iwlwifi/fw/acpi.c
48287 views
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
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
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
*/
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
*/
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
*/
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1085