1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 /* 3 * Copyright (C) 2017 Intel Deutschland GmbH 4 * Copyright (C) 2019-2020 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 IWL_EXPORT_SYMBOL(iwl_guid); 16 17 const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29, 18 0x81, 0x4F, 0x75, 0xE4, 19 0xDD, 0x26, 0xB5, 0xFD); 20 IWL_EXPORT_SYMBOL(iwl_rfi_guid); 21 22 static int iwl_acpi_get_handle(struct device *dev, acpi_string method, 23 acpi_handle *ret_handle) 24 { 25 acpi_handle root_handle; 26 acpi_status status; 27 28 root_handle = ACPI_HANDLE(dev); 29 if (!root_handle) { 30 IWL_DEBUG_DEV_RADIO(dev, 31 "ACPI: Could not retrieve root port handle\n"); 32 return -ENOENT; 33 } 34 35 status = acpi_get_handle(root_handle, method, ret_handle); 36 if (ACPI_FAILURE(status)) { 37 IWL_DEBUG_DEV_RADIO(dev, 38 "ACPI: %s method not found\n", method); 39 return -ENOENT; 40 } 41 return 0; 42 } 43 44 void *iwl_acpi_get_object(struct device *dev, acpi_string method) 45 { 46 struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; 47 acpi_handle handle; 48 acpi_status status; 49 int ret; 50 51 ret = iwl_acpi_get_handle(dev, method, &handle); 52 if (ret) 53 return ERR_PTR(-ENOENT); 54 55 /* Call the method with no arguments */ 56 status = acpi_evaluate_object(handle, NULL, NULL, &buf); 57 if (ACPI_FAILURE(status)) { 58 IWL_DEBUG_DEV_RADIO(dev, 59 "ACPI: %s method invocation failed (status: 0x%x)\n", 60 method, status); 61 return ERR_PTR(-ENOENT); 62 } 63 return buf.pointer; 64 } 65 IWL_EXPORT_SYMBOL(iwl_acpi_get_object); 66 67 /* 68 * Generic function for evaluating a method defined in the device specific 69 * method (DSM) interface. The returned acpi object must be freed by calling 70 * function. 71 */ 72 static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func, 73 union acpi_object *args, 74 const guid_t *guid) 75 { 76 union acpi_object *obj; 77 78 obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), guid, rev, func, 79 args); 80 if (!obj) { 81 IWL_DEBUG_DEV_RADIO(dev, 82 "ACPI: DSM method invocation failed (rev: %d, func:%d)\n", 83 rev, func); 84 return ERR_PTR(-ENOENT); 85 } 86 return obj; 87 } 88 89 /* 90 * Generic function to evaluate a DSM with no arguments 91 * and an integer return value, 92 * (as an integer object or inside a buffer object), 93 * verify and assign the value in the "value" parameter. 94 * return 0 in success and the appropriate errno otherwise. 95 */ 96 static int iwl_acpi_get_dsm_integer(struct device *dev, int rev, int func, 97 const guid_t *guid, u64 *value, 98 size_t expected_size) 99 { 100 union acpi_object *obj; 101 int ret = 0; 102 103 obj = iwl_acpi_get_dsm_object(dev, rev, func, NULL, guid); 104 if (IS_ERR(obj)) { 105 IWL_DEBUG_DEV_RADIO(dev, 106 "Failed to get DSM object. func= %d\n", 107 func); 108 return -ENOENT; 109 } 110 111 if (obj->type == ACPI_TYPE_INTEGER) { 112 *value = obj->integer.value; 113 } else if (obj->type == ACPI_TYPE_BUFFER) { 114 __le64 le_value = 0; 115 116 if (WARN_ON_ONCE(expected_size > sizeof(le_value))) 117 return -EINVAL; 118 119 /* if the buffer size doesn't match the expected size */ 120 if (obj->buffer.length != expected_size) 121 IWL_DEBUG_DEV_RADIO(dev, 122 "ACPI: DSM invalid buffer size, padding or truncating (%d)\n", 123 obj->buffer.length); 124 125 /* assuming LE from Intel BIOS spec */ 126 memcpy(&le_value, obj->buffer.pointer, 127 min_t(size_t, expected_size, (size_t)obj->buffer.length)); 128 *value = le64_to_cpu(le_value); 129 } else { 130 IWL_DEBUG_DEV_RADIO(dev, 131 "ACPI: DSM method did not return a valid object, type=%d\n", 132 obj->type); 133 ret = -EINVAL; 134 goto out; 135 } 136 137 IWL_DEBUG_DEV_RADIO(dev, 138 "ACPI: DSM method evaluated: func=%d, ret=%d\n", 139 func, ret); 140 out: 141 ACPI_FREE(obj); 142 return ret; 143 } 144 145 /* 146 * Evaluate a DSM with no arguments and a u8 return value, 147 */ 148 int iwl_acpi_get_dsm_u8(struct device *dev, int rev, int func, 149 const guid_t *guid, u8 *value) 150 { 151 int ret; 152 u64 val; 153 154 ret = iwl_acpi_get_dsm_integer(dev, rev, func, 155 guid, &val, sizeof(u8)); 156 157 if (ret < 0) 158 return ret; 159 160 /* cast val (u64) to be u8 */ 161 *value = (u8)val; 162 return 0; 163 } 164 IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8); 165 166 union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev, 167 union acpi_object *data, 168 int data_size, int *tbl_rev) 169 { 170 int i; 171 union acpi_object *wifi_pkg; 172 173 /* 174 * We need at least one entry in the wifi package that 175 * describes the domain, and one more entry, otherwise there's 176 * no point in reading it. 177 */ 178 if (WARN_ON_ONCE(data_size < 2)) 179 return ERR_PTR(-EINVAL); 180 181 /* 182 * We need at least two packages, one for the revision and one 183 * for the data itself. Also check that the revision is valid 184 * (i.e. it is an integer smaller than 2, as we currently support only 185 * 2 revisions). 186 */ 187 if (data->type != ACPI_TYPE_PACKAGE || 188 data->package.count < 2 || 189 data->package.elements[0].type != ACPI_TYPE_INTEGER || 190 data->package.elements[0].integer.value > 1) { 191 IWL_DEBUG_DEV_RADIO(dev, "Unsupported packages structure\n"); 192 return ERR_PTR(-EINVAL); 193 } 194 195 *tbl_rev = data->package.elements[0].integer.value; 196 197 /* loop through all the packages to find the one for WiFi */ 198 for (i = 1; i < data->package.count; i++) { 199 union acpi_object *domain; 200 201 wifi_pkg = &data->package.elements[i]; 202 203 /* skip entries that are not a package with the right size */ 204 if (wifi_pkg->type != ACPI_TYPE_PACKAGE || 205 wifi_pkg->package.count != data_size) 206 continue; 207 208 domain = &wifi_pkg->package.elements[0]; 209 if (domain->type == ACPI_TYPE_INTEGER && 210 domain->integer.value == ACPI_WIFI_DOMAIN) 211 goto found; 212 } 213 214 return ERR_PTR(-ENOENT); 215 216 found: 217 return wifi_pkg; 218 } 219 IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg); 220 221 int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt, 222 __le32 *block_list_array, 223 int *block_list_size) 224 { 225 union acpi_object *wifi_pkg, *data; 226 int ret, tbl_rev, i; 227 bool enabled; 228 229 data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD); 230 if (IS_ERR(data)) 231 return PTR_ERR(data); 232 233 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 234 ACPI_WTAS_WIFI_DATA_SIZE, 235 &tbl_rev); 236 if (IS_ERR(wifi_pkg)) { 237 ret = PTR_ERR(wifi_pkg); 238 goto out_free; 239 } 240 241 if (wifi_pkg->package.elements[0].type != ACPI_TYPE_INTEGER || 242 tbl_rev != 0) { 243 ret = -EINVAL; 244 goto out_free; 245 } 246 247 enabled = !!wifi_pkg->package.elements[0].integer.value; 248 249 if (!enabled) { 250 *block_list_size = -1; 251 IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n"); 252 ret = 0; 253 goto out_free; 254 } 255 256 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER || 257 wifi_pkg->package.elements[1].integer.value > 258 APCI_WTAS_BLACK_LIST_MAX) { 259 IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n", 260 wifi_pkg->package.elements[1].integer.value); 261 ret = -EINVAL; 262 goto out_free; 263 } 264 *block_list_size = wifi_pkg->package.elements[1].integer.value; 265 266 IWL_DEBUG_RADIO(fwrt, "TAS array size %d\n", *block_list_size); 267 if (*block_list_size > APCI_WTAS_BLACK_LIST_MAX) { 268 IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n", 269 *block_list_size); 270 ret = -EINVAL; 271 goto out_free; 272 } 273 274 for (i = 0; i < *block_list_size; i++) { 275 u32 country; 276 277 if (wifi_pkg->package.elements[2 + i].type != 278 ACPI_TYPE_INTEGER) { 279 IWL_DEBUG_RADIO(fwrt, 280 "TAS invalid array elem %d\n", 2 + i); 281 ret = -EINVAL; 282 goto out_free; 283 } 284 285 country = wifi_pkg->package.elements[2 + i].integer.value; 286 block_list_array[i] = cpu_to_le32(country); 287 IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country); 288 } 289 290 ret = 0; 291 out_free: 292 kfree(data); 293 return ret; 294 } 295 IWL_EXPORT_SYMBOL(iwl_acpi_get_tas); 296 297 int iwl_acpi_get_mcc(struct device *dev, char *mcc) 298 { 299 union acpi_object *wifi_pkg, *data; 300 u32 mcc_val; 301 int ret, tbl_rev; 302 303 data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD); 304 if (IS_ERR(data)) 305 return PTR_ERR(data); 306 307 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE, 308 &tbl_rev); 309 if (IS_ERR(wifi_pkg)) { 310 ret = PTR_ERR(wifi_pkg); 311 goto out_free; 312 } 313 314 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER || 315 tbl_rev != 0) { 316 ret = -EINVAL; 317 goto out_free; 318 } 319 320 mcc_val = wifi_pkg->package.elements[1].integer.value; 321 322 mcc[0] = (mcc_val >> 8) & 0xff; 323 mcc[1] = mcc_val & 0xff; 324 mcc[2] = '\0'; 325 326 ret = 0; 327 out_free: 328 kfree(data); 329 return ret; 330 } 331 IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc); 332 333 u64 iwl_acpi_get_pwr_limit(struct device *dev) 334 { 335 union acpi_object *data, *wifi_pkg; 336 u64 dflt_pwr_limit; 337 int tbl_rev; 338 339 data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD); 340 if (IS_ERR(data)) { 341 dflt_pwr_limit = 0; 342 goto out; 343 } 344 345 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, 346 ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev); 347 if (IS_ERR(wifi_pkg) || tbl_rev != 0 || 348 wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) { 349 dflt_pwr_limit = 0; 350 goto out_free; 351 } 352 353 dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value; 354 out_free: 355 kfree(data); 356 out: 357 return dflt_pwr_limit; 358 } 359 IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit); 360 361 int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk) 362 { 363 union acpi_object *wifi_pkg, *data; 364 int ret, tbl_rev; 365 366 data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD); 367 if (IS_ERR(data)) 368 return PTR_ERR(data); 369 370 wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_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 *extl_clk = wifi_pkg->package.elements[1].integer.value; 384 385 ret = 0; 386 387 out_free: 388 kfree(data); 389 return ret; 390 } 391 IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv); 392 393 static int iwl_sar_set_profile(union acpi_object *table, 394 struct iwl_sar_profile *profile, 395 bool enabled) 396 { 397 int i; 398 399 profile->enabled = enabled; 400 401 for (i = 0; i < ACPI_SAR_TABLE_SIZE; i++) { 402 if (table[i].type != ACPI_TYPE_INTEGER || 403 table[i].integer.value > U8_MAX) 404 return -EINVAL; 405 406 profile->table[i] = table[i].integer.value; 407 } 408 409 return 0; 410 } 411 412 static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt, 413 __le16 *per_chain, u32 n_subbands, 414 int prof_a, int prof_b) 415 { 416 int profs[ACPI_SAR_NUM_CHAIN_LIMITS] = { prof_a, prof_b }; 417 int i, j, idx; 418 419 for (i = 0; i < ACPI_SAR_NUM_CHAIN_LIMITS; i++) { 420 struct iwl_sar_profile *prof; 421 422 /* don't allow SAR to be disabled (profile 0 means disable) */ 423 if (profs[i] == 0) 424 return -EPERM; 425 426 /* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */ 427 if (profs[i] > ACPI_SAR_PROFILE_NUM) 428 return -EINVAL; 429 430 /* profiles go from 1 to 4, so decrement to access the array */ 431 prof = &fwrt->sar_profiles[profs[i] - 1]; 432 433 /* if the profile is disabled, do nothing */ 434 if (!prof->enabled) { 435 IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n", 436 profs[i]); 437 /* 438 * if one of the profiles is disabled, we 439 * ignore all of them and return 1 to 440 * differentiate disabled from other failures. 441 */ 442 return 1; 443 } 444 445 IWL_DEBUG_INFO(fwrt, 446 "SAR EWRD: chain %d profile index %d\n", 447 i, profs[i]); 448 IWL_DEBUG_RADIO(fwrt, " Chain[%d]:\n", i); 449 for (j = 0; j < n_subbands; j++) { 450 idx = i * ACPI_SAR_NUM_SUB_BANDS + j; 451 per_chain[i * n_subbands + j] = 452 cpu_to_le16(prof->table[idx]); 453 IWL_DEBUG_RADIO(fwrt, " Band[%d] = %d * .125dBm\n", 454 j, prof->table[idx]); 455 } 456 } 457 458 return 0; 459 } 460 461 int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt, 462 __le16 *per_chain, u32 n_tables, u32 n_subbands, 463 int prof_a, int prof_b) 464 { 465 int i, ret = 0; 466 467 for (i = 0; i < n_tables; i++) { 468 ret = iwl_sar_fill_table(fwrt, 469 &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAIN_LIMITS], 470 n_subbands, prof_a, prof_b); 471 if (ret) 472 break; 473 } 474 475 return ret; 476 } 477 IWL_EXPORT_SYMBOL(iwl_sar_select_profile); 478 479 int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt) 480 { 481 union acpi_object *wifi_pkg, *table, *data; 482 bool enabled; 483 int ret, tbl_rev; 484 485 data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD); 486 if (IS_ERR(data)) 487 return PTR_ERR(data); 488 489 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 490 ACPI_WRDS_WIFI_DATA_SIZE, &tbl_rev); 491 if (IS_ERR(wifi_pkg)) { 492 ret = PTR_ERR(wifi_pkg); 493 goto out_free; 494 } 495 496 if (tbl_rev != 0) { 497 ret = -EINVAL; 498 goto out_free; 499 } 500 501 if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) { 502 ret = -EINVAL; 503 goto out_free; 504 } 505 506 enabled = !!(wifi_pkg->package.elements[1].integer.value); 507 508 /* position of the actual table */ 509 table = &wifi_pkg->package.elements[2]; 510 511 /* The profile from WRDS is officially profile 1, but goes 512 * into sar_profiles[0] (because we don't have a profile 0). 513 */ 514 ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], enabled); 515 out_free: 516 kfree(data); 517 return ret; 518 } 519 IWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table); 520 521 int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt) 522 { 523 union acpi_object *wifi_pkg, *data; 524 bool enabled; 525 int i, n_profiles, tbl_rev, pos; 526 int ret = 0; 527 528 data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD); 529 if (IS_ERR(data)) 530 return PTR_ERR(data); 531 532 wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, 533 ACPI_EWRD_WIFI_DATA_SIZE, &tbl_rev); 534 if (IS_ERR(wifi_pkg)) { 535 ret = PTR_ERR(wifi_pkg); 536 goto out_free; 537 } 538 539 if (tbl_rev != 0) { 540 ret = -EINVAL; 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 600 if (IS_ERR(wifi_pkg)) { 601 ret = PTR_ERR(wifi_pkg); 602 goto out_free; 603 } 604 605 if (tbl_rev > 1) { 606 ret = -EINVAL; 607 goto out_free; 608 } 609 610 fwrt->geo_rev = tbl_rev; 611 for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) { 612 for (j = 0; j < ACPI_GEO_TABLE_SIZE; j++) { 613 union acpi_object *entry; 614 615 entry = &wifi_pkg->package.elements[idx++]; 616 if (entry->type != ACPI_TYPE_INTEGER || 617 entry->integer.value > U8_MAX) { 618 ret = -EINVAL; 619 goto out_free; 620 } 621 622 fwrt->geo_profiles[i].values[j] = entry->integer.value; 623 } 624 } 625 ret = 0; 626 out_free: 627 kfree(data); 628 return ret; 629 } 630 IWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table); 631 632 bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) 633 { 634 /* 635 * The GEO_TX_POWER_LIMIT command is not supported on earlier 636 * firmware versions. Unfortunately, we don't have a TLV API 637 * flag to rely on, so rely on the major version which is in 638 * the first byte of ucode_ver. This was implemented 639 * initially on version 38 and then backported to 17. It was 640 * also backported to 29, but only for 7265D devices. The 641 * intention was to have it in 36 as well, but not all 8000 642 * family got this feature enabled. The 8000 family is the 643 * only one using version 36, so skip this version entirely. 644 */ 645 return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 || 646 IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 || 647 (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 && 648 ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == 649 CSR_HW_REV_TYPE_7265D)); 650 } 651 IWL_EXPORT_SYMBOL(iwl_sar_geo_support); 652 653 int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt, 654 struct iwl_per_chain_offset *table, u32 n_bands) 655 { 656 int ret, i, j; 657 658 if (!iwl_sar_geo_support(fwrt)) 659 return -EOPNOTSUPP; 660 661 ret = iwl_sar_get_wgds_table(fwrt); 662 if (ret < 0) { 663 IWL_DEBUG_RADIO(fwrt, 664 "Geo SAR BIOS table invalid or unavailable. (%d)\n", 665 ret); 666 /* we don't fail if the table is not available */ 667 return -ENOENT; 668 } 669 670 for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) { 671 for (j = 0; j < n_bands; j++) { 672 struct iwl_per_chain_offset *chain = 673 &table[i * n_bands + j]; 674 u8 *value; 675 676 if (j * ACPI_GEO_PER_CHAIN_SIZE >= 677 ARRAY_SIZE(fwrt->geo_profiles[0].values)) 678 /* 679 * Currently we only store lb an hb values, and 680 * don't have any special ones for uhb. So leave 681 * those empty for the time being 682 */ 683 break; 684 685 value = &fwrt->geo_profiles[i].values[j * 686 ACPI_GEO_PER_CHAIN_SIZE]; 687 chain->max_tx_power = cpu_to_le16(value[0]); 688 chain->chain_a = value[1]; 689 chain->chain_b = value[2]; 690 IWL_DEBUG_RADIO(fwrt, 691 "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n", 692 i, j, value[1], value[2], value[0]); 693 } 694 } 695 696 return 0; 697 } 698 IWL_EXPORT_SYMBOL(iwl_sar_geo_init); 699