1bfcc09ddSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2bfcc09ddSBjoern A. Zeeb /* 3*a4128aadSBjoern A. Zeeb * Copyright(c) 2020-2024 Intel Corporation 4bfcc09ddSBjoern A. Zeeb */ 5bfcc09ddSBjoern A. Zeeb 6bfcc09ddSBjoern A. Zeeb #include "iwl-drv.h" 7bfcc09ddSBjoern A. Zeeb #include "pnvm.h" 8bfcc09ddSBjoern A. Zeeb #include "iwl-prph.h" 9bfcc09ddSBjoern A. Zeeb #include "iwl-io.h" 10bfcc09ddSBjoern A. Zeeb #include "fw/api/commands.h" 11bfcc09ddSBjoern A. Zeeb #include "fw/api/nvm-reg.h" 12bfcc09ddSBjoern A. Zeeb #include "fw/api/alive.h" 13bfcc09ddSBjoern A. Zeeb #include "fw/uefi.h" 14bfcc09ddSBjoern A. Zeeb 15*a4128aadSBjoern A. Zeeb #define IWL_PNVM_REDUCED_CAP_BIT BIT(25) 16*a4128aadSBjoern A. Zeeb 17bfcc09ddSBjoern A. Zeeb struct iwl_pnvm_section { 18bfcc09ddSBjoern A. Zeeb __le32 offset; 19bfcc09ddSBjoern A. Zeeb const u8 data[]; 20bfcc09ddSBjoern A. Zeeb } __packed; 21bfcc09ddSBjoern A. Zeeb 22bfcc09ddSBjoern A. Zeeb static bool iwl_pnvm_complete_fn(struct iwl_notif_wait_data *notif_wait, 23bfcc09ddSBjoern A. Zeeb struct iwl_rx_packet *pkt, void *data) 24bfcc09ddSBjoern A. Zeeb { 25bfcc09ddSBjoern A. Zeeb struct iwl_trans *trans = (struct iwl_trans *)data; 26bfcc09ddSBjoern A. Zeeb struct iwl_pnvm_init_complete_ntfy *pnvm_ntf = (void *)pkt->data; 27bfcc09ddSBjoern A. Zeeb 28bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 29bfcc09ddSBjoern A. Zeeb "PNVM complete notification received with status 0x%0x\n", 30bfcc09ddSBjoern A. Zeeb le32_to_cpu(pnvm_ntf->status)); 31bfcc09ddSBjoern A. Zeeb 32bfcc09ddSBjoern A. Zeeb return true; 33bfcc09ddSBjoern A. Zeeb } 34bfcc09ddSBjoern A. Zeeb 35bfcc09ddSBjoern A. Zeeb static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, 369af1bba4SBjoern A. Zeeb size_t len, 379af1bba4SBjoern A. Zeeb struct iwl_pnvm_image *pnvm_data) 38bfcc09ddSBjoern A. Zeeb { 39bfcc09ddSBjoern A. Zeeb const struct iwl_ucode_tlv *tlv; 40bfcc09ddSBjoern A. Zeeb u32 sha1 = 0; 41bfcc09ddSBjoern A. Zeeb u16 mac_type = 0, rf_id = 0; 42bfcc09ddSBjoern A. Zeeb bool hw_match = false; 43bfcc09ddSBjoern A. Zeeb 44bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Handling PNVM section\n"); 45bfcc09ddSBjoern A. Zeeb 469af1bba4SBjoern A. Zeeb memset(pnvm_data, 0, sizeof(*pnvm_data)); 479af1bba4SBjoern A. Zeeb 48bfcc09ddSBjoern A. Zeeb while (len >= sizeof(*tlv)) { 49bfcc09ddSBjoern A. Zeeb u32 tlv_len, tlv_type; 50bfcc09ddSBjoern A. Zeeb 51bfcc09ddSBjoern A. Zeeb len -= sizeof(*tlv); 52bfcc09ddSBjoern A. Zeeb tlv = (const void *)data; 53bfcc09ddSBjoern A. Zeeb 54bfcc09ddSBjoern A. Zeeb tlv_len = le32_to_cpu(tlv->length); 55bfcc09ddSBjoern A. Zeeb tlv_type = le32_to_cpu(tlv->type); 56bfcc09ddSBjoern A. Zeeb 57bfcc09ddSBjoern A. Zeeb if (len < tlv_len) { 58bfcc09ddSBjoern A. Zeeb IWL_ERR(trans, "invalid TLV len: %zd/%u\n", 59bfcc09ddSBjoern A. Zeeb len, tlv_len); 609af1bba4SBjoern A. Zeeb return -EINVAL; 61bfcc09ddSBjoern A. Zeeb } 62bfcc09ddSBjoern A. Zeeb 63bfcc09ddSBjoern A. Zeeb data += sizeof(*tlv); 64bfcc09ddSBjoern A. Zeeb 65bfcc09ddSBjoern A. Zeeb switch (tlv_type) { 66bfcc09ddSBjoern A. Zeeb case IWL_UCODE_TLV_PNVM_VERSION: 67bfcc09ddSBjoern A. Zeeb if (tlv_len < sizeof(__le32)) { 68bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 69bfcc09ddSBjoern A. Zeeb "Invalid size for IWL_UCODE_TLV_PNVM_VERSION (expected %zd, got %d)\n", 70bfcc09ddSBjoern A. Zeeb sizeof(__le32), tlv_len); 71bfcc09ddSBjoern A. Zeeb break; 72bfcc09ddSBjoern A. Zeeb } 73bfcc09ddSBjoern A. Zeeb 74bfcc09ddSBjoern A. Zeeb sha1 = le32_to_cpup((const __le32 *)data); 75bfcc09ddSBjoern A. Zeeb 76bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 77bfcc09ddSBjoern A. Zeeb "Got IWL_UCODE_TLV_PNVM_VERSION %0x\n", 78bfcc09ddSBjoern A. Zeeb sha1); 799af1bba4SBjoern A. Zeeb pnvm_data->version = sha1; 80bfcc09ddSBjoern A. Zeeb break; 81bfcc09ddSBjoern A. Zeeb case IWL_UCODE_TLV_HW_TYPE: 82bfcc09ddSBjoern A. Zeeb if (tlv_len < 2 * sizeof(__le16)) { 83bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 84bfcc09ddSBjoern A. Zeeb "Invalid size for IWL_UCODE_TLV_HW_TYPE (expected %zd, got %d)\n", 85bfcc09ddSBjoern A. Zeeb 2 * sizeof(__le16), tlv_len); 86bfcc09ddSBjoern A. Zeeb break; 87bfcc09ddSBjoern A. Zeeb } 88bfcc09ddSBjoern A. Zeeb 89bfcc09ddSBjoern A. Zeeb if (hw_match) 90bfcc09ddSBjoern A. Zeeb break; 91bfcc09ddSBjoern A. Zeeb 92bfcc09ddSBjoern A. Zeeb mac_type = le16_to_cpup((const __le16 *)data); 93bfcc09ddSBjoern A. Zeeb rf_id = le16_to_cpup((const __le16 *)(data + sizeof(__le16))); 94bfcc09ddSBjoern A. Zeeb 95bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 96bfcc09ddSBjoern A. Zeeb "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n", 97bfcc09ddSBjoern A. Zeeb mac_type, rf_id); 98bfcc09ddSBjoern A. Zeeb 99bfcc09ddSBjoern A. Zeeb if (mac_type == CSR_HW_REV_TYPE(trans->hw_rev) && 100bfcc09ddSBjoern A. Zeeb rf_id == CSR_HW_RFID_TYPE(trans->hw_rf_id)) 101bfcc09ddSBjoern A. Zeeb hw_match = true; 102bfcc09ddSBjoern A. Zeeb break; 103bfcc09ddSBjoern A. Zeeb case IWL_UCODE_TLV_SEC_RT: { 104bfcc09ddSBjoern A. Zeeb const struct iwl_pnvm_section *section = (const void *)data; 105bfcc09ddSBjoern A. Zeeb u32 data_len = tlv_len - sizeof(*section); 106bfcc09ddSBjoern A. Zeeb 107bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 108bfcc09ddSBjoern A. Zeeb "Got IWL_UCODE_TLV_SEC_RT len %d\n", 109bfcc09ddSBjoern A. Zeeb tlv_len); 110bfcc09ddSBjoern A. Zeeb 111bfcc09ddSBjoern A. Zeeb /* TODO: remove, this is a deprecated separator */ 112bfcc09ddSBjoern A. Zeeb if (le32_to_cpup((const __le32 *)data) == 0xddddeeee) { 113bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Ignoring separator.\n"); 114bfcc09ddSBjoern A. Zeeb break; 115bfcc09ddSBjoern A. Zeeb } 116bfcc09ddSBjoern A. Zeeb 1179af1bba4SBjoern A. Zeeb if (pnvm_data->n_chunks == IPC_DRAM_MAP_ENTRY_NUM_MAX) { 1189af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, 1199af1bba4SBjoern A. Zeeb "too many payloads to allocate in DRAM.\n"); 1209af1bba4SBjoern A. Zeeb return -EINVAL; 1219af1bba4SBjoern A. Zeeb } 1229af1bba4SBjoern A. Zeeb 123bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Adding data (size %d)\n", 124bfcc09ddSBjoern A. Zeeb data_len); 125bfcc09ddSBjoern A. Zeeb 1269af1bba4SBjoern A. Zeeb pnvm_data->chunks[pnvm_data->n_chunks].data = section->data; 1279af1bba4SBjoern A. Zeeb pnvm_data->chunks[pnvm_data->n_chunks].len = data_len; 1289af1bba4SBjoern A. Zeeb pnvm_data->n_chunks++; 129bfcc09ddSBjoern A. Zeeb 130bfcc09ddSBjoern A. Zeeb break; 131bfcc09ddSBjoern A. Zeeb } 1329af1bba4SBjoern A. Zeeb case IWL_UCODE_TLV_MEM_DESC: 1339af1bba4SBjoern A. Zeeb if (iwl_uefi_handle_tlv_mem_desc(trans, data, tlv_len, 1349af1bba4SBjoern A. Zeeb pnvm_data)) 1359af1bba4SBjoern A. Zeeb return -EINVAL; 1369af1bba4SBjoern A. Zeeb break; 137bfcc09ddSBjoern A. Zeeb case IWL_UCODE_TLV_PNVM_SKU: 138bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 139bfcc09ddSBjoern A. Zeeb "New PNVM section started, stop parsing.\n"); 140bfcc09ddSBjoern A. Zeeb goto done; 141bfcc09ddSBjoern A. Zeeb default: 142bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Found TLV 0x%0x, len %d\n", 143bfcc09ddSBjoern A. Zeeb tlv_type, tlv_len); 144bfcc09ddSBjoern A. Zeeb break; 145bfcc09ddSBjoern A. Zeeb } 146bfcc09ddSBjoern A. Zeeb 147bfcc09ddSBjoern A. Zeeb len -= ALIGN(tlv_len, 4); 148bfcc09ddSBjoern A. Zeeb data += ALIGN(tlv_len, 4); 149bfcc09ddSBjoern A. Zeeb } 150bfcc09ddSBjoern A. Zeeb 151bfcc09ddSBjoern A. Zeeb done: 152bfcc09ddSBjoern A. Zeeb if (!hw_match) { 153bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 154bfcc09ddSBjoern A. Zeeb "HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n", 155bfcc09ddSBjoern A. Zeeb CSR_HW_REV_TYPE(trans->hw_rev), 156bfcc09ddSBjoern A. Zeeb CSR_HW_RFID_TYPE(trans->hw_rf_id)); 1579af1bba4SBjoern A. Zeeb return -ENOENT; 158bfcc09ddSBjoern A. Zeeb } 159bfcc09ddSBjoern A. Zeeb 1609af1bba4SBjoern A. Zeeb if (!pnvm_data->n_chunks) { 161bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Empty PNVM, skipping.\n"); 1629af1bba4SBjoern A. Zeeb return -ENOENT; 163bfcc09ddSBjoern A. Zeeb } 164bfcc09ddSBjoern A. Zeeb 1659af1bba4SBjoern A. Zeeb return 0; 166bfcc09ddSBjoern A. Zeeb } 167bfcc09ddSBjoern A. Zeeb 168bfcc09ddSBjoern A. Zeeb static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, 1699af1bba4SBjoern A. Zeeb size_t len, 1709af1bba4SBjoern A. Zeeb struct iwl_pnvm_image *pnvm_data) 171bfcc09ddSBjoern A. Zeeb { 172bfcc09ddSBjoern A. Zeeb const struct iwl_ucode_tlv *tlv; 173bfcc09ddSBjoern A. Zeeb 174bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "Parsing PNVM file\n"); 175bfcc09ddSBjoern A. Zeeb 176bfcc09ddSBjoern A. Zeeb while (len >= sizeof(*tlv)) { 177bfcc09ddSBjoern A. Zeeb u32 tlv_len, tlv_type; 178*a4128aadSBjoern A. Zeeb u32 rf_type; 179bfcc09ddSBjoern A. Zeeb 180bfcc09ddSBjoern A. Zeeb len -= sizeof(*tlv); 181bfcc09ddSBjoern A. Zeeb tlv = (const void *)data; 182bfcc09ddSBjoern A. Zeeb 183bfcc09ddSBjoern A. Zeeb tlv_len = le32_to_cpu(tlv->length); 184bfcc09ddSBjoern A. Zeeb tlv_type = le32_to_cpu(tlv->type); 185bfcc09ddSBjoern A. Zeeb 186bfcc09ddSBjoern A. Zeeb if (len < tlv_len) { 187bfcc09ddSBjoern A. Zeeb IWL_ERR(trans, "invalid TLV len: %zd/%u\n", 188bfcc09ddSBjoern A. Zeeb len, tlv_len); 189bfcc09ddSBjoern A. Zeeb return -EINVAL; 190bfcc09ddSBjoern A. Zeeb } 191bfcc09ddSBjoern A. Zeeb 192bfcc09ddSBjoern A. Zeeb if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) { 193bfcc09ddSBjoern A. Zeeb const struct iwl_sku_id *sku_id = 194bfcc09ddSBjoern A. Zeeb (const void *)(data + sizeof(*tlv)); 195bfcc09ddSBjoern A. Zeeb 196bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, 197bfcc09ddSBjoern A. Zeeb "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", 198bfcc09ddSBjoern A. Zeeb tlv_len); 199bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n", 200bfcc09ddSBjoern A. Zeeb le32_to_cpu(sku_id->data[0]), 201bfcc09ddSBjoern A. Zeeb le32_to_cpu(sku_id->data[1]), 202bfcc09ddSBjoern A. Zeeb le32_to_cpu(sku_id->data[2])); 203bfcc09ddSBjoern A. Zeeb 204bfcc09ddSBjoern A. Zeeb data += sizeof(*tlv) + ALIGN(tlv_len, 4); 205bfcc09ddSBjoern A. Zeeb len -= ALIGN(tlv_len, 4); 206bfcc09ddSBjoern A. Zeeb 207*a4128aadSBjoern A. Zeeb trans->reduced_cap_sku = false; 208*a4128aadSBjoern A. Zeeb rf_type = CSR_HW_RFID_TYPE(trans->hw_rf_id); 209*a4128aadSBjoern A. Zeeb if ((trans->sku_id[0] & IWL_PNVM_REDUCED_CAP_BIT) && 210*a4128aadSBjoern A. Zeeb rf_type == IWL_CFG_RF_TYPE_FM) 211*a4128aadSBjoern A. Zeeb trans->reduced_cap_sku = true; 212*a4128aadSBjoern A. Zeeb 213*a4128aadSBjoern A. Zeeb IWL_DEBUG_FW(trans, 214*a4128aadSBjoern A. Zeeb "Reduced SKU device %d\n", 215*a4128aadSBjoern A. Zeeb trans->reduced_cap_sku); 216*a4128aadSBjoern A. Zeeb 217bfcc09ddSBjoern A. Zeeb if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) && 218bfcc09ddSBjoern A. Zeeb trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) && 219bfcc09ddSBjoern A. Zeeb trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) { 220bfcc09ddSBjoern A. Zeeb int ret; 221bfcc09ddSBjoern A. Zeeb 2229af1bba4SBjoern A. Zeeb ret = iwl_pnvm_handle_section(trans, data, len, 2239af1bba4SBjoern A. Zeeb pnvm_data); 224bfcc09ddSBjoern A. Zeeb if (!ret) 225bfcc09ddSBjoern A. Zeeb return 0; 226bfcc09ddSBjoern A. Zeeb } else { 227bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "SKU ID didn't match!\n"); 228bfcc09ddSBjoern A. Zeeb } 229bfcc09ddSBjoern A. Zeeb } else { 230bfcc09ddSBjoern A. Zeeb data += sizeof(*tlv) + ALIGN(tlv_len, 4); 231bfcc09ddSBjoern A. Zeeb len -= ALIGN(tlv_len, 4); 232bfcc09ddSBjoern A. Zeeb } 233bfcc09ddSBjoern A. Zeeb } 234bfcc09ddSBjoern A. Zeeb 235bfcc09ddSBjoern A. Zeeb return -ENOENT; 236bfcc09ddSBjoern A. Zeeb } 237bfcc09ddSBjoern A. Zeeb 238bfcc09ddSBjoern A. Zeeb static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len) 239bfcc09ddSBjoern A. Zeeb { 240bfcc09ddSBjoern A. Zeeb const struct firmware *pnvm; 241bfcc09ddSBjoern A. Zeeb char pnvm_name[MAX_PNVM_NAME]; 242bfcc09ddSBjoern A. Zeeb size_t new_len; 243bfcc09ddSBjoern A. Zeeb int ret; 244bfcc09ddSBjoern A. Zeeb 245bfcc09ddSBjoern A. Zeeb iwl_pnvm_get_fs_name(trans, pnvm_name, sizeof(pnvm_name)); 246bfcc09ddSBjoern A. Zeeb 247bfcc09ddSBjoern A. Zeeb ret = firmware_request_nowarn(&pnvm, pnvm_name, trans->dev); 248bfcc09ddSBjoern A. Zeeb if (ret) { 249bfcc09ddSBjoern A. Zeeb IWL_DEBUG_FW(trans, "PNVM file %s not found %d\n", 250bfcc09ddSBjoern A. Zeeb pnvm_name, ret); 251bfcc09ddSBjoern A. Zeeb return ret; 252bfcc09ddSBjoern A. Zeeb } 253bfcc09ddSBjoern A. Zeeb 254bfcc09ddSBjoern A. Zeeb new_len = pnvm->size; 255*a4128aadSBjoern A. Zeeb *data = kvmemdup(pnvm->data, pnvm->size, GFP_KERNEL); 256bfcc09ddSBjoern A. Zeeb release_firmware(pnvm); 257bfcc09ddSBjoern A. Zeeb 258bfcc09ddSBjoern A. Zeeb if (!*data) 259bfcc09ddSBjoern A. Zeeb return -ENOMEM; 260bfcc09ddSBjoern A. Zeeb 261bfcc09ddSBjoern A. Zeeb *len = new_len; 262bfcc09ddSBjoern A. Zeeb 263bfcc09ddSBjoern A. Zeeb return 0; 264bfcc09ddSBjoern A. Zeeb } 265bfcc09ddSBjoern A. Zeeb 2669af1bba4SBjoern A. Zeeb static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len) 267bfcc09ddSBjoern A. Zeeb { 268bfcc09ddSBjoern A. Zeeb struct pnvm_sku_package *package; 2699af1bba4SBjoern A. Zeeb u8 *image = NULL; 2709af1bba4SBjoern A. Zeeb 271*a4128aadSBjoern A. Zeeb /* Get PNVM from BIOS for non-Intel SKU */ 272*a4128aadSBjoern A. Zeeb if (trans_p->sku_id[2]) { 2739af1bba4SBjoern A. Zeeb package = iwl_uefi_get_pnvm(trans_p, len); 2749af1bba4SBjoern A. Zeeb if (!IS_ERR_OR_NULL(package)) { 2759af1bba4SBjoern A. Zeeb if (*len >= sizeof(*package)) { 2769af1bba4SBjoern A. Zeeb /* we need only the data */ 2779af1bba4SBjoern A. Zeeb *len -= sizeof(*package); 278*a4128aadSBjoern A. Zeeb image = kvmemdup(package->data, 279*a4128aadSBjoern A. Zeeb *len, GFP_KERNEL); 2809af1bba4SBjoern A. Zeeb } 281*a4128aadSBjoern A. Zeeb /* 282*a4128aadSBjoern A. Zeeb * free package regardless of whether kmemdup 283*a4128aadSBjoern A. Zeeb * succeeded 284*a4128aadSBjoern A. Zeeb */ 2859af1bba4SBjoern A. Zeeb kfree(package); 2869af1bba4SBjoern A. Zeeb if (image) 2879af1bba4SBjoern A. Zeeb return image; 2889af1bba4SBjoern A. Zeeb } 289*a4128aadSBjoern A. Zeeb } 2909af1bba4SBjoern A. Zeeb 291*a4128aadSBjoern A. Zeeb /* If it's not available, or for Intel SKU, try from the filesystem */ 2929af1bba4SBjoern A. Zeeb if (iwl_pnvm_get_from_fs(trans_p, &image, len)) 2939af1bba4SBjoern A. Zeeb return NULL; 2949af1bba4SBjoern A. Zeeb return image; 2959af1bba4SBjoern A. Zeeb } 2969af1bba4SBjoern A. Zeeb 2979af1bba4SBjoern A. Zeeb static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, 2989af1bba4SBjoern A. Zeeb const struct iwl_ucode_capabilities *capa) 2999af1bba4SBjoern A. Zeeb { 3009af1bba4SBjoern A. Zeeb struct iwl_pnvm_image *pnvm_data = NULL; 3019af1bba4SBjoern A. Zeeb u8 *data = NULL; 3029af1bba4SBjoern A. Zeeb size_t length; 3039af1bba4SBjoern A. Zeeb int ret; 3049af1bba4SBjoern A. Zeeb 3059af1bba4SBjoern A. Zeeb /* failed to get/parse the image in the past, no use trying again */ 3069af1bba4SBjoern A. Zeeb if (trans->fail_to_parse_pnvm_image) 3079af1bba4SBjoern A. Zeeb return; 3089af1bba4SBjoern A. Zeeb 3099af1bba4SBjoern A. Zeeb if (trans->pnvm_loaded) 3109af1bba4SBjoern A. Zeeb goto set; 3119af1bba4SBjoern A. Zeeb 3129af1bba4SBjoern A. Zeeb data = iwl_get_pnvm_image(trans, &length); 3139af1bba4SBjoern A. Zeeb if (!data) { 3149af1bba4SBjoern A. Zeeb trans->fail_to_parse_pnvm_image = true; 3159af1bba4SBjoern A. Zeeb return; 3169af1bba4SBjoern A. Zeeb } 3179af1bba4SBjoern A. Zeeb 3189af1bba4SBjoern A. Zeeb pnvm_data = kzalloc(sizeof(*pnvm_data), GFP_KERNEL); 3199af1bba4SBjoern A. Zeeb if (!pnvm_data) 3209af1bba4SBjoern A. Zeeb goto free; 3219af1bba4SBjoern A. Zeeb 3229af1bba4SBjoern A. Zeeb ret = iwl_pnvm_parse(trans, data, length, pnvm_data); 3239af1bba4SBjoern A. Zeeb if (ret) { 3249af1bba4SBjoern A. Zeeb trans->fail_to_parse_pnvm_image = true; 3259af1bba4SBjoern A. Zeeb goto free; 3269af1bba4SBjoern A. Zeeb } 3279af1bba4SBjoern A. Zeeb 3289af1bba4SBjoern A. Zeeb ret = iwl_trans_load_pnvm(trans, pnvm_data, capa); 3299af1bba4SBjoern A. Zeeb if (ret) 3309af1bba4SBjoern A. Zeeb goto free; 3319af1bba4SBjoern A. Zeeb IWL_INFO(trans, "loaded PNVM version %08x\n", pnvm_data->version); 3329af1bba4SBjoern A. Zeeb 3339af1bba4SBjoern A. Zeeb set: 3349af1bba4SBjoern A. Zeeb iwl_trans_set_pnvm(trans, capa); 3359af1bba4SBjoern A. Zeeb free: 336*a4128aadSBjoern A. Zeeb kvfree(data); 3379af1bba4SBjoern A. Zeeb kfree(pnvm_data); 3389af1bba4SBjoern A. Zeeb } 3399af1bba4SBjoern A. Zeeb 3409af1bba4SBjoern A. Zeeb static void 3419af1bba4SBjoern A. Zeeb iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans, 3429af1bba4SBjoern A. Zeeb const struct iwl_ucode_capabilities *capa) 3439af1bba4SBjoern A. Zeeb { 3449af1bba4SBjoern A. Zeeb struct iwl_pnvm_image *pnvm_data = NULL; 3459af1bba4SBjoern A. Zeeb u8 *data = NULL; 3469af1bba4SBjoern A. Zeeb size_t length; 3479af1bba4SBjoern A. Zeeb int ret; 3489af1bba4SBjoern A. Zeeb 3499af1bba4SBjoern A. Zeeb if (trans->failed_to_load_reduce_power_image) 3509af1bba4SBjoern A. Zeeb return; 3519af1bba4SBjoern A. Zeeb 3529af1bba4SBjoern A. Zeeb if (trans->reduce_power_loaded) 3539af1bba4SBjoern A. Zeeb goto set; 3549af1bba4SBjoern A. Zeeb 3559af1bba4SBjoern A. Zeeb data = iwl_uefi_get_reduced_power(trans, &length); 3569af1bba4SBjoern A. Zeeb if (IS_ERR(data)) { 3579af1bba4SBjoern A. Zeeb trans->failed_to_load_reduce_power_image = true; 3589af1bba4SBjoern A. Zeeb return; 3599af1bba4SBjoern A. Zeeb } 3609af1bba4SBjoern A. Zeeb 3619af1bba4SBjoern A. Zeeb pnvm_data = kzalloc(sizeof(*pnvm_data), GFP_KERNEL); 3629af1bba4SBjoern A. Zeeb if (!pnvm_data) 3639af1bba4SBjoern A. Zeeb goto free; 3649af1bba4SBjoern A. Zeeb 3659af1bba4SBjoern A. Zeeb ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data); 3669af1bba4SBjoern A. Zeeb if (ret) { 3679af1bba4SBjoern A. Zeeb trans->failed_to_load_reduce_power_image = true; 3689af1bba4SBjoern A. Zeeb goto free; 3699af1bba4SBjoern A. Zeeb } 3709af1bba4SBjoern A. Zeeb 3719af1bba4SBjoern A. Zeeb ret = iwl_trans_load_reduce_power(trans, pnvm_data, capa); 3729af1bba4SBjoern A. Zeeb if (ret) { 3739af1bba4SBjoern A. Zeeb IWL_DEBUG_FW(trans, 3749af1bba4SBjoern A. Zeeb "Failed to load reduce power table %d\n", 3759af1bba4SBjoern A. Zeeb ret); 3769af1bba4SBjoern A. Zeeb trans->failed_to_load_reduce_power_image = true; 3779af1bba4SBjoern A. Zeeb goto free; 3789af1bba4SBjoern A. Zeeb } 3799af1bba4SBjoern A. Zeeb 3809af1bba4SBjoern A. Zeeb set: 3819af1bba4SBjoern A. Zeeb iwl_trans_set_reduce_power(trans, capa); 3829af1bba4SBjoern A. Zeeb free: 3839af1bba4SBjoern A. Zeeb kfree(data); 3849af1bba4SBjoern A. Zeeb kfree(pnvm_data); 3859af1bba4SBjoern A. Zeeb } 3869af1bba4SBjoern A. Zeeb 3879af1bba4SBjoern A. Zeeb int iwl_pnvm_load(struct iwl_trans *trans, 3889af1bba4SBjoern A. Zeeb struct iwl_notif_wait_data *notif_wait, 3899af1bba4SBjoern A. Zeeb const struct iwl_ucode_capabilities *capa) 3909af1bba4SBjoern A. Zeeb { 391bfcc09ddSBjoern A. Zeeb struct iwl_notification_wait pnvm_wait; 392bfcc09ddSBjoern A. Zeeb static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP, 393bfcc09ddSBjoern A. Zeeb PNVM_INIT_COMPLETE_NTFY) }; 394bfcc09ddSBjoern A. Zeeb 395bfcc09ddSBjoern A. Zeeb /* if the SKU_ID is empty, there's nothing to do */ 396bfcc09ddSBjoern A. Zeeb if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2]) 397bfcc09ddSBjoern A. Zeeb return 0; 398bfcc09ddSBjoern A. Zeeb 3999af1bba4SBjoern A. Zeeb iwl_pnvm_load_pnvm_to_trans(trans, capa); 4009af1bba4SBjoern A. Zeeb iwl_pnvm_load_reduce_power_to_trans(trans, capa); 401bfcc09ddSBjoern A. Zeeb 402bfcc09ddSBjoern A. Zeeb iwl_init_notification_wait(notif_wait, &pnvm_wait, 403bfcc09ddSBjoern A. Zeeb ntf_cmds, ARRAY_SIZE(ntf_cmds), 404bfcc09ddSBjoern A. Zeeb iwl_pnvm_complete_fn, trans); 405bfcc09ddSBjoern A. Zeeb 406bfcc09ddSBjoern A. Zeeb /* kick the doorbell */ 407bfcc09ddSBjoern A. Zeeb iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, 408bfcc09ddSBjoern A. Zeeb UREG_DOORBELL_TO_ISR6_PNVM); 409bfcc09ddSBjoern A. Zeeb 410bfcc09ddSBjoern A. Zeeb return iwl_wait_notification(notif_wait, &pnvm_wait, 411bfcc09ddSBjoern A. Zeeb MVM_UCODE_PNVM_TIMEOUT); 412bfcc09ddSBjoern A. Zeeb } 413bfcc09ddSBjoern A. Zeeb IWL_EXPORT_SYMBOL(iwl_pnvm_load); 414