#include "fw/api/nvm-reg.h"
 #include "fw/api/alive.h"
 #include "fw/uefi.h"
+#include "fw/img.h"
 
 #define IWL_PNVM_REDUCED_CAP_BIT BIT(25)
 
        return 0;
 }
 
-static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len,
-                             __le32 sku_id[3])
+static const u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len,
+                                   __le32 sku_id[3], const struct iwl_fw *fw)
 {
        struct pnvm_sku_package *package;
        u8 *image = NULL;
                }
        }
 
+       if (fw->pnvm_data) {
+               *len = fw->pnvm_size;
+
+               return fw->pnvm_data;
+       }
+
        /* If it's not available, or for Intel SKU, try from the filesystem */
        if (iwl_pnvm_get_from_fs(trans_p, &image, len))
                return NULL;
 
 static void
 iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans,
-                           const struct iwl_ucode_capabilities *capa,
+                           const struct iwl_fw *fw,
                            __le32 sku_id[3])
 {
        struct iwl_pnvm_image *pnvm_data = NULL;
-       u8 *data = NULL;
+       const u8 *data = NULL;
        size_t length;
        int ret;
 
        if (trans->pnvm_loaded)
                goto set;
 
-       data = iwl_get_pnvm_image(trans, &length, sku_id);
+       data = iwl_get_pnvm_image(trans, &length, sku_id, fw);
        if (!data) {
                trans->fail_to_parse_pnvm_image = true;
                return;
                goto free;
        }
 
-       ret = iwl_trans_load_pnvm(trans, pnvm_data, capa);
+       ret = iwl_trans_load_pnvm(trans, pnvm_data, &fw->ucode_capa);
        if (ret)
                goto free;
        IWL_DEBUG_INFO(trans, "loaded PNVM version %08x\n", pnvm_data->version);
 
 set:
-       iwl_trans_set_pnvm(trans, capa);
+       iwl_trans_set_pnvm(trans, &fw->ucode_capa);
 free:
-       kvfree(data);
+       /* free only if it was allocated, i.e. not just embedded PNVM data */
+       if (data != fw->pnvm_data)
+               kvfree(data);
        kfree(pnvm_data);
 }
 
 
 int iwl_pnvm_load(struct iwl_trans *trans,
                  struct iwl_notif_wait_data *notif_wait,
-                 const struct iwl_ucode_capabilities *capa,
-                 __le32 sku_id[3])
+                 const struct iwl_fw *fw, __le32 sku_id[3])
 {
        struct iwl_notification_wait pnvm_wait;
        static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP,
        if (!sku_id[0] && !sku_id[1] && !sku_id[2])
                return 0;
 
-       iwl_pnvm_load_pnvm_to_trans(trans, capa, sku_id);
-       iwl_pnvm_load_reduce_power_to_trans(trans, capa, sku_id);
+       iwl_pnvm_load_pnvm_to_trans(trans, fw, sku_id);
+       iwl_pnvm_load_reduce_power_to_trans(trans, &fw->ucode_capa, sku_id);
 
        iwl_init_notification_wait(notif_wait, &pnvm_wait,
                                   ntf_cmds, ARRAY_SIZE(ntf_cmds),
 
        kfree(drv->fw.phy_integration_ver);
        kfree(drv->trans->dbg.pc_data);
        drv->trans->dbg.pc_data = NULL;
+       kvfree(drv->fw.pnvm_data);
+       drv->fw.pnvm_data = NULL;
+       drv->fw.pnvm_size = 0;
 
        for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
                iwl_free_fw_img(drv, drv->fw.img + i);
                        drv->trans->dbg.num_pc =
                                tlv_len / sizeof(struct iwl_pc_data);
                        break;
+               case IWL_UCODE_TLV_PNVM_DATA:
+                       if (drv->fw.pnvm_data)
+                               break;
+                       drv->fw.pnvm_data =
+                               kvmemdup(tlv_data, tlv_len, GFP_KERNEL);
+                       if (!drv->fw.pnvm_data)
+                               return -ENOMEM;
+                       drv->fw.pnvm_size = tlv_len;
+                       break;
                default:
                        IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
                        break;