linux/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/*
   3 * Copyright (C) 2012-2014, 2018-2019 Intel Corporation
   4 * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
   5 * Copyright (C) 2016-2017 Intel Deutschland GmbH
   6 */
   7#include <linux/firmware.h>
   8#include <linux/rtnetlink.h>
   9#include "iwl-trans.h"
  10#include "iwl-csr.h"
  11#include "mvm.h"
  12#include "iwl-eeprom-parse.h"
  13#include "iwl-eeprom-read.h"
  14#include "iwl-nvm-parse.h"
  15#include "iwl-prph.h"
  16#include "fw/acpi.h"
  17
  18/* Default NVM size to read */
  19#define IWL_NVM_DEFAULT_CHUNK_SIZE (2 * 1024)
  20
  21#define NVM_WRITE_OPCODE 1
  22#define NVM_READ_OPCODE 0
  23
  24/* load nvm chunk response */
  25enum {
  26        READ_NVM_CHUNK_SUCCEED = 0,
  27        READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1
  28};
  29
  30/*
  31 * prepare the NVM host command w/ the pointers to the nvm buffer
  32 * and send it to fw
  33 */
  34static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section,
  35                               u16 offset, u16 length, const u8 *data)
  36{
  37        struct iwl_nvm_access_cmd nvm_access_cmd = {
  38                .offset = cpu_to_le16(offset),
  39                .length = cpu_to_le16(length),
  40                .type = cpu_to_le16(section),
  41                .op_code = NVM_WRITE_OPCODE,
  42        };
  43        struct iwl_host_cmd cmd = {
  44                .id = NVM_ACCESS_CMD,
  45                .len = { sizeof(struct iwl_nvm_access_cmd), length },
  46                .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
  47                .data = { &nvm_access_cmd, data },
  48                /* data may come from vmalloc, so use _DUP */
  49                .dataflags = { 0, IWL_HCMD_DFL_DUP },
  50        };
  51        struct iwl_rx_packet *pkt;
  52        struct iwl_nvm_access_resp *nvm_resp;
  53        int ret;
  54
  55        ret = iwl_mvm_send_cmd(mvm, &cmd);
  56        if (ret)
  57                return ret;
  58
  59        pkt = cmd.resp_pkt;
  60        /* Extract & check NVM write response */
  61        nvm_resp = (void *)pkt->data;
  62        if (le16_to_cpu(nvm_resp->status) != READ_NVM_CHUNK_SUCCEED) {
  63                IWL_ERR(mvm,
  64                        "NVM access write command failed for section %u (status = 0x%x)\n",
  65                        section, le16_to_cpu(nvm_resp->status));
  66                ret = -EIO;
  67        }
  68
  69        iwl_free_resp(&cmd);
  70        return ret;
  71}
  72
  73static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section,
  74                              u16 offset, u16 length, u8 *data)
  75{
  76        struct iwl_nvm_access_cmd nvm_access_cmd = {
  77                .offset = cpu_to_le16(offset),
  78                .length = cpu_to_le16(length),
  79                .type = cpu_to_le16(section),
  80                .op_code = NVM_READ_OPCODE,
  81        };
  82        struct iwl_nvm_access_resp *nvm_resp;
  83        struct iwl_rx_packet *pkt;
  84        struct iwl_host_cmd cmd = {
  85                .id = NVM_ACCESS_CMD,
  86                .flags = CMD_WANT_SKB | CMD_SEND_IN_RFKILL,
  87                .data = { &nvm_access_cmd, },
  88        };
  89        int ret, bytes_read, offset_read;
  90        u8 *resp_data;
  91
  92        cmd.len[0] = sizeof(struct iwl_nvm_access_cmd);
  93
  94        ret = iwl_mvm_send_cmd(mvm, &cmd);
  95        if (ret)
  96                return ret;
  97
  98        pkt = cmd.resp_pkt;
  99
 100        /* Extract NVM response */
 101        nvm_resp = (void *)pkt->data;
 102        ret = le16_to_cpu(nvm_resp->status);
 103        bytes_read = le16_to_cpu(nvm_resp->length);
 104        offset_read = le16_to_cpu(nvm_resp->offset);
 105        resp_data = nvm_resp->data;
 106        if (ret) {
 107                if ((offset != 0) &&
 108                    (ret == READ_NVM_CHUNK_NOT_VALID_ADDRESS)) {
 109                        /*
 110                         * meaning of NOT_VALID_ADDRESS:
 111                         * driver try to read chunk from address that is
 112                         * multiple of 2K and got an error since addr is empty.
 113                         * meaning of (offset != 0): driver already
 114                         * read valid data from another chunk so this case
 115                         * is not an error.
 116                         */
 117                        IWL_DEBUG_EEPROM(mvm->trans->dev,
 118                                         "NVM access command failed on offset 0x%x since that section size is multiple 2K\n",
 119                                         offset);
 120                        ret = 0;
 121                } else {
 122                        IWL_DEBUG_EEPROM(mvm->trans->dev,
 123                                         "NVM access command failed with status %d (device: %s)\n",
 124                                         ret, mvm->trans->name);
 125                        ret = -ENODATA;
 126                }
 127                goto exit;
 128        }
 129
 130        if (offset_read != offset) {
 131                IWL_ERR(mvm, "NVM ACCESS response with invalid offset %d\n",
 132                        offset_read);
 133                ret = -EINVAL;
 134                goto exit;
 135        }
 136
 137        /* Write data to NVM */
 138        memcpy(data + offset, resp_data, bytes_read);
 139        ret = bytes_read;
 140
 141exit:
 142        iwl_free_resp(&cmd);
 143        return ret;
 144}
 145
 146static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
 147                                 const u8 *data, u16 length)
 148{
 149        int offset = 0;
 150
 151        /* copy data in chunks of 2k (and remainder if any) */
 152
 153        while (offset < length) {
 154                int chunk_size, ret;
 155
 156                chunk_size = min(IWL_NVM_DEFAULT_CHUNK_SIZE,
 157                                 length - offset);
 158
 159                ret = iwl_nvm_write_chunk(mvm, section, offset,
 160                                          chunk_size, data + offset);
 161                if (ret < 0)
 162                        return ret;
 163
 164                offset += chunk_size;
 165        }
 166
 167        return 0;
 168}
 169
 170/*
 171 * Reads an NVM section completely.
 172 * NICs prior to 7000 family doesn't have a real NVM, but just read
 173 * section 0 which is the EEPROM. Because the EEPROM reading is unlimited
 174 * by uCode, we need to manually check in this case that we don't
 175 * overflow and try to read more than the EEPROM size.
 176 * For 7000 family NICs, we supply the maximal size we can read, and
 177 * the uCode fills the response with as much data as we can,
 178 * without overflowing, so no check is needed.
 179 */
 180static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
 181                                u8 *data, u32 size_read)
 182{
 183        u16 length, offset = 0;
 184        int ret;
 185
 186        /* Set nvm section read length */
 187        length = IWL_NVM_DEFAULT_CHUNK_SIZE;
 188
 189        ret = length;
 190
 191        /* Read the NVM until exhausted (reading less than requested) */
 192        while (ret == length) {
 193                /* Check no memory assumptions fail and cause an overflow */
 194                if ((size_read + offset + length) >
 195                    mvm->trans->trans_cfg->base_params->eeprom_size) {
 196                        IWL_ERR(mvm, "EEPROM size is too small for NVM\n");
 197                        return -ENOBUFS;
 198                }
 199
 200                ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
 201                if (ret < 0) {
 202                        IWL_DEBUG_EEPROM(mvm->trans->dev,
 203                                         "Cannot read NVM from section %d offset %d, length %d\n",
 204                                         section, offset, length);
 205                        return ret;
 206                }
 207                offset += ret;
 208        }
 209
 210        iwl_nvm_fixups(mvm->trans->hw_id, section, data, offset);
 211
 212        IWL_DEBUG_EEPROM(mvm->trans->dev,
 213                         "NVM section %d read completed\n", section);
 214        return offset;
 215}
 216
 217static struct iwl_nvm_data *
 218iwl_parse_nvm_sections(struct iwl_mvm *mvm)
 219{
 220        struct iwl_nvm_section *sections = mvm->nvm_sections;
 221        const __be16 *hw;
 222        const __le16 *sw, *calib, *regulatory, *mac_override, *phy_sku;
 223        int regulatory_type;
 224
 225        /* Checking for required sections */
 226        if (mvm->trans->cfg->nvm_type == IWL_NVM) {
 227                if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
 228                    !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) {
 229                        IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n");
 230                        return NULL;
 231                }
 232        } else {
 233                if (mvm->trans->cfg->nvm_type == IWL_NVM_SDP)
 234                        regulatory_type = NVM_SECTION_TYPE_REGULATORY_SDP;
 235                else
 236                        regulatory_type = NVM_SECTION_TYPE_REGULATORY;
 237
 238                /* SW and REGULATORY sections are mandatory */
 239                if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data ||
 240                    !mvm->nvm_sections[regulatory_type].data) {
 241                        IWL_ERR(mvm,
 242                                "Can't parse empty family 8000 OTP/NVM sections\n");
 243                        return NULL;
 244                }
 245                /* MAC_OVERRIDE or at least HW section must exist */
 246                if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data &&
 247                    !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) {
 248                        IWL_ERR(mvm,
 249                                "Can't parse mac_address, empty sections\n");
 250                        return NULL;
 251                }
 252
 253                /* PHY_SKU section is mandatory in B0 */
 254                if (mvm->trans->cfg->nvm_type == IWL_NVM_EXT &&
 255                    !mvm->nvm_sections[NVM_SECTION_TYPE_PHY_SKU].data) {
 256                        IWL_ERR(mvm,
 257                                "Can't parse phy_sku in B0, empty sections\n");
 258                        return NULL;
 259                }
 260        }
 261
 262        hw = (const __be16 *)sections[mvm->cfg->nvm_hw_section_num].data;
 263        sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data;
 264        calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data;
 265        mac_override =
 266                (const __le16 *)sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data;
 267        phy_sku = (const __le16 *)sections[NVM_SECTION_TYPE_PHY_SKU].data;
 268
 269        regulatory = mvm->trans->cfg->nvm_type == IWL_NVM_SDP ?
 270                (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY_SDP].data :
 271                (const __le16 *)sections[NVM_SECTION_TYPE_REGULATORY].data;
 272
 273        return iwl_parse_nvm_data(mvm->trans, mvm->cfg, mvm->fw, hw, sw, calib,
 274                                  regulatory, mac_override, phy_sku,
 275                                  mvm->fw->valid_tx_ant, mvm->fw->valid_rx_ant);
 276}
 277
 278/* Loads the NVM data stored in mvm->nvm_sections into the NIC */
 279int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
 280{
 281        int i, ret = 0;
 282        struct iwl_nvm_section *sections = mvm->nvm_sections;
 283
 284        IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n");
 285
 286        for (i = 0; i < ARRAY_SIZE(mvm->nvm_sections); i++) {
 287                if (!mvm->nvm_sections[i].data || !mvm->nvm_sections[i].length)
 288                        continue;
 289                ret = iwl_nvm_write_section(mvm, i, sections[i].data,
 290                                            sections[i].length);
 291                if (ret < 0) {
 292                        IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret);
 293                        break;
 294                }
 295        }
 296        return ret;
 297}
 298
 299int iwl_nvm_init(struct iwl_mvm *mvm)
 300{
 301        int ret, section;
 302        u32 size_read = 0;
 303        u8 *nvm_buffer, *temp;
 304        const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step;
 305
 306        if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
 307                return -EINVAL;
 308
 309        /* load NVM values from nic */
 310        /* Read From FW NVM */
 311        IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n");
 312
 313        nvm_buffer = kmalloc(mvm->trans->trans_cfg->base_params->eeprom_size,
 314                             GFP_KERNEL);
 315        if (!nvm_buffer)
 316                return -ENOMEM;
 317        for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) {
 318                /* we override the constness for initial read */
 319                ret = iwl_nvm_read_section(mvm, section, nvm_buffer,
 320                                           size_read);
 321                if (ret == -ENODATA) {
 322                        ret = 0;
 323                        continue;
 324                }
 325                if (ret < 0)
 326                        break;
 327                size_read += ret;
 328                temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
 329                if (!temp) {
 330                        ret = -ENOMEM;
 331                        break;
 332                }
 333
 334                iwl_nvm_fixups(mvm->trans->hw_id, section, temp, ret);
 335
 336                mvm->nvm_sections[section].data = temp;
 337                mvm->nvm_sections[section].length = ret;
 338
 339#ifdef CONFIG_IWLWIFI_DEBUGFS
 340                switch (section) {
 341                case NVM_SECTION_TYPE_SW:
 342                        mvm->nvm_sw_blob.data = temp;
 343                        mvm->nvm_sw_blob.size  = ret;
 344                        break;
 345                case NVM_SECTION_TYPE_CALIBRATION:
 346                        mvm->nvm_calib_blob.data = temp;
 347                        mvm->nvm_calib_blob.size  = ret;
 348                        break;
 349                case NVM_SECTION_TYPE_PRODUCTION:
 350                        mvm->nvm_prod_blob.data = temp;
 351                        mvm->nvm_prod_blob.size  = ret;
 352                        break;
 353                case NVM_SECTION_TYPE_PHY_SKU:
 354                        mvm->nvm_phy_sku_blob.data = temp;
 355                        mvm->nvm_phy_sku_blob.size  = ret;
 356                        break;
 357                case NVM_SECTION_TYPE_REGULATORY_SDP:
 358                case NVM_SECTION_TYPE_REGULATORY:
 359                        mvm->nvm_reg_blob.data = temp;
 360                        mvm->nvm_reg_blob.size  = ret;
 361                        break;
 362                default:
 363                        if (section == mvm->cfg->nvm_hw_section_num) {
 364                                mvm->nvm_hw_blob.data = temp;
 365                                mvm->nvm_hw_blob.size = ret;
 366                                break;
 367                        }
 368                }
 369#endif
 370        }
 371        if (!size_read)
 372                IWL_ERR(mvm, "OTP is blank\n");
 373        kfree(nvm_buffer);
 374
 375        /* Only if PNVM selected in the mod param - load external NVM  */
 376        if (mvm->nvm_file_name) {
 377                /* read External NVM file from the mod param */
 378                ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name,
 379                                            mvm->nvm_sections);
 380                if (ret) {
 381                        mvm->nvm_file_name = nvm_file_C;
 382
 383                        if ((ret == -EFAULT || ret == -ENOENT) &&
 384                            mvm->nvm_file_name) {
 385                                /* in case nvm file was failed try again */
 386                                ret = iwl_read_external_nvm(mvm->trans,
 387                                                            mvm->nvm_file_name,
 388                                                            mvm->nvm_sections);
 389                                if (ret)
 390                                        return ret;
 391                        } else {
 392                                return ret;
 393                        }
 394                }
 395        }
 396
 397        /* parse the relevant nvm sections */
 398        mvm->nvm_data = iwl_parse_nvm_sections(mvm);
 399        if (!mvm->nvm_data)
 400                return -ENODATA;
 401        IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n",
 402                         mvm->nvm_data->nvm_version);
 403
 404        return ret < 0 ? ret : 0;
 405}
 406
 407struct iwl_mcc_update_resp *
 408iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2,
 409                   enum iwl_mcc_source src_id)
 410{
 411        struct iwl_mcc_update_cmd mcc_update_cmd = {
 412                .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]),
 413                .source_id = (u8)src_id,
 414        };
 415        struct iwl_mcc_update_resp *resp_cp;
 416        struct iwl_rx_packet *pkt;
 417        struct iwl_host_cmd cmd = {
 418                .id = MCC_UPDATE_CMD,
 419                .flags = CMD_WANT_SKB,
 420                .data = { &mcc_update_cmd },
 421        };
 422
 423        int ret;
 424        u32 status;
 425        int resp_len, n_channels;
 426        u16 mcc;
 427
 428        if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
 429                return ERR_PTR(-EOPNOTSUPP);
 430
 431        cmd.len[0] = sizeof(struct iwl_mcc_update_cmd);
 432
 433        IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n",
 434                      alpha2[0], alpha2[1], src_id);
 435
 436        ret = iwl_mvm_send_cmd(mvm, &cmd);
 437        if (ret)
 438                return ERR_PTR(ret);
 439
 440        pkt = cmd.resp_pkt;
 441
 442        /* Extract MCC response */
 443        if (fw_has_capa(&mvm->fw->ucode_capa,
 444                        IWL_UCODE_TLV_CAPA_MCC_UPDATE_11AX_SUPPORT)) {
 445                struct iwl_mcc_update_resp *mcc_resp = (void *)pkt->data;
 446
 447                n_channels =  __le32_to_cpu(mcc_resp->n_channels);
 448                resp_len = sizeof(struct iwl_mcc_update_resp) +
 449                           n_channels * sizeof(__le32);
 450                resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL);
 451                if (!resp_cp) {
 452                        resp_cp = ERR_PTR(-ENOMEM);
 453                        goto exit;
 454                }
 455        } else {
 456                struct iwl_mcc_update_resp_v3 *mcc_resp_v3 = (void *)pkt->data;
 457
 458                n_channels =  __le32_to_cpu(mcc_resp_v3->n_channels);
 459                resp_len = sizeof(struct iwl_mcc_update_resp) +
 460                           n_channels * sizeof(__le32);
 461                resp_cp = kzalloc(resp_len, GFP_KERNEL);
 462                if (!resp_cp) {
 463                        resp_cp = ERR_PTR(-ENOMEM);
 464                        goto exit;
 465                }
 466
 467                resp_cp->status = mcc_resp_v3->status;
 468                resp_cp->mcc = mcc_resp_v3->mcc;
 469                resp_cp->cap = cpu_to_le16(mcc_resp_v3->cap);
 470                resp_cp->source_id = mcc_resp_v3->source_id;
 471                resp_cp->time = mcc_resp_v3->time;
 472                resp_cp->geo_info = mcc_resp_v3->geo_info;
 473                resp_cp->n_channels = mcc_resp_v3->n_channels;
 474                memcpy(resp_cp->channels, mcc_resp_v3->channels,
 475                       n_channels * sizeof(__le32));
 476        }
 477
 478        status = le32_to_cpu(resp_cp->status);
 479
 480        mcc = le16_to_cpu(resp_cp->mcc);
 481
 482        /* W/A for a FW/NVM issue - returns 0x00 for the world domain */
 483        if (mcc == 0) {
 484                mcc = 0x3030;  /* "00" - world */
 485                resp_cp->mcc = cpu_to_le16(mcc);
 486        }
 487
 488        IWL_DEBUG_LAR(mvm,
 489                      "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') n_chans: %d\n",
 490                      status, mcc, mcc >> 8, mcc & 0xff, n_channels);
 491
 492exit:
 493        iwl_free_resp(&cmd);
 494        return resp_cp;
 495}
 496
 497int iwl_mvm_init_mcc(struct iwl_mvm *mvm)
 498{
 499        bool tlv_lar;
 500        bool nvm_lar;
 501        int retval;
 502        struct ieee80211_regdomain *regd;
 503        char mcc[3];
 504
 505        if (mvm->cfg->nvm_type == IWL_NVM_EXT) {
 506                tlv_lar = fw_has_capa(&mvm->fw->ucode_capa,
 507                                      IWL_UCODE_TLV_CAPA_LAR_SUPPORT);
 508                nvm_lar = mvm->nvm_data->lar_enabled;
 509                if (tlv_lar != nvm_lar)
 510                        IWL_INFO(mvm,
 511                                 "Conflict between TLV & NVM regarding enabling LAR (TLV = %s NVM =%s)\n",
 512                                 tlv_lar ? "enabled" : "disabled",
 513                                 nvm_lar ? "enabled" : "disabled");
 514        }
 515
 516        if (!iwl_mvm_is_lar_supported(mvm))
 517                return 0;
 518
 519        /*
 520         * try to replay the last set MCC to FW. If it doesn't exist,
 521         * queue an update to cfg80211 to retrieve the default alpha2 from FW.
 522         */
 523        retval = iwl_mvm_init_fw_regd(mvm);
 524        if (retval != -ENOENT)
 525                return retval;
 526
 527        /*
 528         * Driver regulatory hint for initial update, this also informs the
 529         * firmware we support wifi location updates.
 530         * Disallow scans that might crash the FW while the LAR regdomain
 531         * is not set.
 532         */
 533        mvm->lar_regdom_set = false;
 534
 535        regd = iwl_mvm_get_current_regdomain(mvm, NULL);
 536        if (IS_ERR_OR_NULL(regd))
 537                return -EIO;
 538
 539        if (iwl_mvm_is_wifi_mcc_supported(mvm) &&
 540            !iwl_acpi_get_mcc(mvm->dev, mcc)) {
 541                kfree(regd);
 542                regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc,
 543                                             MCC_SOURCE_BIOS, NULL);
 544                if (IS_ERR_OR_NULL(regd))
 545                        return -EIO;
 546        }
 547
 548        retval = regulatory_set_wiphy_regd_sync_rtnl(mvm->hw->wiphy, regd);
 549        kfree(regd);
 550        return retval;
 551}
 552
 553void iwl_mvm_rx_chub_update_mcc(struct iwl_mvm *mvm,
 554                                struct iwl_rx_cmd_buffer *rxb)
 555{
 556        struct iwl_rx_packet *pkt = rxb_addr(rxb);
 557        struct iwl_mcc_chub_notif *notif = (void *)pkt->data;
 558        enum iwl_mcc_source src;
 559        char mcc[3];
 560        struct ieee80211_regdomain *regd;
 561        int wgds_tbl_idx;
 562
 563        lockdep_assert_held(&mvm->mutex);
 564
 565        if (iwl_mvm_is_vif_assoc(mvm) && notif->source_id == MCC_SOURCE_WIFI) {
 566                IWL_DEBUG_LAR(mvm, "Ignore mcc update while associated\n");
 567                return;
 568        }
 569
 570        if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm)))
 571                return;
 572
 573        mcc[0] = le16_to_cpu(notif->mcc) >> 8;
 574        mcc[1] = le16_to_cpu(notif->mcc) & 0xff;
 575        mcc[2] = '\0';
 576        src = notif->source_id;
 577
 578        IWL_DEBUG_LAR(mvm,
 579                      "RX: received chub update mcc cmd (mcc '%s' src %d)\n",
 580                      mcc, src);
 581        regd = iwl_mvm_get_regdomain(mvm->hw->wiphy, mcc, src, NULL);
 582        if (IS_ERR_OR_NULL(regd))
 583                return;
 584
 585        wgds_tbl_idx = iwl_mvm_get_sar_geo_profile(mvm);
 586        if (wgds_tbl_idx < 0)
 587                IWL_DEBUG_INFO(mvm, "SAR WGDS is disabled (%d)\n",
 588                               wgds_tbl_idx);
 589        else
 590                IWL_DEBUG_INFO(mvm, "SAR WGDS: geo profile %d is configured\n",
 591                               wgds_tbl_idx);
 592
 593        regulatory_set_wiphy_regd(mvm->hw->wiphy, regd);
 594        kfree(regd);
 595}
 596