linux/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2017 Intel Deutschland GmbH
   4 * Copyright (C) 2019-2021 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
  12const guid_t iwl_guid = GUID_INIT(0xF21202BF, 0x8F78, 0x4DC6,
  13                                  0xA5, 0xB3, 0x1F, 0x73,
  14                                  0x8E, 0x28, 0x5A, 0xDE);
  15IWL_EXPORT_SYMBOL(iwl_guid);
  16
  17const guid_t iwl_rfi_guid = GUID_INIT(0x7266172C, 0x220B, 0x4B29,
  18                                      0x81, 0x4F, 0x75, 0xE4,
  19                                      0xDD, 0x26, 0xB5, 0xFD);
  20IWL_EXPORT_SYMBOL(iwl_rfi_guid);
  21
  22static 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
  44void *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}
  65IWL_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 */
  72static 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 */
  96static 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);
 140out:
 141        ACPI_FREE(obj);
 142        return ret;
 143}
 144
 145/*
 146 * Evaluate a DSM with no arguments and a u8 return value,
 147 */
 148int 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}
 164IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u8);
 165
 166/*
 167 * Evaluate a DSM with no arguments and a u32 return value,
 168 */
 169int iwl_acpi_get_dsm_u32(struct device *dev, int rev, int func,
 170                         const guid_t *guid, u32 *value)
 171{
 172        int ret;
 173        u64 val;
 174
 175        ret = iwl_acpi_get_dsm_integer(dev, rev, func,
 176                                       guid, &val, sizeof(u32));
 177
 178        if (ret < 0)
 179                return ret;
 180
 181        /* cast val (u64) to be u32 */
 182        *value = (u32)val;
 183        return 0;
 184}
 185IWL_EXPORT_SYMBOL(iwl_acpi_get_dsm_u32);
 186
 187union acpi_object *iwl_acpi_get_wifi_pkg(struct device *dev,
 188                                         union acpi_object *data,
 189                                         int data_size, int *tbl_rev)
 190{
 191        int i;
 192        union acpi_object *wifi_pkg;
 193
 194        /*
 195         * We need at least one entry in the wifi package that
 196         * describes the domain, and one more entry, otherwise there's
 197         * no point in reading it.
 198         */
 199        if (WARN_ON_ONCE(data_size < 2))
 200                return ERR_PTR(-EINVAL);
 201
 202        /*
 203         * We need at least two packages, one for the revision and one
 204         * for the data itself.  Also check that the revision is valid
 205         * (i.e. it is an integer (each caller has to check by itself
 206         * if the returned revision is supported)).
 207         */
 208        if (data->type != ACPI_TYPE_PACKAGE ||
 209            data->package.count < 2 ||
 210            data->package.elements[0].type != ACPI_TYPE_INTEGER) {
 211                IWL_DEBUG_DEV_RADIO(dev, "Invalid packages structure\n");
 212                return ERR_PTR(-EINVAL);
 213        }
 214
 215        *tbl_rev = data->package.elements[0].integer.value;
 216
 217        /* loop through all the packages to find the one for WiFi */
 218        for (i = 1; i < data->package.count; i++) {
 219                union acpi_object *domain;
 220
 221                wifi_pkg = &data->package.elements[i];
 222
 223                /* skip entries that are not a package with the right size */
 224                if (wifi_pkg->type != ACPI_TYPE_PACKAGE ||
 225                    wifi_pkg->package.count != data_size)
 226                        continue;
 227
 228                domain = &wifi_pkg->package.elements[0];
 229                if (domain->type == ACPI_TYPE_INTEGER &&
 230                    domain->integer.value == ACPI_WIFI_DOMAIN)
 231                        goto found;
 232        }
 233
 234        return ERR_PTR(-ENOENT);
 235
 236found:
 237        return wifi_pkg;
 238}
 239IWL_EXPORT_SYMBOL(iwl_acpi_get_wifi_pkg);
 240
 241int iwl_acpi_get_tas(struct iwl_fw_runtime *fwrt,
 242                     __le32 *block_list_array,
 243                     int *block_list_size)
 244{
 245        union acpi_object *wifi_pkg, *data;
 246        int ret, tbl_rev, i;
 247        bool enabled;
 248
 249        data = iwl_acpi_get_object(fwrt->dev, ACPI_WTAS_METHOD);
 250        if (IS_ERR(data))
 251                return PTR_ERR(data);
 252
 253        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 254                                         ACPI_WTAS_WIFI_DATA_SIZE,
 255                                         &tbl_rev);
 256        if (IS_ERR(wifi_pkg)) {
 257                ret = PTR_ERR(wifi_pkg);
 258                goto out_free;
 259        }
 260
 261        if (wifi_pkg->package.elements[0].type != ACPI_TYPE_INTEGER ||
 262            tbl_rev != 0) {
 263                ret = -EINVAL;
 264                goto out_free;
 265        }
 266
 267        enabled = !!wifi_pkg->package.elements[1].integer.value;
 268
 269        if (!enabled) {
 270                *block_list_size = -1;
 271                IWL_DEBUG_RADIO(fwrt, "TAS not enabled\n");
 272                ret = 0;
 273                goto out_free;
 274        }
 275
 276        if (wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER ||
 277            wifi_pkg->package.elements[2].integer.value >
 278            APCI_WTAS_BLACK_LIST_MAX) {
 279                IWL_DEBUG_RADIO(fwrt, "TAS invalid array size %llu\n",
 280                                wifi_pkg->package.elements[1].integer.value);
 281                ret = -EINVAL;
 282                goto out_free;
 283        }
 284        *block_list_size = wifi_pkg->package.elements[2].integer.value;
 285
 286        IWL_DEBUG_RADIO(fwrt, "TAS array size %d\n", *block_list_size);
 287        if (*block_list_size > APCI_WTAS_BLACK_LIST_MAX) {
 288                IWL_DEBUG_RADIO(fwrt, "TAS invalid array size value %u\n",
 289                                *block_list_size);
 290                ret = -EINVAL;
 291                goto out_free;
 292        }
 293
 294        for (i = 0; i < *block_list_size; i++) {
 295                u32 country;
 296
 297                if (wifi_pkg->package.elements[3 + i].type !=
 298                    ACPI_TYPE_INTEGER) {
 299                        IWL_DEBUG_RADIO(fwrt,
 300                                        "TAS invalid array elem %d\n", 3 + i);
 301                        ret = -EINVAL;
 302                        goto out_free;
 303                }
 304
 305                country = wifi_pkg->package.elements[3 + i].integer.value;
 306                block_list_array[i] = cpu_to_le32(country);
 307                IWL_DEBUG_RADIO(fwrt, "TAS block list country %d\n", country);
 308        }
 309
 310        ret = 0;
 311out_free:
 312        kfree(data);
 313        return ret;
 314}
 315IWL_EXPORT_SYMBOL(iwl_acpi_get_tas);
 316
 317int iwl_acpi_get_mcc(struct device *dev, char *mcc)
 318{
 319        union acpi_object *wifi_pkg, *data;
 320        u32 mcc_val;
 321        int ret, tbl_rev;
 322
 323        data = iwl_acpi_get_object(dev, ACPI_WRDD_METHOD);
 324        if (IS_ERR(data))
 325                return PTR_ERR(data);
 326
 327        wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_WRDD_WIFI_DATA_SIZE,
 328                                         &tbl_rev);
 329        if (IS_ERR(wifi_pkg)) {
 330                ret = PTR_ERR(wifi_pkg);
 331                goto out_free;
 332        }
 333
 334        if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
 335            tbl_rev != 0) {
 336                ret = -EINVAL;
 337                goto out_free;
 338        }
 339
 340        mcc_val = wifi_pkg->package.elements[1].integer.value;
 341
 342        mcc[0] = (mcc_val >> 8) & 0xff;
 343        mcc[1] = mcc_val & 0xff;
 344        mcc[2] = '\0';
 345
 346        ret = 0;
 347out_free:
 348        kfree(data);
 349        return ret;
 350}
 351IWL_EXPORT_SYMBOL(iwl_acpi_get_mcc);
 352
 353u64 iwl_acpi_get_pwr_limit(struct device *dev)
 354{
 355        union acpi_object *data, *wifi_pkg;
 356        u64 dflt_pwr_limit;
 357        int tbl_rev;
 358
 359        data = iwl_acpi_get_object(dev, ACPI_SPLC_METHOD);
 360        if (IS_ERR(data)) {
 361                dflt_pwr_limit = 0;
 362                goto out;
 363        }
 364
 365        wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data,
 366                                         ACPI_SPLC_WIFI_DATA_SIZE, &tbl_rev);
 367        if (IS_ERR(wifi_pkg) || tbl_rev != 0 ||
 368            wifi_pkg->package.elements[1].integer.value != ACPI_TYPE_INTEGER) {
 369                dflt_pwr_limit = 0;
 370                goto out_free;
 371        }
 372
 373        dflt_pwr_limit = wifi_pkg->package.elements[1].integer.value;
 374out_free:
 375        kfree(data);
 376out:
 377        return dflt_pwr_limit;
 378}
 379IWL_EXPORT_SYMBOL(iwl_acpi_get_pwr_limit);
 380
 381int iwl_acpi_get_eckv(struct device *dev, u32 *extl_clk)
 382{
 383        union acpi_object *wifi_pkg, *data;
 384        int ret, tbl_rev;
 385
 386        data = iwl_acpi_get_object(dev, ACPI_ECKV_METHOD);
 387        if (IS_ERR(data))
 388                return PTR_ERR(data);
 389
 390        wifi_pkg = iwl_acpi_get_wifi_pkg(dev, data, ACPI_ECKV_WIFI_DATA_SIZE,
 391                                         &tbl_rev);
 392        if (IS_ERR(wifi_pkg)) {
 393                ret = PTR_ERR(wifi_pkg);
 394                goto out_free;
 395        }
 396
 397        if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
 398            tbl_rev != 0) {
 399                ret = -EINVAL;
 400                goto out_free;
 401        }
 402
 403        *extl_clk = wifi_pkg->package.elements[1].integer.value;
 404
 405        ret = 0;
 406
 407out_free:
 408        kfree(data);
 409        return ret;
 410}
 411IWL_EXPORT_SYMBOL(iwl_acpi_get_eckv);
 412
 413static int iwl_sar_set_profile(union acpi_object *table,
 414                               struct iwl_sar_profile *profile,
 415                               bool enabled, u8 num_chains, u8 num_sub_bands)
 416{
 417        int i, j, idx = 0;
 418
 419        /*
 420         * The table from ACPI is flat, but we store it in a
 421         * structured array.
 422         */
 423        for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV2; i++) {
 424                for (j = 0; j < ACPI_SAR_NUM_SUB_BANDS_REV2; j++) {
 425                        /* if we don't have the values, use the default */
 426                        if (i >= num_chains || j >= num_sub_bands) {
 427                                profile->chains[i].subbands[j] = 0;
 428                        } else {
 429                                if (table[idx].type != ACPI_TYPE_INTEGER ||
 430                                    table[idx].integer.value > U8_MAX)
 431                                        return -EINVAL;
 432
 433                                profile->chains[i].subbands[j] =
 434                                        table[idx].integer.value;
 435
 436                                idx++;
 437                        }
 438                }
 439        }
 440
 441        /* Only if all values were valid can the profile be enabled */
 442        profile->enabled = enabled;
 443
 444        return 0;
 445}
 446
 447static int iwl_sar_fill_table(struct iwl_fw_runtime *fwrt,
 448                              __le16 *per_chain, u32 n_subbands,
 449                              int prof_a, int prof_b)
 450{
 451        int profs[ACPI_SAR_NUM_CHAINS_REV0] = { prof_a, prof_b };
 452        int i, j;
 453
 454        for (i = 0; i < ACPI_SAR_NUM_CHAINS_REV0; i++) {
 455                struct iwl_sar_profile *prof;
 456
 457                /* don't allow SAR to be disabled (profile 0 means disable) */
 458                if (profs[i] == 0)
 459                        return -EPERM;
 460
 461                /* we are off by one, so allow up to ACPI_SAR_PROFILE_NUM */
 462                if (profs[i] > ACPI_SAR_PROFILE_NUM)
 463                        return -EINVAL;
 464
 465                /* profiles go from 1 to 4, so decrement to access the array */
 466                prof = &fwrt->sar_profiles[profs[i] - 1];
 467
 468                /* if the profile is disabled, do nothing */
 469                if (!prof->enabled) {
 470                        IWL_DEBUG_RADIO(fwrt, "SAR profile %d is disabled.\n",
 471                                        profs[i]);
 472                        /*
 473                         * if one of the profiles is disabled, we
 474                         * ignore all of them and return 1 to
 475                         * differentiate disabled from other failures.
 476                         */
 477                        return 1;
 478                }
 479
 480                IWL_DEBUG_INFO(fwrt,
 481                               "SAR EWRD: chain %d profile index %d\n",
 482                               i, profs[i]);
 483                IWL_DEBUG_RADIO(fwrt, "  Chain[%d]:\n", i);
 484                for (j = 0; j < n_subbands; j++) {
 485                        per_chain[i * n_subbands + j] =
 486                                cpu_to_le16(prof->chains[i].subbands[j]);
 487                        IWL_DEBUG_RADIO(fwrt, "    Band[%d] = %d * .125dBm\n",
 488                                        j, prof->chains[i].subbands[j]);
 489                }
 490        }
 491
 492        return 0;
 493}
 494
 495int iwl_sar_select_profile(struct iwl_fw_runtime *fwrt,
 496                           __le16 *per_chain, u32 n_tables, u32 n_subbands,
 497                           int prof_a, int prof_b)
 498{
 499        int i, ret = 0;
 500
 501        for (i = 0; i < n_tables; i++) {
 502                ret = iwl_sar_fill_table(fwrt,
 503                         &per_chain[i * n_subbands * ACPI_SAR_NUM_CHAINS_REV0],
 504                         n_subbands, prof_a, prof_b);
 505                if (ret)
 506                        break;
 507        }
 508
 509        return ret;
 510}
 511IWL_EXPORT_SYMBOL(iwl_sar_select_profile);
 512
 513int iwl_sar_get_wrds_table(struct iwl_fw_runtime *fwrt)
 514{
 515        union acpi_object *wifi_pkg, *table, *data;
 516        bool enabled;
 517        int ret, tbl_rev;
 518        u8 num_chains, num_sub_bands;
 519
 520        data = iwl_acpi_get_object(fwrt->dev, ACPI_WRDS_METHOD);
 521        if (IS_ERR(data))
 522                return PTR_ERR(data);
 523
 524        /* start by trying to read revision 2 */
 525        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 526                                         ACPI_WRDS_WIFI_DATA_SIZE_REV2,
 527                                         &tbl_rev);
 528        if (!IS_ERR(wifi_pkg)) {
 529                if (tbl_rev != 2) {
 530                        ret = PTR_ERR(wifi_pkg);
 531                        goto out_free;
 532                }
 533
 534                num_chains = ACPI_SAR_NUM_CHAINS_REV2;
 535                num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
 536
 537                goto read_table;
 538        }
 539
 540        /* then try revision 1 */
 541        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 542                                         ACPI_WRDS_WIFI_DATA_SIZE_REV1,
 543                                         &tbl_rev);
 544        if (!IS_ERR(wifi_pkg)) {
 545                if (tbl_rev != 1) {
 546                        ret = PTR_ERR(wifi_pkg);
 547                        goto out_free;
 548                }
 549
 550                num_chains = ACPI_SAR_NUM_CHAINS_REV1;
 551                num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
 552
 553                goto read_table;
 554        }
 555
 556        /* then finally revision 0 */
 557        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 558                                         ACPI_WRDS_WIFI_DATA_SIZE_REV0,
 559                                         &tbl_rev);
 560        if (!IS_ERR(wifi_pkg)) {
 561                if (tbl_rev != 0) {
 562                        ret = PTR_ERR(wifi_pkg);
 563                        goto out_free;
 564                }
 565
 566                num_chains = ACPI_SAR_NUM_CHAINS_REV0;
 567                num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
 568
 569                goto read_table;
 570        }
 571
 572        ret = PTR_ERR(wifi_pkg);
 573        goto out_free;
 574
 575read_table:
 576        if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER) {
 577                ret = -EINVAL;
 578                goto out_free;
 579        }
 580
 581        IWL_DEBUG_RADIO(fwrt, "Reading WRDS tbl_rev=%d\n", tbl_rev);
 582
 583        enabled = !!(wifi_pkg->package.elements[1].integer.value);
 584
 585        /* position of the actual table */
 586        table = &wifi_pkg->package.elements[2];
 587
 588        /* The profile from WRDS is officially profile 1, but goes
 589         * into sar_profiles[0] (because we don't have a profile 0).
 590         */
 591        ret = iwl_sar_set_profile(table, &fwrt->sar_profiles[0], enabled,
 592                                  num_chains, num_sub_bands);
 593out_free:
 594        kfree(data);
 595        return ret;
 596}
 597IWL_EXPORT_SYMBOL(iwl_sar_get_wrds_table);
 598
 599int iwl_sar_get_ewrd_table(struct iwl_fw_runtime *fwrt)
 600{
 601        union acpi_object *wifi_pkg, *data;
 602        bool enabled;
 603        int i, n_profiles, tbl_rev, pos;
 604        int ret = 0;
 605        u8 num_chains, num_sub_bands;
 606
 607        data = iwl_acpi_get_object(fwrt->dev, ACPI_EWRD_METHOD);
 608        if (IS_ERR(data))
 609                return PTR_ERR(data);
 610
 611        /* start by trying to read revision 2 */
 612        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 613                                         ACPI_EWRD_WIFI_DATA_SIZE_REV2,
 614                                         &tbl_rev);
 615        if (!IS_ERR(wifi_pkg)) {
 616                if (tbl_rev != 2) {
 617                        ret = PTR_ERR(wifi_pkg);
 618                        goto out_free;
 619                }
 620
 621                num_chains = ACPI_SAR_NUM_CHAINS_REV2;
 622                num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV2;
 623
 624                goto read_table;
 625        }
 626
 627        /* then try revision 1 */
 628        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 629                                         ACPI_EWRD_WIFI_DATA_SIZE_REV1,
 630                                         &tbl_rev);
 631        if (!IS_ERR(wifi_pkg)) {
 632                if (tbl_rev != 1) {
 633                        ret = PTR_ERR(wifi_pkg);
 634                        goto out_free;
 635                }
 636
 637                num_chains = ACPI_SAR_NUM_CHAINS_REV1;
 638                num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV1;
 639
 640                goto read_table;
 641        }
 642
 643        /* then finally revision 0 */
 644        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 645                                         ACPI_EWRD_WIFI_DATA_SIZE_REV0,
 646                                         &tbl_rev);
 647        if (!IS_ERR(wifi_pkg)) {
 648                if (tbl_rev != 0) {
 649                        ret = PTR_ERR(wifi_pkg);
 650                        goto out_free;
 651                }
 652
 653                num_chains = ACPI_SAR_NUM_CHAINS_REV0;
 654                num_sub_bands = ACPI_SAR_NUM_SUB_BANDS_REV0;
 655
 656                goto read_table;
 657        }
 658
 659        ret = PTR_ERR(wifi_pkg);
 660        goto out_free;
 661
 662read_table:
 663        if (wifi_pkg->package.elements[1].type != ACPI_TYPE_INTEGER ||
 664            wifi_pkg->package.elements[2].type != ACPI_TYPE_INTEGER) {
 665                ret = -EINVAL;
 666                goto out_free;
 667        }
 668
 669        enabled = !!(wifi_pkg->package.elements[1].integer.value);
 670        n_profiles = wifi_pkg->package.elements[2].integer.value;
 671
 672        /*
 673         * Check the validity of n_profiles.  The EWRD profiles start
 674         * from index 1, so the maximum value allowed here is
 675         * ACPI_SAR_PROFILES_NUM - 1.
 676         */
 677        if (n_profiles <= 0 || n_profiles >= ACPI_SAR_PROFILE_NUM) {
 678                ret = -EINVAL;
 679                goto out_free;
 680        }
 681
 682        /* the tables start at element 3 */
 683        pos = 3;
 684
 685        for (i = 0; i < n_profiles; i++) {
 686                /* The EWRD profiles officially go from 2 to 4, but we
 687                 * save them in sar_profiles[1-3] (because we don't
 688                 * have profile 0).  So in the array we start from 1.
 689                 */
 690                ret = iwl_sar_set_profile(&wifi_pkg->package.elements[pos],
 691                                          &fwrt->sar_profiles[i + 1], enabled,
 692                                          num_chains, num_sub_bands);
 693                if (ret < 0)
 694                        break;
 695
 696                /* go to the next table */
 697                pos += num_chains * num_sub_bands;
 698        }
 699
 700out_free:
 701        kfree(data);
 702        return ret;
 703}
 704IWL_EXPORT_SYMBOL(iwl_sar_get_ewrd_table);
 705
 706int iwl_sar_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        int idx = 1; /* start from one to skip the domain */
 711        u8 num_bands;
 712
 713        data = iwl_acpi_get_object(fwrt->dev, ACPI_WGDS_METHOD);
 714        if (IS_ERR(data))
 715                return PTR_ERR(data);
 716
 717        /* start by trying to read revision 2 */
 718        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 719                                         ACPI_WGDS_WIFI_DATA_SIZE_REV2,
 720                                         &tbl_rev);
 721        if (!IS_ERR(wifi_pkg)) {
 722                if (tbl_rev != 2) {
 723                        ret = PTR_ERR(wifi_pkg);
 724                        goto out_free;
 725                }
 726
 727                num_bands = ACPI_GEO_NUM_BANDS_REV2;
 728
 729                goto read_table;
 730        }
 731
 732        /* then try revision 0 (which is the same as 1) */
 733        wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data,
 734                                         ACPI_WGDS_WIFI_DATA_SIZE_REV0,
 735                                         &tbl_rev);
 736        if (!IS_ERR(wifi_pkg)) {
 737                if (tbl_rev != 0 && tbl_rev != 1) {
 738                        ret = PTR_ERR(wifi_pkg);
 739                        goto out_free;
 740                }
 741
 742                num_bands = ACPI_GEO_NUM_BANDS_REV0;
 743
 744                goto read_table;
 745        }
 746
 747        ret = PTR_ERR(wifi_pkg);
 748        goto out_free;
 749
 750read_table:
 751        fwrt->geo_rev = tbl_rev;
 752        for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) {
 753                for (j = 0; j < ACPI_GEO_NUM_BANDS_REV2; j++) {
 754                        union acpi_object *entry;
 755
 756                        /*
 757                         * num_bands is either 2 or 3, if it's only 2 then
 758                         * fill the third band (6 GHz) with the values from
 759                         * 5 GHz (second band)
 760                         */
 761                        if (j >= num_bands) {
 762                                fwrt->geo_profiles[i].bands[j].max =
 763                                        fwrt->geo_profiles[i].bands[1].max;
 764                        } else {
 765                                entry = &wifi_pkg->package.elements[idx++];
 766                                if (entry->type != ACPI_TYPE_INTEGER ||
 767                                    entry->integer.value > U8_MAX) {
 768                                        ret = -EINVAL;
 769                                        goto out_free;
 770                                }
 771
 772                                fwrt->geo_profiles[i].bands[j].max =
 773                                        entry->integer.value;
 774                        }
 775
 776                        for (k = 0; k < ACPI_GEO_NUM_CHAINS; k++) {
 777                                /* same here as above */
 778                                if (j >= num_bands) {
 779                                        fwrt->geo_profiles[i].bands[j].chains[k] =
 780                                                fwrt->geo_profiles[i].bands[1].chains[k];
 781                                } else {
 782                                        entry = &wifi_pkg->package.elements[idx++];
 783                                        if (entry->type != ACPI_TYPE_INTEGER ||
 784                                            entry->integer.value > U8_MAX) {
 785                                                ret = -EINVAL;
 786                                                goto out_free;
 787                                        }
 788
 789                                        fwrt->geo_profiles[i].bands[j].chains[k] =
 790                                                entry->integer.value;
 791                                }
 792                        }
 793                }
 794        }
 795
 796        ret = 0;
 797out_free:
 798        kfree(data);
 799        return ret;
 800}
 801IWL_EXPORT_SYMBOL(iwl_sar_get_wgds_table);
 802
 803bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt)
 804{
 805        /*
 806         * The GEO_TX_POWER_LIMIT command is not supported on earlier
 807         * firmware versions.  Unfortunately, we don't have a TLV API
 808         * flag to rely on, so rely on the major version which is in
 809         * the first byte of ucode_ver.  This was implemented
 810         * initially on version 38 and then backported to 17.  It was
 811         * also backported to 29, but only for 7265D devices.  The
 812         * intention was to have it in 36 as well, but not all 8000
 813         * family got this feature enabled.  The 8000 family is the
 814         * only one using version 36, so skip this version entirely.
 815         */
 816        return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 ||
 817               IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 ||
 818               (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 &&
 819                ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) ==
 820                 CSR_HW_REV_TYPE_7265D));
 821}
 822IWL_EXPORT_SYMBOL(iwl_sar_geo_support);
 823
 824int iwl_sar_geo_init(struct iwl_fw_runtime *fwrt,
 825                     struct iwl_per_chain_offset *table, u32 n_bands)
 826{
 827        int i, j;
 828
 829        if (!iwl_sar_geo_support(fwrt))
 830                return -EOPNOTSUPP;
 831
 832        for (i = 0; i < ACPI_NUM_GEO_PROFILES; i++) {
 833                for (j = 0; j < n_bands; j++) {
 834                        struct iwl_per_chain_offset *chain =
 835                                &table[i * n_bands + j];
 836
 837                        chain->max_tx_power =
 838                                cpu_to_le16(fwrt->geo_profiles[i].bands[j].max);
 839                        chain->chain_a = fwrt->geo_profiles[i].bands[j].chains[0];
 840                        chain->chain_b = fwrt->geo_profiles[i].bands[j].chains[1];
 841                        IWL_DEBUG_RADIO(fwrt,
 842                                        "SAR geographic profile[%d] Band[%d]: chain A = %d chain B = %d max_tx_power = %d\n",
 843                                        i, j,
 844                                        fwrt->geo_profiles[i].bands[j].chains[0],
 845                                        fwrt->geo_profiles[i].bands[j].chains[1],
 846                                        fwrt->geo_profiles[i].bands[j].max);
 847                }
 848        }
 849
 850        return 0;
 851}
 852IWL_EXPORT_SYMBOL(iwl_sar_geo_init);
 853
 854__le32 iwl_acpi_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt)
 855{
 856        int ret;
 857        u8 value;
 858        __le32 config_bitmap = 0;
 859
 860        /*
 861         ** Evaluate func 'DSM_FUNC_ENABLE_INDONESIA_5G2'
 862         */
 863        ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
 864                                  DSM_FUNC_ENABLE_INDONESIA_5G2,
 865                                  &iwl_guid, &value);
 866
 867        if (!ret && value == DSM_VALUE_INDONESIA_ENABLE)
 868                config_bitmap |=
 869                        cpu_to_le32(LARI_CONFIG_ENABLE_5G2_IN_INDONESIA_MSK);
 870
 871        /*
 872         ** Evaluate func 'DSM_FUNC_DISABLE_SRD'
 873         */
 874        ret = iwl_acpi_get_dsm_u8(fwrt->dev, 0,
 875                                  DSM_FUNC_DISABLE_SRD,
 876                                  &iwl_guid, &value);
 877        if (!ret) {
 878                if (value == DSM_VALUE_SRD_PASSIVE)
 879                        config_bitmap |=
 880                                cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_PASSIVE_MSK);
 881                else if (value == DSM_VALUE_SRD_DISABLE)
 882                        config_bitmap |=
 883                                cpu_to_le32(LARI_CONFIG_CHANGE_ETSI_TO_DISABLED_MSK);
 884        }
 885
 886        return config_bitmap;
 887}
 888IWL_EXPORT_SYMBOL(iwl_acpi_get_lari_config_bitmap);
 889