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