hc
2024-01-03 2f7c68cb55ecb7331f2381deb497c27155f32faf
kernel/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
....@@ -6,6 +6,7 @@
66 * GPL LICENSE SUMMARY
77 *
88 * Copyright(c) 2017 Intel Deutschland GmbH
9
+ * Copyright (C) 2019 - 2020 Intel Corporation
910 *
1011 * This program is free software; you can redistribute it and/or modify
1112 * it under the terms of version 2 of the GNU General Public License as
....@@ -15,9 +16,6 @@
1516 * WITHOUT ANY WARRANTY; without even the implied warranty of
1617 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1718 * General Public License for more details.
18
- *
19
- * You should have received a copy of the GNU General Public License
20
- * along with this program;
2119 *
2220 * The full GNU General Public License is included in this distribution
2321 * in the file called COPYING.
....@@ -29,6 +27,7 @@
2927 * BSD LICENSE
3028 *
3129 * Copyright(c) 2017 Intel Deutschland GmbH
30
+ * Copyright (C) 2019 - 2020 Intel Corporation
3231 * All rights reserved.
3332 *
3433 * Redistribution and use in source and binary forms, with or without
....@@ -59,46 +58,124 @@
5958 *
6059 *****************************************************************************/
6160
61
+#include <linux/uuid.h>
6262 #include "iwl-drv.h"
6363 #include "iwl-debug.h"
6464 #include "acpi.h"
65
+#include "fw/runtime.h"
6566
66
-void *iwl_acpi_get_object(struct device *dev, acpi_string method)
67
+static const guid_t intel_wifi_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
68
+ 0xA5, 0xB3, 0x1F, 0x73,
69
+ 0x8E, 0x28, 0x5A, 0xDE);
70
+
71
+static int iwl_acpi_get_handle(struct device *dev, acpi_string method,
72
+ acpi_handle *ret_handle)
6773 {
6874 acpi_handle root_handle;
69
- acpi_handle handle;
70
- struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
7175 acpi_status status;
7276
7377 root_handle = ACPI_HANDLE(dev);
7478 if (!root_handle) {
7579 IWL_DEBUG_DEV_RADIO(dev,
76
- "Could not retrieve root port ACPI handle\n");
77
- return ERR_PTR(-ENOENT);
80
+ "ACPI: Could not retrieve root port handle\n");
81
+ return -ENOENT;
7882 }
7983
80
- /* Get the method's handle */
81
- status = acpi_get_handle(root_handle, method, &handle);
84
+ status = acpi_get_handle(root_handle, method, ret_handle);
8285 if (ACPI_FAILURE(status)) {
83
- IWL_DEBUG_DEV_RADIO(dev, "%s method not found\n", method);
84
- return ERR_PTR(-ENOENT);
86
+ IWL_DEBUG_DEV_RADIO(dev,
87
+ "ACPI: %s method not found\n", method);
88
+ return -ENOENT;
8589 }
90
+ return 0;
91
+}
92
+
93
+void *iwl_acpi_get_object(struct device *dev, acpi_string method)
94
+{
95
+ struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL};
96
+ acpi_handle handle;
97
+ acpi_status status;
98
+ int ret;
99
+
100
+ ret = iwl_acpi_get_handle(dev, method, &handle);
101
+ if (ret)
102
+ return ERR_PTR(-ENOENT);
86103
87104 /* Call the method with no arguments */
88105 status = acpi_evaluate_object(handle, NULL, NULL, &buf);
89106 if (ACPI_FAILURE(status)) {
90
- IWL_DEBUG_DEV_RADIO(dev, "%s invocation failed (0x%x)\n",
107
+ IWL_DEBUG_DEV_RADIO(dev,
108
+ "ACPI: %s method invocation failed (status: 0x%x)\n",
91109 method, status);
92110 return ERR_PTR(-ENOENT);
93111 }
94
-
95112 return buf.pointer;
96113 }
97114 IWL_EXPORT_SYMBOL(iwl_acpi_get_object);
98115
116
+/**
117
+* Generic function for evaluating a method defined in the device specific
118
+* method (DSM) interface. The returned acpi object must be freed by calling
119
+* function.
120
+*/
121
+static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
122
+ union acpi_object *args)
123
+{
124
+ union acpi_object *obj;
125
+
126
+ obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &intel_wifi_guid, rev, func,
127
+ args);
128
+ if (!obj) {
129
+ IWL_DEBUG_DEV_RADIO(dev,
130
+ "ACPI: DSM method invocation failed (rev: %d, func:%d)\n",
131
+ rev, func);
132
+ return ERR_PTR(-ENOENT);
133
+ }
134
+ return obj;
135
+}
136
+
137
+/**
138
+ * Evaluate a DSM with no arguments and a single u8 return value (inside a
139
+ * buffer object), verify and return that value.
140
+ */
141
+int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func)
142
+{
143
+ union acpi_object *obj;
144
+ int ret;
145
+
146
+ obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL);
147
+ if (IS_ERR(obj))
148
+ return -ENOENT;
149
+
150
+ if (obj->type != ACPI_TYPE_BUFFER) {
151
+ IWL_DEBUG_DEV_RADIO(dev,
152
+ "ACPI: DSM method did not return a valid object, type=%d\n",
153
+ obj->type);
154
+ ret = -EINVAL;
155
+ goto out;
156
+ }
157
+
158
+ if (obj->buffer.length != sizeof(u8)) {
159
+ IWL_DEBUG_DEV_RADIO(dev,
160
+ "ACPI: DSM method returned invalid buffer, length=%d\n",
161
+ obj->buffer.length);
162
+ ret = -EINVAL;
163
+ goto out;
164
+ }
165
+
166
+ ret = obj->buffer.pointer[0];
167
+ IWL_DEBUG_DEV_RADIO(dev,
168
+ "ACPI: DSM method evaluated: func=%d, ret=%d\n",
169
+ func, ret);
170
+out:
171
+ ACPI_FREE(obj);
172
+ return ret;
173
+}
174
+IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8);
175
+
99176 union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
100177 union acpi_object *data,
101
- int data_size)
178
+ int data_size, int *tbl_rev)
102179 {
103180 int i;
104181 union acpi_object *wifi_pkg;
....@@ -114,15 +191,18 @@
114191 /*
115192 * We need at least two packages, one for the revision and one
116193 * for the data itself. Also check that the revision is valid
117
- * (i.e. it is an integer set to 0).
194
+ * (i.e. it is an integer smaller than 2, as we currently support only
195
+ * 2 revisions).
118196 */
119197 if (data->type != ACPI_TYPE_PACKAGE ||
120198 data->package.count < 2 ||
121199 data->package.elements[0].type != ACPI_TYPE_INTEGER ||
122
- data->package.elements[0].integer.value != 0) {
200
+ data->package.elements[0].integer.value > 1) {
123201 IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n");
124202 return ERR_PTR(-EINVAL);
125203 }
204
+
205
+ *tbl_rev = data->package.elements[0].integer.value;
126206
127207 /* loop through all the packages to find the one for WiFi */
128208 for (i = 1; i < data->package.count; i++) {
....@@ -148,23 +228,101 @@
148228 }
149229 IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg);
150230
151
-int iwl_acpi_get_mcc(struct device *dev, char *mcc)
231
+int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt,
232
+ __le32 *block_list_array,
233
+ int *block_list_size)
152234 {
153235 union acpi_object *wifi_pkg, *data;
154
- u32 mcc_val;
155
- int ret;
236
+ int ret, tbl_rev, i;
237
+ bool enabled;
156238
157
- data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
239
+ data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
158240 if (IS_ERR(data))
159241 return PTR_ERR(data);
160242
161
- wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE);
243
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
244
+ ACPI_WTAS_WIFI_DATA_SIZE,
245
+ &tbl_rev);
162246 if (IS_ERR(wifi_pkg)) {
163247 ret = PTR_ERR(wifi_pkg);
164248 goto out_free;
165249 }
166250
167
- if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
251
+ if (wifi_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
252
+ tbl_rev != 0) {
253
+ ret = -EINVAL;
254
+ goto out_free;
255
+ }
256
+
257
+ enabled = !!wifi_pkg->package.elements[1].integer.value;
258
+
259
+ if (!enabled) {
260
+ *block_list_size = -1;
261
+ IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");
262
+ ret = 0;
263
+ goto out_free;
264
+ }
265
+
266
+ if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
267
+ wifi_pkg->package.elements[2].integer.value >
268
+ APCI_WTAS_BLACK_LIST_MAX) {
269
+ IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
270
+ wifi_pkg->package.elements[1].integer.value);
271
+ ret = -EINVAL;
272
+ goto out_free;
273
+ }
274
+ *block_list_size = wifi_pkg->package.elements[2].integer.value;
275
+
276
+ IWL_DEBUG_RADIO(fwrt, "TAS array size %d\n", *block_list_size);
277
+ if (*block_list_size > APCI_WTAS_BLACK_LIST_MAX) {
278
+ IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n",
279
+ *block_list_size);
280
+ ret = -EINVAL;
281
+ goto out_free;
282
+ }
283
+
284
+ for (i = 0; i < *block_list_size; i++) {
285
+ u32 country;
286
+
287
+ if (wifi_pkg->package.elements[3 + i].type !=
288
+ ACPI_TYPE_INTEGER) {
289
+ IWL_DEBUG_RADIO(fwrt,
290
+ "TAS invalid array elem %d\n", 3 + i);
291
+ ret = -EINVAL;
292
+ goto out_free;
293
+ }
294
+
295
+ country = wifi_pkg->package.elements[3 + i].integer.value;
296
+ block_list_array[i] = cpu_to_le32(country);
297
+ IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
298
+ }
299
+
300
+ ret = 0;
301
+out_free:
302
+ kfree(data);
303
+ return ret;
304
+}
305
+IWL_EXPORT_SYMBOL(iwl_acpi_get_tas);
306
+
307
+int iwl_acpi_get_mcc(struct device *dev, char *mcc)
308
+{
309
+ union acpi_object *wifi_pkg, *data;
310
+ u32 mcc_val;
311
+ int ret, tbl_rev;
312
+
313
+ data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
314
+ if (IS_ERR(data))
315
+ return PTR_ERR(data);
316
+
317
+ wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE,
318
+ &tbl_rev);
319
+ if (IS_ERR(wifi_pkg)) {
320
+ ret = PTR_ERR(wifi_pkg);
321
+ goto out_free;
322
+ }
323
+
324
+ if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
325
+ tbl_rev != 0) {
168326 ret = -EINVAL;
169327 goto out_free;
170328 }
....@@ -186,6 +344,7 @@
186344 {
187345 union acpi_object *data, *wifi_pkg;
188346 u64 dflt_pwr_limit;
347
+ int tbl_rev;
189348
190349 data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
191350 if (IS_ERR(data)) {
....@@ -194,8 +353,8 @@
194353 }
195354
196355 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
197
- ACPI_SPLC_WIFI_DATA_SIZE);
198
- if (IS_ERR(wifi_pkg) ||
356
+ ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
357
+ if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
199358 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
200359 dflt_pwr_limit = 0;
201360 goto out_free;
....@@ -208,3 +367,326 @@
208367 return dflt_pwr_limit;
209368 }
210369 IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
370
+
371
+int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
372
+{
373
+ union acpi_object *wifi_pkg, *data;
374
+ int ret, tbl_rev;
375
+
376
+ data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
377
+ if (IS_ERR(data))
378
+ return PTR_ERR(data);
379
+
380
+ wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE,
381
+ &tbl_rev);
382
+ if (IS_ERR(wifi_pkg)) {
383
+ ret = PTR_ERR(wifi_pkg);
384
+ goto out_free;
385
+ }
386
+
387
+ if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
388
+ tbl_rev != 0) {
389
+ ret = -EINVAL;
390
+ goto out_free;
391
+ }
392
+
393
+ *extl_clk = wifi_pkg->package.elements[1].integer.value;
394
+
395
+ ret = 0;
396
+
397
+out_free:
398
+ kfree(data);
399
+ return ret;
400
+}
401
+IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv);
402
+
403
+static int iwl_sar_set_profile(union acpi_object *table,
404
+ struct iwl_sar_profile *profile,
405
+ bool enabled)
406
+{
407
+ int i;
408
+
409
+ profile->enabled = enabled;
410
+
411
+ for (i = 0; i < ACPI_SAR_TABLE_SIZE; i++) {
412
+ if (table[i].type != ACPI_TYPE_INTEGER ||
413
+ table[i].integer.value > U8_MAX)
414
+ return -EINVAL;
415
+
416
+ profile->table[i] = table[i].integer.value;
417
+ }
418
+
419
+ return 0;
420
+}
421
+
422
+static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
423
+ __le16 *per_chain, u32 n_subbands,
424
+ int prof_a, int prof_b)
425
+{
426
+ int profs[ACPI_SAR_NUM_CHAIN_LIMITS] = { prof_a, prof_b };
427
+ int i, j, idx;
428
+
429
+ for (i = 0; i < ACPI_SAR_NUM_CHAIN_LIMITS; i++) {
430
+ struct iwl_sar_profile *prof;
431
+
432
+ /* don't allow SAR to be disabled (profile 0 means disable) */
433
+ if (profs[i] == 0)
434
+ return -EPERM;
435
+
436
+ /* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */
437
+ if (profs[i] > ACPI_SAR_PROFILE_NUM)
438
+ return -EINVAL;
439
+
440
+ /* profiles go from 1 to 4, so decrement to access the array */
441
+ prof = &fwrt->sar_profiles[profs[i] - 1];
442
+
443
+ /* if the profile is disabled, do nothing */
444
+ if (!prof->enabled) {
445
+ IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n",
446
+ profs[i]);
447
+ /*
448
+ * if one of the profiles is disabled, we
449
+ * ignore all of them and return 1 to
450
+ * differentiate disabled from other failures.
451
+ */
452
+ return 1;
453
+ }
454
+
455
+ IWL_DEBUG_INFO(fwrt,
456
+ "SAR EWRD: chain %d profile index %d\n",
457
+ i, profs[i]);
458
+ IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i);
459
+ for (j = 0; j < n_subbands; j++) {
460
+ idx = i * ACPI_SAR_NUM_SUB_BANDS + j;
461
+ per_chain[i * n_subbands + j] =
462
+ cpu_to_le16(prof->table[idx]);
463
+ IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n",
464
+ j, prof->table[idx]);
465
+ }
466
+ }
467
+
468
+ return 0;
469
+}
470
+
471
+int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt,
472
+ __le16 *per_chain, u32 n_tables, u32 n_subbands,
473
+ int prof_a, int prof_b)
474
+{
475
+ int i, ret = 0;
476
+
477
+ for (i = 0; i < n_tables; i++) {
478
+ ret = iwl_sar_fill_table(fwrt,
479
+ &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAIN_LIMITS],
480
+ n_subbands, prof_a, prof_b);
481
+ if (ret)
482
+ break;
483
+ }
484
+
485
+ return ret;
486
+}
487
+IWL_EXPORT_SYMBOL(iwl_sar_select_profile);
488
+
489
+int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt)
490
+{
491
+ union acpi_object *wifi_pkg, *table, *data;
492
+ bool enabled;
493
+ int ret, tbl_rev;
494
+
495
+ data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
496
+ if (IS_ERR(data))
497
+ return PTR_ERR(data);
498
+
499
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
500
+ ACPI_WRDS_WIFI_DATA_SIZE, &tbl_rev);
501
+ if (IS_ERR(wifi_pkg) || tbl_rev != 0) {
502
+ ret = PTR_ERR(wifi_pkg);
503
+ goto out_free;
504
+ }
505
+
506
+ if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
507
+ ret = -EINVAL;
508
+ goto out_free;
509
+ }
510
+
511
+ enabled = !!(wifi_pkg->package.elements[1].integer.value);
512
+
513
+ /* position of the actual table */
514
+ table = &wifi_pkg->package.elements[2];
515
+
516
+ /* The profile from WRDS is officially profile 1, but goes
517
+ * into sar_profiles[0] (because we don't have a profile 0).
518
+ */
519
+ ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], enabled);
520
+out_free:
521
+ kfree(data);
522
+ return ret;
523
+}
524
+IWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table);
525
+
526
+int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt)
527
+{
528
+ union acpi_object *wifi_pkg, *data;
529
+ bool enabled;
530
+ int i, n_profiles, tbl_rev, pos;
531
+ int ret = 0;
532
+
533
+ data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
534
+ if (IS_ERR(data))
535
+ return PTR_ERR(data);
536
+
537
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
538
+ ACPI_EWRD_WIFI_DATA_SIZE, &tbl_rev);
539
+ if (IS_ERR(wifi_pkg) || tbl_rev != 0) {
540
+ ret = PTR_ERR(wifi_pkg);
541
+ goto out_free;
542
+ }
543
+
544
+ if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
545
+ wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
546
+ ret = -EINVAL;
547
+ goto out_free;
548
+ }
549
+
550
+ enabled = !!(wifi_pkg->package.elements[1].integer.value);
551
+ n_profiles = wifi_pkg->package.elements[2].integer.value;
552
+
553
+ /*
554
+ * Check the validity of n_profiles. The EWRD profiles start
555
+ * from index 1, so the maximum value allowed here is
556
+ * ACPI_SAR_PROFILES_NUM - 1.
557
+ */
558
+ if (n_profiles <= 0 || n_profiles >= ACPI_SAR_PROFILE_NUM) {
559
+ ret = -EINVAL;
560
+ goto out_free;
561
+ }
562
+
563
+ /* the tables start at element 3 */
564
+ pos = 3;
565
+
566
+ for (i = 0; i < n_profiles; i++) {
567
+ /* The EWRD profiles officially go from 2 to 4, but we
568
+ * save them in sar_profiles[1-3] (because we don't
569
+ * have profile 0). So in the array we start from 1.
570
+ */
571
+ ret = iwl_sar_set_profile(&wifi_pkg->package.elements[pos],
572
+ &fwrt->sar_profiles[i + 1],
573
+ enabled);
574
+ if (ret < 0)
575
+ break;
576
+
577
+ /* go to the next table */
578
+ pos += ACPI_SAR_TABLE_SIZE;
579
+ }
580
+
581
+out_free:
582
+ kfree(data);
583
+ return ret;
584
+}
585
+IWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table);
586
+
587
+int iwl_sar_get_wgds_table(struct iwl_fw_runtime *fwrt)
588
+{
589
+ union acpi_object *wifi_pkg, *data;
590
+ int i, j, ret, tbl_rev;
591
+ int idx = 1;
592
+
593
+ data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
594
+ if (IS_ERR(data))
595
+ return PTR_ERR(data);
596
+
597
+ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
598
+ ACPI_WGDS_WIFI_DATA_SIZE, &tbl_rev);
599
+ if (IS_ERR(wifi_pkg) || tbl_rev > 1) {
600
+ ret = PTR_ERR(wifi_pkg);
601
+ goto out_free;
602
+ }
603
+
604
+ fwrt->geo_rev = tbl_rev;
605
+ for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) {
606
+ for (j = 0; j < ACPI_GEO_TABLE_SIZE; j++) {
607
+ union acpi_object *entry;
608
+
609
+ entry = &wifi_pkg->package.elements[idx++];
610
+ if (entry->type != ACPI_TYPE_INTEGER ||
611
+ entry->integer.value > U8_MAX) {
612
+ ret = -EINVAL;
613
+ goto out_free;
614
+ }
615
+
616
+ fwrt->geo_profiles[i].values[j] = entry->integer.value;
617
+ }
618
+ }
619
+ ret = 0;
620
+out_free:
621
+ kfree(data);
622
+ return ret;
623
+}
624
+IWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table);
625
+
626
+bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
627
+{
628
+ /*
629
+ * The GEO_TX_POWER_LIMIT command is not supported on earlier
630
+ * firmware versions. Unfortunately, we don't have a TLV API
631
+ * flag to rely on, so rely on the major version which is in
632
+ * the first byte of ucode_ver. This was implemented
633
+ * initially on version 38 and then backported to 17. It was
634
+ * also backported to 29, but only for 7265D devices. The
635
+ * intention was to have it in 36 as well, but not all 8000
636
+ * family got this feature enabled. The 8000 family is the
637
+ * only one using version 36, so skip this version entirely.
638
+ */
639
+ return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 ||
640
+ IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 ||
641
+ (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 &&
642
+ ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) ==
643
+ CSR_HW_REV_TYPE_7265D));
644
+}
645
+IWL_EXPORT_SYMBOL(iwl_sar_geo_support);
646
+
647
+int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt,
648
+ struct iwl_per_chain_offset *table, u32 n_bands)
649
+{
650
+ int ret, i, j;
651
+
652
+ if (!iwl_sar_geo_support(fwrt))
653
+ return -EOPNOTSUPP;
654
+
655
+ ret = iwl_sar_get_wgds_table(fwrt);
656
+ if (ret < 0) {
657
+ IWL_DEBUG_RADIO(fwrt,
658
+ "Geo SAR BIOS table invalid or unavailable. (%d)\n",
659
+ ret);
660
+ /* we don't fail if the table is not available */
661
+ return -ENOENT;
662
+ }
663
+
664
+ for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) {
665
+ for (j = 0; j < n_bands; j++) {
666
+ struct iwl_per_chain_offset *chain =
667
+ &table[i * n_bands + j];
668
+ u8 *value;
669
+
670
+ if (j * ACPI_GEO_PER_CHAIN_SIZE >=
671
+ ARRAY_SIZE(fwrt->geo_profiles[0].values))
672
+ /*
673
+ * Currently we only store lb an hb values, and
674
+ * don't have any special ones for uhb. So leave
675
+ * those empty for the time being
676
+ */
677
+ break;
678
+
679
+ value = &fwrt->geo_profiles[i].values[j *
680
+ ACPI_GEO_PER_CHAIN_SIZE];
681
+ chain->max_tx_power = cpu_to_le16(value[0]);
682
+ chain->chain_a = value[1];
683
+ chain->chain_b = value[2];
684
+ IWL_DEBUG_RADIO(fwrt,
685
+ "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n",
686
+ i, j, value[1], value[2], value[0]);
687
+ }
688
+ }
689
+
690
+ return 0;
691
+}
692
+IWL_EXPORT_SYMBOL(iwl_sar_geo_init);