162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005-2014, 2018-2021 Intel Corporation 462306a36Sopenharmony_ci * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Intel Deutschland GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/completion.h> 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/firmware.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/vmalloc.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "iwl-drv.h" 1462306a36Sopenharmony_ci#include "iwl-csr.h" 1562306a36Sopenharmony_ci#include "iwl-debug.h" 1662306a36Sopenharmony_ci#include "iwl-trans.h" 1762306a36Sopenharmony_ci#include "iwl-op-mode.h" 1862306a36Sopenharmony_ci#include "iwl-agn-hw.h" 1962306a36Sopenharmony_ci#include "fw/img.h" 2062306a36Sopenharmony_ci#include "iwl-dbg-tlv.h" 2162306a36Sopenharmony_ci#include "iwl-config.h" 2262306a36Sopenharmony_ci#include "iwl-modparams.h" 2362306a36Sopenharmony_ci#include "fw/api/alive.h" 2462306a36Sopenharmony_ci#include "fw/api/mac.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/****************************************************************************** 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci * module boiler plate 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci ******************************************************************************/ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux" 3362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION); 3462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 3762306a36Sopenharmony_cistatic struct dentry *iwl_dbgfs_root; 3862306a36Sopenharmony_ci#endif 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/** 4162306a36Sopenharmony_ci * struct iwl_drv - drv common data 4262306a36Sopenharmony_ci * @list: list of drv structures using this opmode 4362306a36Sopenharmony_ci * @fw: the iwl_fw structure 4462306a36Sopenharmony_ci * @op_mode: the running op_mode 4562306a36Sopenharmony_ci * @trans: transport layer 4662306a36Sopenharmony_ci * @dev: for debug prints only 4762306a36Sopenharmony_ci * @fw_index: firmware revision to try loading 4862306a36Sopenharmony_ci * @firmware_name: composite filename of ucode file to load 4962306a36Sopenharmony_ci * @request_firmware_complete: the firmware has been obtained from user space 5062306a36Sopenharmony_ci * @dbgfs_drv: debugfs root directory entry 5162306a36Sopenharmony_ci * @dbgfs_trans: debugfs transport directory entry 5262306a36Sopenharmony_ci * @dbgfs_op_mode: debugfs op_mode directory entry 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistruct iwl_drv { 5562306a36Sopenharmony_ci struct list_head list; 5662306a36Sopenharmony_ci struct iwl_fw fw; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci struct iwl_op_mode *op_mode; 5962306a36Sopenharmony_ci struct iwl_trans *trans; 6062306a36Sopenharmony_ci struct device *dev; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci int fw_index; /* firmware we're trying to load */ 6362306a36Sopenharmony_ci char firmware_name[64]; /* name of firmware file to load */ 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci struct completion request_firmware_complete; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 6862306a36Sopenharmony_ci struct dentry *dbgfs_drv; 6962306a36Sopenharmony_ci struct dentry *dbgfs_trans; 7062306a36Sopenharmony_ci struct dentry *dbgfs_op_mode; 7162306a36Sopenharmony_ci#endif 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cienum { 7562306a36Sopenharmony_ci DVM_OP_MODE, 7662306a36Sopenharmony_ci MVM_OP_MODE, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Protects the table contents, i.e. the ops pointer & drv list */ 8062306a36Sopenharmony_cistatic DEFINE_MUTEX(iwlwifi_opmode_table_mtx); 8162306a36Sopenharmony_cistatic struct iwlwifi_opmode_table { 8262306a36Sopenharmony_ci const char *name; /* name: iwldvm, iwlmvm, etc */ 8362306a36Sopenharmony_ci const struct iwl_op_mode_ops *ops; /* pointer to op_mode ops */ 8462306a36Sopenharmony_ci struct list_head drv; /* list of devices using this op_mode */ 8562306a36Sopenharmony_ci} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */ 8662306a36Sopenharmony_ci [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL }, 8762306a36Sopenharmony_ci [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define IWL_DEFAULT_SCAN_CHANNELS 40 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * struct fw_sec: Just for the image parsing process. 9462306a36Sopenharmony_ci * For the fw storage we are using struct fw_desc. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistruct fw_sec { 9762306a36Sopenharmony_ci const void *data; /* the sec data */ 9862306a36Sopenharmony_ci size_t size; /* section size */ 9962306a36Sopenharmony_ci u32 offset; /* offset of writing in the device */ 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic void iwl_free_fw_desc(struct iwl_drv *drv, struct fw_desc *desc) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci vfree(desc->data); 10562306a36Sopenharmony_ci desc->data = NULL; 10662306a36Sopenharmony_ci desc->len = 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int i; 11262306a36Sopenharmony_ci for (i = 0; i < img->num_sec; i++) 11362306a36Sopenharmony_ci iwl_free_fw_desc(drv, &img->sec[i]); 11462306a36Sopenharmony_ci kfree(img->sec); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic void iwl_dealloc_ucode(struct iwl_drv *drv) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int i; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci kfree(drv->fw.dbg.dest_tlv); 12262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drv->fw.dbg.conf_tlv); i++) 12362306a36Sopenharmony_ci kfree(drv->fw.dbg.conf_tlv[i]); 12462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drv->fw.dbg.trigger_tlv); i++) 12562306a36Sopenharmony_ci kfree(drv->fw.dbg.trigger_tlv[i]); 12662306a36Sopenharmony_ci kfree(drv->fw.dbg.mem_tlv); 12762306a36Sopenharmony_ci kfree(drv->fw.iml); 12862306a36Sopenharmony_ci kfree(drv->fw.ucode_capa.cmd_versions); 12962306a36Sopenharmony_ci kfree(drv->fw.phy_integration_ver); 13062306a36Sopenharmony_ci kfree(drv->trans->dbg.pc_data); 13162306a36Sopenharmony_ci drv->trans->dbg.pc_data = NULL; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) 13462306a36Sopenharmony_ci iwl_free_fw_img(drv, drv->fw.img + i); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* clear the data for the aborted load case */ 13762306a36Sopenharmony_ci memset(&drv->fw, 0, sizeof(drv->fw)); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int iwl_alloc_fw_desc(struct iwl_drv *drv, struct fw_desc *desc, 14162306a36Sopenharmony_ci struct fw_sec *sec) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci void *data; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci desc->data = NULL; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (!sec || !sec->size) 14862306a36Sopenharmony_ci return -EINVAL; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci data = vmalloc(sec->size); 15162306a36Sopenharmony_ci if (!data) 15262306a36Sopenharmony_ci return -ENOMEM; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci desc->len = sec->size; 15562306a36Sopenharmony_ci desc->offset = sec->offset; 15662306a36Sopenharmony_ci memcpy(data, sec->data, desc->len); 15762306a36Sopenharmony_ci desc->data = data; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic inline char iwl_drv_get_step(int step) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci if (step == SILICON_Z_STEP) 16562306a36Sopenharmony_ci return 'z'; 16662306a36Sopenharmony_ci return 'a' + step; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ciconst char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci char mac_step, rf_step; 17262306a36Sopenharmony_ci const char *rf, *cdb; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (trans->cfg->fw_name_pre) 17562306a36Sopenharmony_ci return trans->cfg->fw_name_pre; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if (WARN_ON(!trans->cfg->fw_name_mac)) 17862306a36Sopenharmony_ci return "unconfigured"; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci mac_step = iwl_drv_get_step(trans->hw_rev_step); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { 18362306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_HR1: 18462306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_HR2: 18562306a36Sopenharmony_ci rf = "hr"; 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_GF: 18862306a36Sopenharmony_ci rf = "gf"; 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_MR: 19162306a36Sopenharmony_ci rf = "mr"; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_MS: 19462306a36Sopenharmony_ci rf = "ms"; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_FM: 19762306a36Sopenharmony_ci rf = "fm"; 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci case IWL_CFG_RF_TYPE_WH: 20062306a36Sopenharmony_ci rf = "wh"; 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci default: 20362306a36Sopenharmony_ci return "unknown-rf"; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci cdb = CSR_HW_RFID_IS_CDB(trans->hw_rf_id) ? "4" : ""; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci rf_step = iwl_drv_get_step(CSR_HW_RFID_STEP(trans->hw_rf_id)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci scnprintf(buf, FW_NAME_PRE_BUFSIZE, 21162306a36Sopenharmony_ci "iwlwifi-%s-%c0-%s%s-%c0", 21262306a36Sopenharmony_ci trans->cfg->fw_name_mac, mac_step, 21362306a36Sopenharmony_ci rf, cdb, rf_step); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return buf; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_drv_get_fwname_pre); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void iwl_req_fw_callback(const struct firmware *ucode_raw, 22062306a36Sopenharmony_ci void *context); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic int iwl_request_firmware(struct iwl_drv *drv, bool first) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci const struct iwl_cfg *cfg = drv->trans->cfg; 22562306a36Sopenharmony_ci char _fw_name_pre[FW_NAME_PRE_BUFSIZE]; 22662306a36Sopenharmony_ci const char *fw_name_pre; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (drv->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000 && 22962306a36Sopenharmony_ci (drv->trans->hw_rev_step != SILICON_B_STEP && 23062306a36Sopenharmony_ci drv->trans->hw_rev_step != SILICON_C_STEP)) { 23162306a36Sopenharmony_ci IWL_ERR(drv, 23262306a36Sopenharmony_ci "Only HW steps B and C are currently supported (0x%0x)\n", 23362306a36Sopenharmony_ci drv->trans->hw_rev); 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci fw_name_pre = iwl_drv_get_fwname_pre(drv->trans, _fw_name_pre); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if (first) 24062306a36Sopenharmony_ci drv->fw_index = cfg->ucode_api_max; 24162306a36Sopenharmony_ci else 24262306a36Sopenharmony_ci drv->fw_index--; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (drv->fw_index < cfg->ucode_api_min) { 24562306a36Sopenharmony_ci IWL_ERR(drv, "no suitable firmware found!\n"); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (cfg->ucode_api_min == cfg->ucode_api_max) { 24862306a36Sopenharmony_ci IWL_ERR(drv, "%s-%d is required\n", fw_name_pre, 24962306a36Sopenharmony_ci cfg->ucode_api_max); 25062306a36Sopenharmony_ci } else { 25162306a36Sopenharmony_ci IWL_ERR(drv, "minimum version required: %s-%d\n", 25262306a36Sopenharmony_ci fw_name_pre, cfg->ucode_api_min); 25362306a36Sopenharmony_ci IWL_ERR(drv, "maximum version supported: %s-%d\n", 25462306a36Sopenharmony_ci fw_name_pre, cfg->ucode_api_max); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci IWL_ERR(drv, 25862306a36Sopenharmony_ci "check git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git\n"); 25962306a36Sopenharmony_ci return -ENOENT; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s-%d.ucode", 26362306a36Sopenharmony_ci fw_name_pre, drv->fw_index); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci IWL_DEBUG_FW_INFO(drv, "attempting to load firmware '%s'\n", 26662306a36Sopenharmony_ci drv->firmware_name); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return request_firmware_nowait(THIS_MODULE, 1, drv->firmware_name, 26962306a36Sopenharmony_ci drv->trans->dev, 27062306a36Sopenharmony_ci GFP_KERNEL, drv, iwl_req_fw_callback); 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistruct fw_img_parsing { 27462306a36Sopenharmony_ci struct fw_sec *sec; 27562306a36Sopenharmony_ci int sec_counter; 27662306a36Sopenharmony_ci}; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci/* 27962306a36Sopenharmony_ci * struct fw_sec_parsing: to extract fw section and it's offset from tlv 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_cistruct fw_sec_parsing { 28262306a36Sopenharmony_ci __le32 offset; 28362306a36Sopenharmony_ci const u8 data[]; 28462306a36Sopenharmony_ci} __packed; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/** 28762306a36Sopenharmony_ci * struct iwl_tlv_calib_data - parse the default calib data from TLV 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * @ucode_type: the uCode to which the following default calib relates. 29062306a36Sopenharmony_ci * @calib: default calibrations. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_cistruct iwl_tlv_calib_data { 29362306a36Sopenharmony_ci __le32 ucode_type; 29462306a36Sopenharmony_ci struct iwl_tlv_calib_ctrl calib; 29562306a36Sopenharmony_ci} __packed; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistruct iwl_firmware_pieces { 29862306a36Sopenharmony_ci struct fw_img_parsing img[IWL_UCODE_TYPE_MAX]; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr; 30162306a36Sopenharmony_ci u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* FW debug data parsed for driver usage */ 30462306a36Sopenharmony_ci bool dbg_dest_tlv_init; 30562306a36Sopenharmony_ci const u8 *dbg_dest_ver; 30662306a36Sopenharmony_ci union { 30762306a36Sopenharmony_ci const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; 30862306a36Sopenharmony_ci const struct iwl_fw_dbg_dest_tlv_v1 *dbg_dest_tlv_v1; 30962306a36Sopenharmony_ci }; 31062306a36Sopenharmony_ci const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX]; 31162306a36Sopenharmony_ci size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX]; 31262306a36Sopenharmony_ci const struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX]; 31362306a36Sopenharmony_ci size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; 31462306a36Sopenharmony_ci struct iwl_fw_dbg_mem_seg_tlv *dbg_mem_tlv; 31562306a36Sopenharmony_ci size_t n_mem_tlv; 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * These functions are just to extract uCode section data from the pieces 32062306a36Sopenharmony_ci * structure. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic struct fw_sec *get_sec(struct iwl_firmware_pieces *pieces, 32362306a36Sopenharmony_ci enum iwl_ucode_type type, 32462306a36Sopenharmony_ci int sec) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci return &pieces->img[type].sec[sec]; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic void alloc_sec_data(struct iwl_firmware_pieces *pieces, 33062306a36Sopenharmony_ci enum iwl_ucode_type type, 33162306a36Sopenharmony_ci int sec) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct fw_img_parsing *img = &pieces->img[type]; 33462306a36Sopenharmony_ci struct fw_sec *sec_memory; 33562306a36Sopenharmony_ci int size = sec + 1; 33662306a36Sopenharmony_ci size_t alloc_size = sizeof(*img->sec) * size; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (img->sec && img->sec_counter >= size) 33962306a36Sopenharmony_ci return; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci sec_memory = krealloc(img->sec, alloc_size, GFP_KERNEL); 34262306a36Sopenharmony_ci if (!sec_memory) 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci img->sec = sec_memory; 34662306a36Sopenharmony_ci img->sec_counter = size; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic void set_sec_data(struct iwl_firmware_pieces *pieces, 35062306a36Sopenharmony_ci enum iwl_ucode_type type, 35162306a36Sopenharmony_ci int sec, 35262306a36Sopenharmony_ci const void *data) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci alloc_sec_data(pieces, type, sec); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci pieces->img[type].sec[sec].data = data; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void set_sec_size(struct iwl_firmware_pieces *pieces, 36062306a36Sopenharmony_ci enum iwl_ucode_type type, 36162306a36Sopenharmony_ci int sec, 36262306a36Sopenharmony_ci size_t size) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci alloc_sec_data(pieces, type, sec); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci pieces->img[type].sec[sec].size = size; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic size_t get_sec_size(struct iwl_firmware_pieces *pieces, 37062306a36Sopenharmony_ci enum iwl_ucode_type type, 37162306a36Sopenharmony_ci int sec) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci return pieces->img[type].sec[sec].size; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_cistatic void set_sec_offset(struct iwl_firmware_pieces *pieces, 37762306a36Sopenharmony_ci enum iwl_ucode_type type, 37862306a36Sopenharmony_ci int sec, 37962306a36Sopenharmony_ci u32 offset) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci alloc_sec_data(pieces, type, sec); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci pieces->img[type].sec[sec].offset = offset; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/* 38762306a36Sopenharmony_ci * Gets uCode section from tlv. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic int iwl_store_ucode_sec(struct iwl_firmware_pieces *pieces, 39062306a36Sopenharmony_ci const void *data, enum iwl_ucode_type type, 39162306a36Sopenharmony_ci int size) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct fw_img_parsing *img; 39462306a36Sopenharmony_ci struct fw_sec *sec; 39562306a36Sopenharmony_ci const struct fw_sec_parsing *sec_parse; 39662306a36Sopenharmony_ci size_t alloc_size; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (WARN_ON(!pieces || !data || type >= IWL_UCODE_TYPE_MAX)) 39962306a36Sopenharmony_ci return -1; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci sec_parse = (const struct fw_sec_parsing *)data; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci img = &pieces->img[type]; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci alloc_size = sizeof(*img->sec) * (img->sec_counter + 1); 40662306a36Sopenharmony_ci sec = krealloc(img->sec, alloc_size, GFP_KERNEL); 40762306a36Sopenharmony_ci if (!sec) 40862306a36Sopenharmony_ci return -ENOMEM; 40962306a36Sopenharmony_ci img->sec = sec; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci sec = &img->sec[img->sec_counter]; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci sec->offset = le32_to_cpu(sec_parse->offset); 41462306a36Sopenharmony_ci sec->data = sec_parse->data; 41562306a36Sopenharmony_ci sec->size = size - sizeof(sec_parse->offset); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci ++img->sec_counter; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci return 0; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic int iwl_set_default_calib(struct iwl_drv *drv, const u8 *data) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci const struct iwl_tlv_calib_data *def_calib = 42562306a36Sopenharmony_ci (const struct iwl_tlv_calib_data *)data; 42662306a36Sopenharmony_ci u32 ucode_type = le32_to_cpu(def_calib->ucode_type); 42762306a36Sopenharmony_ci if (ucode_type >= IWL_UCODE_TYPE_MAX) { 42862306a36Sopenharmony_ci IWL_ERR(drv, "Wrong ucode_type %u for default calibration.\n", 42962306a36Sopenharmony_ci ucode_type); 43062306a36Sopenharmony_ci return -EINVAL; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci drv->fw.default_calib[ucode_type].flow_trigger = 43362306a36Sopenharmony_ci def_calib->calib.flow_trigger; 43462306a36Sopenharmony_ci drv->fw.default_calib[ucode_type].event_trigger = 43562306a36Sopenharmony_ci def_calib->calib.event_trigger; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void iwl_set_ucode_api_flags(struct iwl_drv *drv, const u8 *data, 44162306a36Sopenharmony_ci struct iwl_ucode_capabilities *capa) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci const struct iwl_ucode_api *ucode_api = (const void *)data; 44462306a36Sopenharmony_ci u32 api_index = le32_to_cpu(ucode_api->api_index); 44562306a36Sopenharmony_ci u32 api_flags = le32_to_cpu(ucode_api->api_flags); 44662306a36Sopenharmony_ci int i; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) { 44962306a36Sopenharmony_ci IWL_WARN(drv, 45062306a36Sopenharmony_ci "api flags index %d larger than supported by driver\n", 45162306a36Sopenharmony_ci api_index); 45262306a36Sopenharmony_ci return; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 45662306a36Sopenharmony_ci if (api_flags & BIT(i)) 45762306a36Sopenharmony_ci __set_bit(i + 32 * api_index, capa->_api); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic void iwl_set_ucode_capabilities(struct iwl_drv *drv, const u8 *data, 46262306a36Sopenharmony_ci struct iwl_ucode_capabilities *capa) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci const struct iwl_ucode_capa *ucode_capa = (const void *)data; 46562306a36Sopenharmony_ci u32 api_index = le32_to_cpu(ucode_capa->api_index); 46662306a36Sopenharmony_ci u32 api_flags = le32_to_cpu(ucode_capa->api_capa); 46762306a36Sopenharmony_ci int i; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) { 47062306a36Sopenharmony_ci IWL_WARN(drv, 47162306a36Sopenharmony_ci "capa flags index %d larger than supported by driver\n", 47262306a36Sopenharmony_ci api_index); 47362306a36Sopenharmony_ci return; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 47762306a36Sopenharmony_ci if (api_flags & BIT(i)) 47862306a36Sopenharmony_ci __set_bit(i + 32 * api_index, capa->_capa); 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic const char *iwl_reduced_fw_name(struct iwl_drv *drv) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci const char *name = drv->firmware_name; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (strncmp(name, "iwlwifi-", 8) == 0) 48762306a36Sopenharmony_ci name += 8; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return name; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int iwl_parse_v1_v2_firmware(struct iwl_drv *drv, 49362306a36Sopenharmony_ci const struct firmware *ucode_raw, 49462306a36Sopenharmony_ci struct iwl_firmware_pieces *pieces) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci const struct iwl_ucode_header *ucode = (const void *)ucode_raw->data; 49762306a36Sopenharmony_ci u32 api_ver, hdr_size, build; 49862306a36Sopenharmony_ci char buildstr[25]; 49962306a36Sopenharmony_ci const u8 *src; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci drv->fw.ucode_ver = le32_to_cpu(ucode->ver); 50262306a36Sopenharmony_ci api_ver = IWL_UCODE_API(drv->fw.ucode_ver); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci switch (api_ver) { 50562306a36Sopenharmony_ci default: 50662306a36Sopenharmony_ci hdr_size = 28; 50762306a36Sopenharmony_ci if (ucode_raw->size < hdr_size) { 50862306a36Sopenharmony_ci IWL_ERR(drv, "File size too small!\n"); 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci build = le32_to_cpu(ucode->u.v2.build); 51262306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, 51362306a36Sopenharmony_ci le32_to_cpu(ucode->u.v2.inst_size)); 51462306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, 51562306a36Sopenharmony_ci le32_to_cpu(ucode->u.v2.data_size)); 51662306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, 51762306a36Sopenharmony_ci le32_to_cpu(ucode->u.v2.init_size)); 51862306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, 51962306a36Sopenharmony_ci le32_to_cpu(ucode->u.v2.init_data_size)); 52062306a36Sopenharmony_ci src = ucode->u.v2.data; 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci case 0: 52362306a36Sopenharmony_ci case 1: 52462306a36Sopenharmony_ci case 2: 52562306a36Sopenharmony_ci hdr_size = 24; 52662306a36Sopenharmony_ci if (ucode_raw->size < hdr_size) { 52762306a36Sopenharmony_ci IWL_ERR(drv, "File size too small!\n"); 52862306a36Sopenharmony_ci return -EINVAL; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci build = 0; 53162306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, 53262306a36Sopenharmony_ci le32_to_cpu(ucode->u.v1.inst_size)); 53362306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, 53462306a36Sopenharmony_ci le32_to_cpu(ucode->u.v1.data_size)); 53562306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, 53662306a36Sopenharmony_ci le32_to_cpu(ucode->u.v1.init_size)); 53762306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, 53862306a36Sopenharmony_ci le32_to_cpu(ucode->u.v1.init_data_size)); 53962306a36Sopenharmony_ci src = ucode->u.v1.data; 54062306a36Sopenharmony_ci break; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (build) 54462306a36Sopenharmony_ci sprintf(buildstr, " build %u", build); 54562306a36Sopenharmony_ci else 54662306a36Sopenharmony_ci buildstr[0] = '\0'; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci snprintf(drv->fw.fw_version, 54962306a36Sopenharmony_ci sizeof(drv->fw.fw_version), 55062306a36Sopenharmony_ci "%u.%u.%u.%u%s %s", 55162306a36Sopenharmony_ci IWL_UCODE_MAJOR(drv->fw.ucode_ver), 55262306a36Sopenharmony_ci IWL_UCODE_MINOR(drv->fw.ucode_ver), 55362306a36Sopenharmony_ci IWL_UCODE_API(drv->fw.ucode_ver), 55462306a36Sopenharmony_ci IWL_UCODE_SERIAL(drv->fw.ucode_ver), 55562306a36Sopenharmony_ci buildstr, iwl_reduced_fw_name(drv)); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Verify size of file vs. image size info in file's header */ 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (ucode_raw->size != hdr_size + 56062306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) + 56162306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) + 56262306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) + 56362306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)) { 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci IWL_ERR(drv, 56662306a36Sopenharmony_ci "uCode file size %d does not match expected size\n", 56762306a36Sopenharmony_ci (int)ucode_raw->size); 56862306a36Sopenharmony_ci return -EINVAL; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, src); 57362306a36Sopenharmony_ci src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST); 57462306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST, 57562306a36Sopenharmony_ci IWLAGN_RTC_INST_LOWER_BOUND); 57662306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, src); 57762306a36Sopenharmony_ci src += get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA); 57862306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA, 57962306a36Sopenharmony_ci IWLAGN_RTC_DATA_LOWER_BOUND); 58062306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, src); 58162306a36Sopenharmony_ci src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST); 58262306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST, 58362306a36Sopenharmony_ci IWLAGN_RTC_INST_LOWER_BOUND); 58462306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, src); 58562306a36Sopenharmony_ci src += get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA); 58662306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA, 58762306a36Sopenharmony_ci IWLAGN_RTC_DATA_LOWER_BOUND); 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void iwl_drv_set_dump_exclude(struct iwl_drv *drv, 59262306a36Sopenharmony_ci enum iwl_ucode_tlv_type tlv_type, 59362306a36Sopenharmony_ci const void *tlv_data, u32 tlv_len) 59462306a36Sopenharmony_ci{ 59562306a36Sopenharmony_ci const struct iwl_fw_dump_exclude *fw = tlv_data; 59662306a36Sopenharmony_ci struct iwl_dump_exclude *excl; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (tlv_len < sizeof(*fw)) 59962306a36Sopenharmony_ci return; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (tlv_type == IWL_UCODE_TLV_SEC_TABLE_ADDR) { 60262306a36Sopenharmony_ci excl = &drv->fw.dump_excl[0]; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* second time we find this, it's for WoWLAN */ 60562306a36Sopenharmony_ci if (excl->addr) 60662306a36Sopenharmony_ci excl = &drv->fw.dump_excl_wowlan[0]; 60762306a36Sopenharmony_ci } else if (fw_has_capa(&drv->fw.ucode_capa, 60862306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG)) { 60962306a36Sopenharmony_ci /* IWL_UCODE_TLV_D3_KEK_KCK_ADDR is regular image */ 61062306a36Sopenharmony_ci excl = &drv->fw.dump_excl[0]; 61162306a36Sopenharmony_ci } else { 61262306a36Sopenharmony_ci /* IWL_UCODE_TLV_D3_KEK_KCK_ADDR is WoWLAN image */ 61362306a36Sopenharmony_ci excl = &drv->fw.dump_excl_wowlan[0]; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (excl->addr) 61762306a36Sopenharmony_ci excl++; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (excl->addr) { 62062306a36Sopenharmony_ci IWL_DEBUG_FW_INFO(drv, "found too many excludes in fw file\n"); 62162306a36Sopenharmony_ci return; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci excl->addr = le32_to_cpu(fw->addr) & ~FW_ADDR_CACHE_CONTROL; 62562306a36Sopenharmony_ci excl->size = le32_to_cpu(fw->size); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic void iwl_parse_dbg_tlv_assert_tables(struct iwl_drv *drv, 62962306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci const struct iwl_fw_ini_region_tlv *region; 63262306a36Sopenharmony_ci u32 length = le32_to_cpu(tlv->length); 63362306a36Sopenharmony_ci u32 addr; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci if (length < offsetof(typeof(*region), special_mem) + 63662306a36Sopenharmony_ci sizeof(region->special_mem)) 63762306a36Sopenharmony_ci return; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci region = (const void *)tlv->data; 64062306a36Sopenharmony_ci addr = le32_to_cpu(region->special_mem.base_addr); 64162306a36Sopenharmony_ci addr += le32_to_cpu(region->special_mem.offset); 64262306a36Sopenharmony_ci addr &= ~FW_ADDR_CACHE_CONTROL; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (region->type != IWL_FW_INI_REGION_SPECIAL_DEVICE_MEMORY) 64562306a36Sopenharmony_ci return; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci switch (region->sub_type) { 64862306a36Sopenharmony_ci case IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_UMAC_ERROR_TABLE: 64962306a36Sopenharmony_ci drv->trans->dbg.umac_error_event_table = addr; 65062306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 65162306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_UMAC; 65262306a36Sopenharmony_ci break; 65362306a36Sopenharmony_ci case IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_LMAC_1_ERROR_TABLE: 65462306a36Sopenharmony_ci drv->trans->dbg.lmac_error_event_table[0] = addr; 65562306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 65662306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_LMAC1; 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci case IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_LMAC_2_ERROR_TABLE: 65962306a36Sopenharmony_ci drv->trans->dbg.lmac_error_event_table[1] = addr; 66062306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 66162306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_LMAC2; 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci case IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_TCM_1_ERROR_TABLE: 66462306a36Sopenharmony_ci drv->trans->dbg.tcm_error_event_table[0] = addr; 66562306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 66662306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_TCM1; 66762306a36Sopenharmony_ci break; 66862306a36Sopenharmony_ci case IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_TCM_2_ERROR_TABLE: 66962306a36Sopenharmony_ci drv->trans->dbg.tcm_error_event_table[1] = addr; 67062306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 67162306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_TCM2; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_RCM_1_ERROR_TABLE: 67462306a36Sopenharmony_ci drv->trans->dbg.rcm_error_event_table[0] = addr; 67562306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 67662306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_RCM1; 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci case IWL_FW_INI_REGION_DEVICE_MEMORY_SUBTYPE_RCM_2_ERROR_TABLE: 67962306a36Sopenharmony_ci drv->trans->dbg.rcm_error_event_table[1] = addr; 68062306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 68162306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_RCM2; 68262306a36Sopenharmony_ci break; 68362306a36Sopenharmony_ci default: 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic int iwl_parse_tlv_firmware(struct iwl_drv *drv, 68962306a36Sopenharmony_ci const struct firmware *ucode_raw, 69062306a36Sopenharmony_ci struct iwl_firmware_pieces *pieces, 69162306a36Sopenharmony_ci struct iwl_ucode_capabilities *capa, 69262306a36Sopenharmony_ci bool *usniffer_images) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci const struct iwl_tlv_ucode_header *ucode = (const void *)ucode_raw->data; 69562306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv; 69662306a36Sopenharmony_ci size_t len = ucode_raw->size; 69762306a36Sopenharmony_ci const u8 *data; 69862306a36Sopenharmony_ci u32 tlv_len; 69962306a36Sopenharmony_ci u32 usniffer_img; 70062306a36Sopenharmony_ci enum iwl_ucode_tlv_type tlv_type; 70162306a36Sopenharmony_ci const u8 *tlv_data; 70262306a36Sopenharmony_ci char buildstr[25]; 70362306a36Sopenharmony_ci u32 build, paging_mem_size; 70462306a36Sopenharmony_ci int num_of_cpus; 70562306a36Sopenharmony_ci bool usniffer_req = false; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (len < sizeof(*ucode)) { 70862306a36Sopenharmony_ci IWL_ERR(drv, "uCode has invalid length: %zd\n", len); 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) { 71362306a36Sopenharmony_ci IWL_ERR(drv, "invalid uCode magic: 0X%x\n", 71462306a36Sopenharmony_ci le32_to_cpu(ucode->magic)); 71562306a36Sopenharmony_ci return -EINVAL; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci drv->fw.ucode_ver = le32_to_cpu(ucode->ver); 71962306a36Sopenharmony_ci memcpy(drv->fw.human_readable, ucode->human_readable, 72062306a36Sopenharmony_ci sizeof(drv->fw.human_readable)); 72162306a36Sopenharmony_ci build = le32_to_cpu(ucode->build); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (build) 72462306a36Sopenharmony_ci sprintf(buildstr, " build %u", build); 72562306a36Sopenharmony_ci else 72662306a36Sopenharmony_ci buildstr[0] = '\0'; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci snprintf(drv->fw.fw_version, 72962306a36Sopenharmony_ci sizeof(drv->fw.fw_version), 73062306a36Sopenharmony_ci "%u.%u.%u.%u%s %s", 73162306a36Sopenharmony_ci IWL_UCODE_MAJOR(drv->fw.ucode_ver), 73262306a36Sopenharmony_ci IWL_UCODE_MINOR(drv->fw.ucode_ver), 73362306a36Sopenharmony_ci IWL_UCODE_API(drv->fw.ucode_ver), 73462306a36Sopenharmony_ci IWL_UCODE_SERIAL(drv->fw.ucode_ver), 73562306a36Sopenharmony_ci buildstr, iwl_reduced_fw_name(drv)); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci data = ucode->data; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci len -= sizeof(*ucode); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci while (len >= sizeof(*tlv)) { 74262306a36Sopenharmony_ci len -= sizeof(*tlv); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci tlv = (const void *)data; 74562306a36Sopenharmony_ci tlv_len = le32_to_cpu(tlv->length); 74662306a36Sopenharmony_ci tlv_type = le32_to_cpu(tlv->type); 74762306a36Sopenharmony_ci tlv_data = tlv->data; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (len < tlv_len) { 75062306a36Sopenharmony_ci IWL_ERR(drv, "invalid TLV len: %zd/%u\n", 75162306a36Sopenharmony_ci len, tlv_len); 75262306a36Sopenharmony_ci return -EINVAL; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci len -= ALIGN(tlv_len, 4); 75562306a36Sopenharmony_ci data += sizeof(*tlv) + ALIGN(tlv_len, 4); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci switch (tlv_type) { 75862306a36Sopenharmony_ci case IWL_UCODE_TLV_INST: 75962306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_REGULAR, 76062306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, tlv_data); 76162306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_REGULAR, 76262306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, tlv_len); 76362306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_REGULAR, 76462306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, 76562306a36Sopenharmony_ci IWLAGN_RTC_INST_LOWER_BOUND); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci case IWL_UCODE_TLV_DATA: 76862306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_REGULAR, 76962306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, tlv_data); 77062306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_REGULAR, 77162306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, tlv_len); 77262306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_REGULAR, 77362306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, 77462306a36Sopenharmony_ci IWLAGN_RTC_DATA_LOWER_BOUND); 77562306a36Sopenharmony_ci break; 77662306a36Sopenharmony_ci case IWL_UCODE_TLV_INIT: 77762306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_INIT, 77862306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, tlv_data); 77962306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_INIT, 78062306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, tlv_len); 78162306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_INIT, 78262306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, 78362306a36Sopenharmony_ci IWLAGN_RTC_INST_LOWER_BOUND); 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci case IWL_UCODE_TLV_INIT_DATA: 78662306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_INIT, 78762306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, tlv_data); 78862306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_INIT, 78962306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, tlv_len); 79062306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_INIT, 79162306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, 79262306a36Sopenharmony_ci IWLAGN_RTC_DATA_LOWER_BOUND); 79362306a36Sopenharmony_ci break; 79462306a36Sopenharmony_ci case IWL_UCODE_TLV_BOOT: 79562306a36Sopenharmony_ci IWL_ERR(drv, "Found unexpected BOOT ucode\n"); 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case IWL_UCODE_TLV_PROBE_MAX_LEN: 79862306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 79962306a36Sopenharmony_ci goto invalid_tlv_len; 80062306a36Sopenharmony_ci capa->max_probe_length = 80162306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 80262306a36Sopenharmony_ci break; 80362306a36Sopenharmony_ci case IWL_UCODE_TLV_PAN: 80462306a36Sopenharmony_ci if (tlv_len) 80562306a36Sopenharmony_ci goto invalid_tlv_len; 80662306a36Sopenharmony_ci capa->flags |= IWL_UCODE_TLV_FLAGS_PAN; 80762306a36Sopenharmony_ci break; 80862306a36Sopenharmony_ci case IWL_UCODE_TLV_FLAGS: 80962306a36Sopenharmony_ci /* must be at least one u32 */ 81062306a36Sopenharmony_ci if (tlv_len < sizeof(u32)) 81162306a36Sopenharmony_ci goto invalid_tlv_len; 81262306a36Sopenharmony_ci /* and a proper number of u32s */ 81362306a36Sopenharmony_ci if (tlv_len % sizeof(u32)) 81462306a36Sopenharmony_ci goto invalid_tlv_len; 81562306a36Sopenharmony_ci /* 81662306a36Sopenharmony_ci * This driver only reads the first u32 as 81762306a36Sopenharmony_ci * right now no more features are defined, 81862306a36Sopenharmony_ci * if that changes then either the driver 81962306a36Sopenharmony_ci * will not work with the new firmware, or 82062306a36Sopenharmony_ci * it'll not take advantage of new features. 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci capa->flags = le32_to_cpup((const __le32 *)tlv_data); 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci case IWL_UCODE_TLV_API_CHANGES_SET: 82562306a36Sopenharmony_ci if (tlv_len != sizeof(struct iwl_ucode_api)) 82662306a36Sopenharmony_ci goto invalid_tlv_len; 82762306a36Sopenharmony_ci iwl_set_ucode_api_flags(drv, tlv_data, capa); 82862306a36Sopenharmony_ci break; 82962306a36Sopenharmony_ci case IWL_UCODE_TLV_ENABLED_CAPABILITIES: 83062306a36Sopenharmony_ci if (tlv_len != sizeof(struct iwl_ucode_capa)) 83162306a36Sopenharmony_ci goto invalid_tlv_len; 83262306a36Sopenharmony_ci iwl_set_ucode_capabilities(drv, tlv_data, capa); 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci case IWL_UCODE_TLV_INIT_EVTLOG_PTR: 83562306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 83662306a36Sopenharmony_ci goto invalid_tlv_len; 83762306a36Sopenharmony_ci pieces->init_evtlog_ptr = 83862306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci case IWL_UCODE_TLV_INIT_EVTLOG_SIZE: 84162306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 84262306a36Sopenharmony_ci goto invalid_tlv_len; 84362306a36Sopenharmony_ci pieces->init_evtlog_size = 84462306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci case IWL_UCODE_TLV_INIT_ERRLOG_PTR: 84762306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 84862306a36Sopenharmony_ci goto invalid_tlv_len; 84962306a36Sopenharmony_ci pieces->init_errlog_ptr = 85062306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 85162306a36Sopenharmony_ci break; 85262306a36Sopenharmony_ci case IWL_UCODE_TLV_RUNT_EVTLOG_PTR: 85362306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 85462306a36Sopenharmony_ci goto invalid_tlv_len; 85562306a36Sopenharmony_ci pieces->inst_evtlog_ptr = 85662306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 85762306a36Sopenharmony_ci break; 85862306a36Sopenharmony_ci case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE: 85962306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 86062306a36Sopenharmony_ci goto invalid_tlv_len; 86162306a36Sopenharmony_ci pieces->inst_evtlog_size = 86262306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 86362306a36Sopenharmony_ci break; 86462306a36Sopenharmony_ci case IWL_UCODE_TLV_RUNT_ERRLOG_PTR: 86562306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 86662306a36Sopenharmony_ci goto invalid_tlv_len; 86762306a36Sopenharmony_ci pieces->inst_errlog_ptr = 86862306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case IWL_UCODE_TLV_ENHANCE_SENS_TBL: 87162306a36Sopenharmony_ci if (tlv_len) 87262306a36Sopenharmony_ci goto invalid_tlv_len; 87362306a36Sopenharmony_ci drv->fw.enhance_sensitivity_table = true; 87462306a36Sopenharmony_ci break; 87562306a36Sopenharmony_ci case IWL_UCODE_TLV_WOWLAN_INST: 87662306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_WOWLAN, 87762306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, tlv_data); 87862306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_WOWLAN, 87962306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, tlv_len); 88062306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_WOWLAN, 88162306a36Sopenharmony_ci IWL_UCODE_SECTION_INST, 88262306a36Sopenharmony_ci IWLAGN_RTC_INST_LOWER_BOUND); 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci case IWL_UCODE_TLV_WOWLAN_DATA: 88562306a36Sopenharmony_ci set_sec_data(pieces, IWL_UCODE_WOWLAN, 88662306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, tlv_data); 88762306a36Sopenharmony_ci set_sec_size(pieces, IWL_UCODE_WOWLAN, 88862306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, tlv_len); 88962306a36Sopenharmony_ci set_sec_offset(pieces, IWL_UCODE_WOWLAN, 89062306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA, 89162306a36Sopenharmony_ci IWLAGN_RTC_DATA_LOWER_BOUND); 89262306a36Sopenharmony_ci break; 89362306a36Sopenharmony_ci case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE: 89462306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 89562306a36Sopenharmony_ci goto invalid_tlv_len; 89662306a36Sopenharmony_ci capa->standard_phy_calibration_size = 89762306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci case IWL_UCODE_TLV_SEC_RT: 90062306a36Sopenharmony_ci iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, 90162306a36Sopenharmony_ci tlv_len); 90262306a36Sopenharmony_ci drv->fw.type = IWL_FW_MVM; 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci case IWL_UCODE_TLV_SEC_INIT: 90562306a36Sopenharmony_ci iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, 90662306a36Sopenharmony_ci tlv_len); 90762306a36Sopenharmony_ci drv->fw.type = IWL_FW_MVM; 90862306a36Sopenharmony_ci break; 90962306a36Sopenharmony_ci case IWL_UCODE_TLV_SEC_WOWLAN: 91062306a36Sopenharmony_ci iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, 91162306a36Sopenharmony_ci tlv_len); 91262306a36Sopenharmony_ci drv->fw.type = IWL_FW_MVM; 91362306a36Sopenharmony_ci break; 91462306a36Sopenharmony_ci case IWL_UCODE_TLV_DEF_CALIB: 91562306a36Sopenharmony_ci if (tlv_len != sizeof(struct iwl_tlv_calib_data)) 91662306a36Sopenharmony_ci goto invalid_tlv_len; 91762306a36Sopenharmony_ci if (iwl_set_default_calib(drv, tlv_data)) 91862306a36Sopenharmony_ci goto tlv_error; 91962306a36Sopenharmony_ci break; 92062306a36Sopenharmony_ci case IWL_UCODE_TLV_PHY_SKU: 92162306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 92262306a36Sopenharmony_ci goto invalid_tlv_len; 92362306a36Sopenharmony_ci drv->fw.phy_config = le32_to_cpup((const __le32 *)tlv_data); 92462306a36Sopenharmony_ci drv->fw.valid_tx_ant = (drv->fw.phy_config & 92562306a36Sopenharmony_ci FW_PHY_CFG_TX_CHAIN) >> 92662306a36Sopenharmony_ci FW_PHY_CFG_TX_CHAIN_POS; 92762306a36Sopenharmony_ci drv->fw.valid_rx_ant = (drv->fw.phy_config & 92862306a36Sopenharmony_ci FW_PHY_CFG_RX_CHAIN) >> 92962306a36Sopenharmony_ci FW_PHY_CFG_RX_CHAIN_POS; 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci case IWL_UCODE_TLV_SECURE_SEC_RT: 93262306a36Sopenharmony_ci iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, 93362306a36Sopenharmony_ci tlv_len); 93462306a36Sopenharmony_ci drv->fw.type = IWL_FW_MVM; 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci case IWL_UCODE_TLV_SECURE_SEC_INIT: 93762306a36Sopenharmony_ci iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, 93862306a36Sopenharmony_ci tlv_len); 93962306a36Sopenharmony_ci drv->fw.type = IWL_FW_MVM; 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci case IWL_UCODE_TLV_SECURE_SEC_WOWLAN: 94262306a36Sopenharmony_ci iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, 94362306a36Sopenharmony_ci tlv_len); 94462306a36Sopenharmony_ci drv->fw.type = IWL_FW_MVM; 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci case IWL_UCODE_TLV_NUM_OF_CPU: 94762306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 94862306a36Sopenharmony_ci goto invalid_tlv_len; 94962306a36Sopenharmony_ci num_of_cpus = 95062306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (num_of_cpus == 2) { 95362306a36Sopenharmony_ci drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus = 95462306a36Sopenharmony_ci true; 95562306a36Sopenharmony_ci drv->fw.img[IWL_UCODE_INIT].is_dual_cpus = 95662306a36Sopenharmony_ci true; 95762306a36Sopenharmony_ci drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus = 95862306a36Sopenharmony_ci true; 95962306a36Sopenharmony_ci } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { 96062306a36Sopenharmony_ci IWL_ERR(drv, "Driver support up to 2 CPUs\n"); 96162306a36Sopenharmony_ci return -EINVAL; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci break; 96462306a36Sopenharmony_ci case IWL_UCODE_TLV_N_SCAN_CHANNELS: 96562306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 96662306a36Sopenharmony_ci goto invalid_tlv_len; 96762306a36Sopenharmony_ci capa->n_scan_channels = 96862306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_VERSION: { 97162306a36Sopenharmony_ci const __le32 *ptr = (const void *)tlv_data; 97262306a36Sopenharmony_ci u32 major, minor; 97362306a36Sopenharmony_ci u8 local_comp; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (tlv_len != sizeof(u32) * 3) 97662306a36Sopenharmony_ci goto invalid_tlv_len; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci major = le32_to_cpup(ptr++); 97962306a36Sopenharmony_ci minor = le32_to_cpup(ptr++); 98062306a36Sopenharmony_ci local_comp = le32_to_cpup(ptr); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (major >= 35) 98362306a36Sopenharmony_ci snprintf(drv->fw.fw_version, 98462306a36Sopenharmony_ci sizeof(drv->fw.fw_version), 98562306a36Sopenharmony_ci "%u.%08x.%u %s", major, minor, 98662306a36Sopenharmony_ci local_comp, iwl_reduced_fw_name(drv)); 98762306a36Sopenharmony_ci else 98862306a36Sopenharmony_ci snprintf(drv->fw.fw_version, 98962306a36Sopenharmony_ci sizeof(drv->fw.fw_version), 99062306a36Sopenharmony_ci "%u.%u.%u %s", major, minor, 99162306a36Sopenharmony_ci local_comp, iwl_reduced_fw_name(drv)); 99262306a36Sopenharmony_ci break; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_DBG_DEST: { 99562306a36Sopenharmony_ci const struct iwl_fw_dbg_dest_tlv *dest = NULL; 99662306a36Sopenharmony_ci const struct iwl_fw_dbg_dest_tlv_v1 *dest_v1 = NULL; 99762306a36Sopenharmony_ci u8 mon_mode; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci pieces->dbg_dest_ver = (const u8 *)tlv_data; 100062306a36Sopenharmony_ci if (*pieces->dbg_dest_ver == 1) { 100162306a36Sopenharmony_ci dest = (const void *)tlv_data; 100262306a36Sopenharmony_ci } else if (*pieces->dbg_dest_ver == 0) { 100362306a36Sopenharmony_ci dest_v1 = (const void *)tlv_data; 100462306a36Sopenharmony_ci } else { 100562306a36Sopenharmony_ci IWL_ERR(drv, 100662306a36Sopenharmony_ci "The version is %d, and it is invalid\n", 100762306a36Sopenharmony_ci *pieces->dbg_dest_ver); 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci if (pieces->dbg_dest_tlv_init) { 101262306a36Sopenharmony_ci IWL_ERR(drv, 101362306a36Sopenharmony_ci "dbg destination ignored, already exists\n"); 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci pieces->dbg_dest_tlv_init = true; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (dest_v1) { 102062306a36Sopenharmony_ci pieces->dbg_dest_tlv_v1 = dest_v1; 102162306a36Sopenharmony_ci mon_mode = dest_v1->monitor_mode; 102262306a36Sopenharmony_ci } else { 102362306a36Sopenharmony_ci pieces->dbg_dest_tlv = dest; 102462306a36Sopenharmony_ci mon_mode = dest->monitor_mode; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci IWL_INFO(drv, "Found debug destination: %s\n", 102862306a36Sopenharmony_ci get_fw_dbg_mode_string(mon_mode)); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci drv->fw.dbg.n_dest_reg = (dest_v1) ? 103162306a36Sopenharmony_ci tlv_len - 103262306a36Sopenharmony_ci offsetof(struct iwl_fw_dbg_dest_tlv_v1, 103362306a36Sopenharmony_ci reg_ops) : 103462306a36Sopenharmony_ci tlv_len - 103562306a36Sopenharmony_ci offsetof(struct iwl_fw_dbg_dest_tlv, 103662306a36Sopenharmony_ci reg_ops); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci drv->fw.dbg.n_dest_reg /= 103962306a36Sopenharmony_ci sizeof(drv->fw.dbg.dest_tlv->reg_ops[0]); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci break; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_DBG_CONF: { 104462306a36Sopenharmony_ci const struct iwl_fw_dbg_conf_tlv *conf = 104562306a36Sopenharmony_ci (const void *)tlv_data; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (!pieces->dbg_dest_tlv_init) { 104862306a36Sopenharmony_ci IWL_ERR(drv, 104962306a36Sopenharmony_ci "Ignore dbg config %d - no destination configured\n", 105062306a36Sopenharmony_ci conf->id); 105162306a36Sopenharmony_ci break; 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (conf->id >= ARRAY_SIZE(drv->fw.dbg.conf_tlv)) { 105562306a36Sopenharmony_ci IWL_ERR(drv, 105662306a36Sopenharmony_ci "Skip unknown configuration: %d\n", 105762306a36Sopenharmony_ci conf->id); 105862306a36Sopenharmony_ci break; 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (pieces->dbg_conf_tlv[conf->id]) { 106262306a36Sopenharmony_ci IWL_ERR(drv, 106362306a36Sopenharmony_ci "Ignore duplicate dbg config %d\n", 106462306a36Sopenharmony_ci conf->id); 106562306a36Sopenharmony_ci break; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (conf->usniffer) 106962306a36Sopenharmony_ci usniffer_req = true; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci IWL_INFO(drv, "Found debug configuration: %d\n", 107262306a36Sopenharmony_ci conf->id); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci pieces->dbg_conf_tlv[conf->id] = conf; 107562306a36Sopenharmony_ci pieces->dbg_conf_tlv_len[conf->id] = tlv_len; 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_DBG_TRIGGER: { 107962306a36Sopenharmony_ci const struct iwl_fw_dbg_trigger_tlv *trigger = 108062306a36Sopenharmony_ci (const void *)tlv_data; 108162306a36Sopenharmony_ci u32 trigger_id = le32_to_cpu(trigger->id); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (trigger_id >= ARRAY_SIZE(drv->fw.dbg.trigger_tlv)) { 108462306a36Sopenharmony_ci IWL_ERR(drv, 108562306a36Sopenharmony_ci "Skip unknown trigger: %u\n", 108662306a36Sopenharmony_ci trigger->id); 108762306a36Sopenharmony_ci break; 108862306a36Sopenharmony_ci } 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (pieces->dbg_trigger_tlv[trigger_id]) { 109162306a36Sopenharmony_ci IWL_ERR(drv, 109262306a36Sopenharmony_ci "Ignore duplicate dbg trigger %u\n", 109362306a36Sopenharmony_ci trigger->id); 109462306a36Sopenharmony_ci break; 109562306a36Sopenharmony_ci } 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci pieces->dbg_trigger_tlv[trigger_id] = trigger; 110062306a36Sopenharmony_ci pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len; 110162306a36Sopenharmony_ci break; 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_DBG_DUMP_LST: { 110462306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) { 110562306a36Sopenharmony_ci IWL_ERR(drv, 110662306a36Sopenharmony_ci "dbg lst mask size incorrect, skip\n"); 110762306a36Sopenharmony_ci break; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci drv->fw.dbg.dump_mask = 111162306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 111262306a36Sopenharmony_ci break; 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci case IWL_UCODE_TLV_SEC_RT_USNIFFER: 111562306a36Sopenharmony_ci *usniffer_images = true; 111662306a36Sopenharmony_ci iwl_store_ucode_sec(pieces, tlv_data, 111762306a36Sopenharmony_ci IWL_UCODE_REGULAR_USNIFFER, 111862306a36Sopenharmony_ci tlv_len); 111962306a36Sopenharmony_ci break; 112062306a36Sopenharmony_ci case IWL_UCODE_TLV_PAGING: 112162306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 112262306a36Sopenharmony_ci goto invalid_tlv_len; 112362306a36Sopenharmony_ci paging_mem_size = le32_to_cpup((const __le32 *)tlv_data); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci IWL_DEBUG_FW(drv, 112662306a36Sopenharmony_ci "Paging: paging enabled (size = %u bytes)\n", 112762306a36Sopenharmony_ci paging_mem_size); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (paging_mem_size > MAX_PAGING_IMAGE_SIZE) { 113062306a36Sopenharmony_ci IWL_ERR(drv, 113162306a36Sopenharmony_ci "Paging: driver supports up to %lu bytes for paging image\n", 113262306a36Sopenharmony_ci MAX_PAGING_IMAGE_SIZE); 113362306a36Sopenharmony_ci return -EINVAL; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (paging_mem_size & (FW_PAGING_SIZE - 1)) { 113762306a36Sopenharmony_ci IWL_ERR(drv, 113862306a36Sopenharmony_ci "Paging: image isn't multiple %lu\n", 113962306a36Sopenharmony_ci FW_PAGING_SIZE); 114062306a36Sopenharmony_ci return -EINVAL; 114162306a36Sopenharmony_ci } 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci drv->fw.img[IWL_UCODE_REGULAR].paging_mem_size = 114462306a36Sopenharmony_ci paging_mem_size; 114562306a36Sopenharmony_ci usniffer_img = IWL_UCODE_REGULAR_USNIFFER; 114662306a36Sopenharmony_ci drv->fw.img[usniffer_img].paging_mem_size = 114762306a36Sopenharmony_ci paging_mem_size; 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_GSCAN_CAPA: 115062306a36Sopenharmony_ci /* ignored */ 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_MEM_SEG: { 115362306a36Sopenharmony_ci const struct iwl_fw_dbg_mem_seg_tlv *dbg_mem = 115462306a36Sopenharmony_ci (const void *)tlv_data; 115562306a36Sopenharmony_ci size_t size; 115662306a36Sopenharmony_ci struct iwl_fw_dbg_mem_seg_tlv *n; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (tlv_len != (sizeof(*dbg_mem))) 115962306a36Sopenharmony_ci goto invalid_tlv_len; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci IWL_DEBUG_INFO(drv, "Found debug memory segment: %u\n", 116262306a36Sopenharmony_ci dbg_mem->data_type); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci size = sizeof(*pieces->dbg_mem_tlv) * 116562306a36Sopenharmony_ci (pieces->n_mem_tlv + 1); 116662306a36Sopenharmony_ci n = krealloc(pieces->dbg_mem_tlv, size, GFP_KERNEL); 116762306a36Sopenharmony_ci if (!n) 116862306a36Sopenharmony_ci return -ENOMEM; 116962306a36Sopenharmony_ci pieces->dbg_mem_tlv = n; 117062306a36Sopenharmony_ci pieces->dbg_mem_tlv[pieces->n_mem_tlv] = *dbg_mem; 117162306a36Sopenharmony_ci pieces->n_mem_tlv++; 117262306a36Sopenharmony_ci break; 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci case IWL_UCODE_TLV_IML: { 117562306a36Sopenharmony_ci drv->fw.iml_len = tlv_len; 117662306a36Sopenharmony_ci drv->fw.iml = kmemdup(tlv_data, tlv_len, GFP_KERNEL); 117762306a36Sopenharmony_ci if (!drv->fw.iml) 117862306a36Sopenharmony_ci return -ENOMEM; 117962306a36Sopenharmony_ci break; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_RECOVERY_INFO: { 118262306a36Sopenharmony_ci const struct { 118362306a36Sopenharmony_ci __le32 buf_addr; 118462306a36Sopenharmony_ci __le32 buf_size; 118562306a36Sopenharmony_ci } *recov_info = (const void *)tlv_data; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (tlv_len != sizeof(*recov_info)) 118862306a36Sopenharmony_ci goto invalid_tlv_len; 118962306a36Sopenharmony_ci capa->error_log_addr = 119062306a36Sopenharmony_ci le32_to_cpu(recov_info->buf_addr); 119162306a36Sopenharmony_ci capa->error_log_size = 119262306a36Sopenharmony_ci le32_to_cpu(recov_info->buf_size); 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci break; 119562306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_FSEQ_VERSION: { 119662306a36Sopenharmony_ci const struct { 119762306a36Sopenharmony_ci u8 version[32]; 119862306a36Sopenharmony_ci u8 sha1[20]; 119962306a36Sopenharmony_ci } *fseq_ver = (const void *)tlv_data; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (tlv_len != sizeof(*fseq_ver)) 120262306a36Sopenharmony_ci goto invalid_tlv_len; 120362306a36Sopenharmony_ci IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %s\n", 120462306a36Sopenharmony_ci fseq_ver->version); 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci break; 120762306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_NUM_STATIONS: 120862306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 120962306a36Sopenharmony_ci goto invalid_tlv_len; 121062306a36Sopenharmony_ci if (le32_to_cpup((const __le32 *)tlv_data) > 121162306a36Sopenharmony_ci IWL_MVM_STATION_COUNT_MAX) { 121262306a36Sopenharmony_ci IWL_ERR(drv, 121362306a36Sopenharmony_ci "%d is an invalid number of station\n", 121462306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data)); 121562306a36Sopenharmony_ci goto tlv_error; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci capa->num_stations = 121862306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 121962306a36Sopenharmony_ci break; 122062306a36Sopenharmony_ci case IWL_UCODE_TLV_FW_NUM_BEACONS: 122162306a36Sopenharmony_ci if (tlv_len != sizeof(u32)) 122262306a36Sopenharmony_ci goto invalid_tlv_len; 122362306a36Sopenharmony_ci capa->num_beacons = 122462306a36Sopenharmony_ci le32_to_cpup((const __le32 *)tlv_data); 122562306a36Sopenharmony_ci break; 122662306a36Sopenharmony_ci case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: { 122762306a36Sopenharmony_ci const struct iwl_umac_debug_addrs *dbg_ptrs = 122862306a36Sopenharmony_ci (const void *)tlv_data; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (tlv_len != sizeof(*dbg_ptrs)) 123162306a36Sopenharmony_ci goto invalid_tlv_len; 123262306a36Sopenharmony_ci if (drv->trans->trans_cfg->device_family < 123362306a36Sopenharmony_ci IWL_DEVICE_FAMILY_22000) 123462306a36Sopenharmony_ci break; 123562306a36Sopenharmony_ci drv->trans->dbg.umac_error_event_table = 123662306a36Sopenharmony_ci le32_to_cpu(dbg_ptrs->error_info_addr) & 123762306a36Sopenharmony_ci ~FW_ADDR_CACHE_CONTROL; 123862306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 123962306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_UMAC; 124062306a36Sopenharmony_ci break; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci case IWL_UCODE_TLV_LMAC_DEBUG_ADDRS: { 124362306a36Sopenharmony_ci const struct iwl_lmac_debug_addrs *dbg_ptrs = 124462306a36Sopenharmony_ci (const void *)tlv_data; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (tlv_len != sizeof(*dbg_ptrs)) 124762306a36Sopenharmony_ci goto invalid_tlv_len; 124862306a36Sopenharmony_ci if (drv->trans->trans_cfg->device_family < 124962306a36Sopenharmony_ci IWL_DEVICE_FAMILY_22000) 125062306a36Sopenharmony_ci break; 125162306a36Sopenharmony_ci drv->trans->dbg.lmac_error_event_table[0] = 125262306a36Sopenharmony_ci le32_to_cpu(dbg_ptrs->error_event_table_ptr) & 125362306a36Sopenharmony_ci ~FW_ADDR_CACHE_CONTROL; 125462306a36Sopenharmony_ci drv->trans->dbg.error_event_table_tlv_status |= 125562306a36Sopenharmony_ci IWL_ERROR_EVENT_TABLE_LMAC1; 125662306a36Sopenharmony_ci break; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci case IWL_UCODE_TLV_TYPE_REGIONS: 125962306a36Sopenharmony_ci iwl_parse_dbg_tlv_assert_tables(drv, tlv); 126062306a36Sopenharmony_ci fallthrough; 126162306a36Sopenharmony_ci case IWL_UCODE_TLV_TYPE_DEBUG_INFO: 126262306a36Sopenharmony_ci case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: 126362306a36Sopenharmony_ci case IWL_UCODE_TLV_TYPE_HCMD: 126462306a36Sopenharmony_ci case IWL_UCODE_TLV_TYPE_TRIGGERS: 126562306a36Sopenharmony_ci case IWL_UCODE_TLV_TYPE_CONF_SET: 126662306a36Sopenharmony_ci if (iwlwifi_mod_params.enable_ini) 126762306a36Sopenharmony_ci iwl_dbg_tlv_alloc(drv->trans, tlv, false); 126862306a36Sopenharmony_ci break; 126962306a36Sopenharmony_ci case IWL_UCODE_TLV_CMD_VERSIONS: 127062306a36Sopenharmony_ci if (tlv_len % sizeof(struct iwl_fw_cmd_version)) { 127162306a36Sopenharmony_ci IWL_ERR(drv, 127262306a36Sopenharmony_ci "Invalid length for command versions: %u\n", 127362306a36Sopenharmony_ci tlv_len); 127462306a36Sopenharmony_ci tlv_len /= sizeof(struct iwl_fw_cmd_version); 127562306a36Sopenharmony_ci tlv_len *= sizeof(struct iwl_fw_cmd_version); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci if (WARN_ON(capa->cmd_versions)) 127862306a36Sopenharmony_ci return -EINVAL; 127962306a36Sopenharmony_ci capa->cmd_versions = kmemdup(tlv_data, tlv_len, 128062306a36Sopenharmony_ci GFP_KERNEL); 128162306a36Sopenharmony_ci if (!capa->cmd_versions) 128262306a36Sopenharmony_ci return -ENOMEM; 128362306a36Sopenharmony_ci capa->n_cmd_versions = 128462306a36Sopenharmony_ci tlv_len / sizeof(struct iwl_fw_cmd_version); 128562306a36Sopenharmony_ci break; 128662306a36Sopenharmony_ci case IWL_UCODE_TLV_PHY_INTEGRATION_VERSION: 128762306a36Sopenharmony_ci if (drv->fw.phy_integration_ver) { 128862306a36Sopenharmony_ci IWL_ERR(drv, 128962306a36Sopenharmony_ci "phy integration str ignored, already exists\n"); 129062306a36Sopenharmony_ci break; 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci drv->fw.phy_integration_ver = 129462306a36Sopenharmony_ci kmemdup(tlv_data, tlv_len, GFP_KERNEL); 129562306a36Sopenharmony_ci if (!drv->fw.phy_integration_ver) 129662306a36Sopenharmony_ci return -ENOMEM; 129762306a36Sopenharmony_ci drv->fw.phy_integration_ver_len = tlv_len; 129862306a36Sopenharmony_ci break; 129962306a36Sopenharmony_ci case IWL_UCODE_TLV_SEC_TABLE_ADDR: 130062306a36Sopenharmony_ci case IWL_UCODE_TLV_D3_KEK_KCK_ADDR: 130162306a36Sopenharmony_ci iwl_drv_set_dump_exclude(drv, tlv_type, 130262306a36Sopenharmony_ci tlv_data, tlv_len); 130362306a36Sopenharmony_ci break; 130462306a36Sopenharmony_ci case IWL_UCODE_TLV_CURRENT_PC: 130562306a36Sopenharmony_ci if (tlv_len < sizeof(struct iwl_pc_data)) 130662306a36Sopenharmony_ci goto invalid_tlv_len; 130762306a36Sopenharmony_ci drv->trans->dbg.num_pc = 130862306a36Sopenharmony_ci tlv_len / sizeof(struct iwl_pc_data); 130962306a36Sopenharmony_ci drv->trans->dbg.pc_data = 131062306a36Sopenharmony_ci kmemdup(tlv_data, tlv_len, GFP_KERNEL); 131162306a36Sopenharmony_ci break; 131262306a36Sopenharmony_ci default: 131362306a36Sopenharmony_ci IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); 131462306a36Sopenharmony_ci break; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (!fw_has_capa(capa, IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED) && 131962306a36Sopenharmony_ci usniffer_req && !*usniffer_images) { 132062306a36Sopenharmony_ci IWL_ERR(drv, 132162306a36Sopenharmony_ci "user selected to work with usniffer but usniffer image isn't available in ucode package\n"); 132262306a36Sopenharmony_ci return -EINVAL; 132362306a36Sopenharmony_ci } 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (len) { 132662306a36Sopenharmony_ci IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len); 132762306a36Sopenharmony_ci iwl_print_hex_dump(drv, IWL_DL_FW, data, len); 132862306a36Sopenharmony_ci return -EINVAL; 132962306a36Sopenharmony_ci } 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci return 0; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci invalid_tlv_len: 133462306a36Sopenharmony_ci IWL_ERR(drv, "TLV %d has invalid size: %u\n", tlv_type, tlv_len); 133562306a36Sopenharmony_ci tlv_error: 133662306a36Sopenharmony_ci iwl_print_hex_dump(drv, IWL_DL_FW, tlv_data, tlv_len); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci return -EINVAL; 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_cistatic int iwl_alloc_ucode(struct iwl_drv *drv, 134262306a36Sopenharmony_ci struct iwl_firmware_pieces *pieces, 134362306a36Sopenharmony_ci enum iwl_ucode_type type) 134462306a36Sopenharmony_ci{ 134562306a36Sopenharmony_ci int i; 134662306a36Sopenharmony_ci struct fw_desc *sec; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci sec = kcalloc(pieces->img[type].sec_counter, sizeof(*sec), GFP_KERNEL); 134962306a36Sopenharmony_ci if (!sec) 135062306a36Sopenharmony_ci return -ENOMEM; 135162306a36Sopenharmony_ci drv->fw.img[type].sec = sec; 135262306a36Sopenharmony_ci drv->fw.img[type].num_sec = pieces->img[type].sec_counter; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci for (i = 0; i < pieces->img[type].sec_counter; i++) 135562306a36Sopenharmony_ci if (iwl_alloc_fw_desc(drv, &sec[i], get_sec(pieces, type, i))) 135662306a36Sopenharmony_ci return -ENOMEM; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci return 0; 135962306a36Sopenharmony_ci} 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_cistatic int validate_sec_sizes(struct iwl_drv *drv, 136262306a36Sopenharmony_ci struct iwl_firmware_pieces *pieces, 136362306a36Sopenharmony_ci const struct iwl_cfg *cfg) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %zd\n", 136662306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_REGULAR, 136762306a36Sopenharmony_ci IWL_UCODE_SECTION_INST)); 136862306a36Sopenharmony_ci IWL_DEBUG_INFO(drv, "f/w package hdr runtime data size = %zd\n", 136962306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_REGULAR, 137062306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA)); 137162306a36Sopenharmony_ci IWL_DEBUG_INFO(drv, "f/w package hdr init inst size = %zd\n", 137262306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST)); 137362306a36Sopenharmony_ci IWL_DEBUG_INFO(drv, "f/w package hdr init data size = %zd\n", 137462306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA)); 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* Verify that uCode images will fit in card's SRAM. */ 137762306a36Sopenharmony_ci if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) > 137862306a36Sopenharmony_ci cfg->max_inst_size) { 137962306a36Sopenharmony_ci IWL_ERR(drv, "uCode instr len %zd too large to fit in\n", 138062306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_REGULAR, 138162306a36Sopenharmony_ci IWL_UCODE_SECTION_INST)); 138262306a36Sopenharmony_ci return -1; 138362306a36Sopenharmony_ci } 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) > 138662306a36Sopenharmony_ci cfg->max_data_size) { 138762306a36Sopenharmony_ci IWL_ERR(drv, "uCode data len %zd too large to fit in\n", 138862306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_REGULAR, 138962306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA)); 139062306a36Sopenharmony_ci return -1; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) > 139462306a36Sopenharmony_ci cfg->max_inst_size) { 139562306a36Sopenharmony_ci IWL_ERR(drv, "uCode init instr len %zd too large to fit in\n", 139662306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_INIT, 139762306a36Sopenharmony_ci IWL_UCODE_SECTION_INST)); 139862306a36Sopenharmony_ci return -1; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) > 140262306a36Sopenharmony_ci cfg->max_data_size) { 140362306a36Sopenharmony_ci IWL_ERR(drv, "uCode init data len %zd too large to fit in\n", 140462306a36Sopenharmony_ci get_sec_size(pieces, IWL_UCODE_REGULAR, 140562306a36Sopenharmony_ci IWL_UCODE_SECTION_DATA)); 140662306a36Sopenharmony_ci return -1; 140762306a36Sopenharmony_ci } 140862306a36Sopenharmony_ci return 0; 140962306a36Sopenharmony_ci} 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_cistatic struct iwl_op_mode * 141262306a36Sopenharmony_ci_iwl_op_mode_start(struct iwl_drv *drv, struct iwlwifi_opmode_table *op) 141362306a36Sopenharmony_ci{ 141462306a36Sopenharmony_ci const struct iwl_op_mode_ops *ops = op->ops; 141562306a36Sopenharmony_ci struct dentry *dbgfs_dir = NULL; 141662306a36Sopenharmony_ci struct iwl_op_mode *op_mode = NULL; 141762306a36Sopenharmony_ci int retry, max_retry = !!iwlwifi_mod_params.fw_restart * IWL_MAX_INIT_RETRY; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci for (retry = 0; retry <= max_retry; retry++) { 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 142262306a36Sopenharmony_ci drv->dbgfs_op_mode = debugfs_create_dir(op->name, 142362306a36Sopenharmony_ci drv->dbgfs_drv); 142462306a36Sopenharmony_ci dbgfs_dir = drv->dbgfs_op_mode; 142562306a36Sopenharmony_ci#endif 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci op_mode = ops->start(drv->trans, drv->trans->cfg, 142862306a36Sopenharmony_ci &drv->fw, dbgfs_dir); 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (op_mode) 143162306a36Sopenharmony_ci return op_mode; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci IWL_ERR(drv, "retry init count %d\n", retry); 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 143662306a36Sopenharmony_ci debugfs_remove_recursive(drv->dbgfs_op_mode); 143762306a36Sopenharmony_ci drv->dbgfs_op_mode = NULL; 143862306a36Sopenharmony_ci#endif 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci return NULL; 144262306a36Sopenharmony_ci} 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_cistatic void _iwl_op_mode_stop(struct iwl_drv *drv) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci /* op_mode can be NULL if its start failed */ 144762306a36Sopenharmony_ci if (drv->op_mode) { 144862306a36Sopenharmony_ci iwl_op_mode_stop(drv->op_mode); 144962306a36Sopenharmony_ci drv->op_mode = NULL; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 145262306a36Sopenharmony_ci debugfs_remove_recursive(drv->dbgfs_op_mode); 145362306a36Sopenharmony_ci drv->dbgfs_op_mode = NULL; 145462306a36Sopenharmony_ci#endif 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci} 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci/* 145962306a36Sopenharmony_ci * iwl_req_fw_callback - callback when firmware was loaded 146062306a36Sopenharmony_ci * 146162306a36Sopenharmony_ci * If loaded successfully, copies the firmware into buffers 146262306a36Sopenharmony_ci * for the card to fetch (via DMA). 146362306a36Sopenharmony_ci */ 146462306a36Sopenharmony_cistatic void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) 146562306a36Sopenharmony_ci{ 146662306a36Sopenharmony_ci struct iwl_drv *drv = context; 146762306a36Sopenharmony_ci struct iwl_fw *fw = &drv->fw; 146862306a36Sopenharmony_ci const struct iwl_ucode_header *ucode; 146962306a36Sopenharmony_ci struct iwlwifi_opmode_table *op; 147062306a36Sopenharmony_ci int err; 147162306a36Sopenharmony_ci struct iwl_firmware_pieces *pieces; 147262306a36Sopenharmony_ci const unsigned int api_max = drv->trans->cfg->ucode_api_max; 147362306a36Sopenharmony_ci const unsigned int api_min = drv->trans->cfg->ucode_api_min; 147462306a36Sopenharmony_ci size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX]; 147562306a36Sopenharmony_ci u32 api_ver; 147662306a36Sopenharmony_ci int i; 147762306a36Sopenharmony_ci bool load_module = false; 147862306a36Sopenharmony_ci bool usniffer_images = false; 147962306a36Sopenharmony_ci bool failure = true; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; 148262306a36Sopenharmony_ci fw->ucode_capa.standard_phy_calibration_size = 148362306a36Sopenharmony_ci IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; 148462306a36Sopenharmony_ci fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS; 148562306a36Sopenharmony_ci fw->ucode_capa.num_stations = IWL_MVM_STATION_COUNT_MAX; 148662306a36Sopenharmony_ci fw->ucode_capa.num_beacons = 1; 148762306a36Sopenharmony_ci /* dump all fw memory areas by default */ 148862306a36Sopenharmony_ci fw->dbg.dump_mask = 0xffffffff; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci pieces = kzalloc(sizeof(*pieces), GFP_KERNEL); 149162306a36Sopenharmony_ci if (!pieces) 149262306a36Sopenharmony_ci goto out_free_fw; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if (!ucode_raw) 149562306a36Sopenharmony_ci goto try_again; 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci IWL_DEBUG_FW_INFO(drv, "Loaded firmware file '%s' (%zd bytes).\n", 149862306a36Sopenharmony_ci drv->firmware_name, ucode_raw->size); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci /* Make sure that we got at least the API version number */ 150162306a36Sopenharmony_ci if (ucode_raw->size < 4) { 150262306a36Sopenharmony_ci IWL_ERR(drv, "File size way too small!\n"); 150362306a36Sopenharmony_ci goto try_again; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci /* Data from ucode file: header followed by uCode images */ 150762306a36Sopenharmony_ci ucode = (const struct iwl_ucode_header *)ucode_raw->data; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci if (ucode->ver) 151062306a36Sopenharmony_ci err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces); 151162306a36Sopenharmony_ci else 151262306a36Sopenharmony_ci err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces, 151362306a36Sopenharmony_ci &fw->ucode_capa, &usniffer_images); 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci if (err) 151662306a36Sopenharmony_ci goto try_again; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (fw_has_api(&drv->fw.ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION)) 151962306a36Sopenharmony_ci api_ver = drv->fw.ucode_ver; 152062306a36Sopenharmony_ci else 152162306a36Sopenharmony_ci api_ver = IWL_UCODE_API(drv->fw.ucode_ver); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci /* 152462306a36Sopenharmony_ci * api_ver should match the api version forming part of the 152562306a36Sopenharmony_ci * firmware filename ... but we don't check for that and only rely 152662306a36Sopenharmony_ci * on the API version read from firmware header from here on forward 152762306a36Sopenharmony_ci */ 152862306a36Sopenharmony_ci if (api_ver < api_min || api_ver > api_max) { 152962306a36Sopenharmony_ci IWL_ERR(drv, 153062306a36Sopenharmony_ci "Driver unable to support your firmware API. " 153162306a36Sopenharmony_ci "Driver supports v%u, firmware is v%u.\n", 153262306a36Sopenharmony_ci api_max, api_ver); 153362306a36Sopenharmony_ci goto try_again; 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci /* 153762306a36Sopenharmony_ci * In mvm uCode there is no difference between data and instructions 153862306a36Sopenharmony_ci * sections. 153962306a36Sopenharmony_ci */ 154062306a36Sopenharmony_ci if (fw->type == IWL_FW_DVM && validate_sec_sizes(drv, pieces, 154162306a36Sopenharmony_ci drv->trans->cfg)) 154262306a36Sopenharmony_ci goto try_again; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* Allocate ucode buffers for card's bus-master loading ... */ 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci /* Runtime instructions and 2 copies of data: 154762306a36Sopenharmony_ci * 1) unmodified from disk 154862306a36Sopenharmony_ci * 2) backup cache for save/restore during power-downs 154962306a36Sopenharmony_ci */ 155062306a36Sopenharmony_ci for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) 155162306a36Sopenharmony_ci if (iwl_alloc_ucode(drv, pieces, i)) 155262306a36Sopenharmony_ci goto out_free_fw; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci if (pieces->dbg_dest_tlv_init) { 155562306a36Sopenharmony_ci size_t dbg_dest_size = sizeof(*drv->fw.dbg.dest_tlv) + 155662306a36Sopenharmony_ci sizeof(drv->fw.dbg.dest_tlv->reg_ops[0]) * 155762306a36Sopenharmony_ci drv->fw.dbg.n_dest_reg; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci drv->fw.dbg.dest_tlv = kmalloc(dbg_dest_size, GFP_KERNEL); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci if (!drv->fw.dbg.dest_tlv) 156262306a36Sopenharmony_ci goto out_free_fw; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (*pieces->dbg_dest_ver == 0) { 156562306a36Sopenharmony_ci memcpy(drv->fw.dbg.dest_tlv, pieces->dbg_dest_tlv_v1, 156662306a36Sopenharmony_ci dbg_dest_size); 156762306a36Sopenharmony_ci } else { 156862306a36Sopenharmony_ci struct iwl_fw_dbg_dest_tlv_v1 *dest_tlv = 156962306a36Sopenharmony_ci drv->fw.dbg.dest_tlv; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci dest_tlv->version = pieces->dbg_dest_tlv->version; 157262306a36Sopenharmony_ci dest_tlv->monitor_mode = 157362306a36Sopenharmony_ci pieces->dbg_dest_tlv->monitor_mode; 157462306a36Sopenharmony_ci dest_tlv->size_power = 157562306a36Sopenharmony_ci pieces->dbg_dest_tlv->size_power; 157662306a36Sopenharmony_ci dest_tlv->wrap_count = 157762306a36Sopenharmony_ci pieces->dbg_dest_tlv->wrap_count; 157862306a36Sopenharmony_ci dest_tlv->write_ptr_reg = 157962306a36Sopenharmony_ci pieces->dbg_dest_tlv->write_ptr_reg; 158062306a36Sopenharmony_ci dest_tlv->base_shift = 158162306a36Sopenharmony_ci pieces->dbg_dest_tlv->base_shift; 158262306a36Sopenharmony_ci memcpy(dest_tlv->reg_ops, 158362306a36Sopenharmony_ci pieces->dbg_dest_tlv->reg_ops, 158462306a36Sopenharmony_ci sizeof(drv->fw.dbg.dest_tlv->reg_ops[0]) * 158562306a36Sopenharmony_ci drv->fw.dbg.n_dest_reg); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci /* In version 1 of the destination tlv, which is 158862306a36Sopenharmony_ci * relevant for internal buffer exclusively, 158962306a36Sopenharmony_ci * the base address is part of given with the length 159062306a36Sopenharmony_ci * of the buffer, and the size shift is give instead of 159162306a36Sopenharmony_ci * end shift. We now store these values in base_reg, 159262306a36Sopenharmony_ci * and end shift, and when dumping the data we'll 159362306a36Sopenharmony_ci * manipulate it for extracting both the length and 159462306a36Sopenharmony_ci * base address */ 159562306a36Sopenharmony_ci dest_tlv->base_reg = pieces->dbg_dest_tlv->cfg_reg; 159662306a36Sopenharmony_ci dest_tlv->end_shift = 159762306a36Sopenharmony_ci pieces->dbg_dest_tlv->size_shift; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci } 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drv->fw.dbg.conf_tlv); i++) { 160262306a36Sopenharmony_ci if (pieces->dbg_conf_tlv[i]) { 160362306a36Sopenharmony_ci drv->fw.dbg.conf_tlv[i] = 160462306a36Sopenharmony_ci kmemdup(pieces->dbg_conf_tlv[i], 160562306a36Sopenharmony_ci pieces->dbg_conf_tlv_len[i], 160662306a36Sopenharmony_ci GFP_KERNEL); 160762306a36Sopenharmony_ci if (!drv->fw.dbg.conf_tlv[i]) 160862306a36Sopenharmony_ci goto out_free_fw; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci memset(&trigger_tlv_sz, 0xff, sizeof(trigger_tlv_sz)); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_MISSED_BEACONS] = 161562306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_missed_bcon); 161662306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_CHANNEL_SWITCH] = 0; 161762306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_FW_NOTIF] = 161862306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_cmd); 161962306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_MLME] = 162062306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_mlme); 162162306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_STATS] = 162262306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_stats); 162362306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_RSSI] = 162462306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_low_rssi); 162562306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_TXQ_TIMERS] = 162662306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_txq_timer); 162762306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_TIME_EVENT] = 162862306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_time_event); 162962306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_BA] = 163062306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_ba); 163162306a36Sopenharmony_ci trigger_tlv_sz[FW_DBG_TRIGGER_TDLS] = 163262306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_tdls); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(drv->fw.dbg.trigger_tlv); i++) { 163562306a36Sopenharmony_ci if (pieces->dbg_trigger_tlv[i]) { 163662306a36Sopenharmony_ci /* 163762306a36Sopenharmony_ci * If the trigger isn't long enough, WARN and exit. 163862306a36Sopenharmony_ci * Someone is trying to debug something and he won't 163962306a36Sopenharmony_ci * be able to catch the bug he is trying to chase. 164062306a36Sopenharmony_ci * We'd better be noisy to be sure he knows what's 164162306a36Sopenharmony_ci * going on. 164262306a36Sopenharmony_ci */ 164362306a36Sopenharmony_ci if (WARN_ON(pieces->dbg_trigger_tlv_len[i] < 164462306a36Sopenharmony_ci (trigger_tlv_sz[i] + 164562306a36Sopenharmony_ci sizeof(struct iwl_fw_dbg_trigger_tlv)))) 164662306a36Sopenharmony_ci goto out_free_fw; 164762306a36Sopenharmony_ci drv->fw.dbg.trigger_tlv_len[i] = 164862306a36Sopenharmony_ci pieces->dbg_trigger_tlv_len[i]; 164962306a36Sopenharmony_ci drv->fw.dbg.trigger_tlv[i] = 165062306a36Sopenharmony_ci kmemdup(pieces->dbg_trigger_tlv[i], 165162306a36Sopenharmony_ci drv->fw.dbg.trigger_tlv_len[i], 165262306a36Sopenharmony_ci GFP_KERNEL); 165362306a36Sopenharmony_ci if (!drv->fw.dbg.trigger_tlv[i]) 165462306a36Sopenharmony_ci goto out_free_fw; 165562306a36Sopenharmony_ci } 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* Now that we can no longer fail, copy information */ 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci drv->fw.dbg.mem_tlv = pieces->dbg_mem_tlv; 166162306a36Sopenharmony_ci pieces->dbg_mem_tlv = NULL; 166262306a36Sopenharmony_ci drv->fw.dbg.n_mem_tlv = pieces->n_mem_tlv; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci /* 166562306a36Sopenharmony_ci * The (size - 16) / 12 formula is based on the information recorded 166662306a36Sopenharmony_ci * for each event, which is of mode 1 (including timestamp) for all 166762306a36Sopenharmony_ci * new microcodes that include this information. 166862306a36Sopenharmony_ci */ 166962306a36Sopenharmony_ci fw->init_evtlog_ptr = pieces->init_evtlog_ptr; 167062306a36Sopenharmony_ci if (pieces->init_evtlog_size) 167162306a36Sopenharmony_ci fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12; 167262306a36Sopenharmony_ci else 167362306a36Sopenharmony_ci fw->init_evtlog_size = 167462306a36Sopenharmony_ci drv->trans->trans_cfg->base_params->max_event_log_size; 167562306a36Sopenharmony_ci fw->init_errlog_ptr = pieces->init_errlog_ptr; 167662306a36Sopenharmony_ci fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr; 167762306a36Sopenharmony_ci if (pieces->inst_evtlog_size) 167862306a36Sopenharmony_ci fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12; 167962306a36Sopenharmony_ci else 168062306a36Sopenharmony_ci fw->inst_evtlog_size = 168162306a36Sopenharmony_ci drv->trans->trans_cfg->base_params->max_event_log_size; 168262306a36Sopenharmony_ci fw->inst_errlog_ptr = pieces->inst_errlog_ptr; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* 168562306a36Sopenharmony_ci * figure out the offset of chain noise reset and gain commands 168662306a36Sopenharmony_ci * base on the size of standard phy calibration commands table size 168762306a36Sopenharmony_ci */ 168862306a36Sopenharmony_ci if (fw->ucode_capa.standard_phy_calibration_size > 168962306a36Sopenharmony_ci IWL_MAX_PHY_CALIBRATE_TBL_SIZE) 169062306a36Sopenharmony_ci fw->ucode_capa.standard_phy_calibration_size = 169162306a36Sopenharmony_ci IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci /* We have our copies now, allow OS release its copies */ 169462306a36Sopenharmony_ci release_firmware(ucode_raw); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci iwl_dbg_tlv_load_bin(drv->trans->dev, drv->trans); 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci mutex_lock(&iwlwifi_opmode_table_mtx); 169962306a36Sopenharmony_ci switch (fw->type) { 170062306a36Sopenharmony_ci case IWL_FW_DVM: 170162306a36Sopenharmony_ci op = &iwlwifi_opmode_table[DVM_OP_MODE]; 170262306a36Sopenharmony_ci break; 170362306a36Sopenharmony_ci default: 170462306a36Sopenharmony_ci WARN(1, "Invalid fw type %d\n", fw->type); 170562306a36Sopenharmony_ci fallthrough; 170662306a36Sopenharmony_ci case IWL_FW_MVM: 170762306a36Sopenharmony_ci op = &iwlwifi_opmode_table[MVM_OP_MODE]; 170862306a36Sopenharmony_ci break; 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci IWL_INFO(drv, "loaded firmware version %s op_mode %s\n", 171262306a36Sopenharmony_ci drv->fw.fw_version, op->name); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci /* add this device to the list of devices using this op_mode */ 171562306a36Sopenharmony_ci list_add_tail(&drv->list, &op->drv); 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci if (op->ops) { 171862306a36Sopenharmony_ci drv->op_mode = _iwl_op_mode_start(drv, op); 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci if (!drv->op_mode) { 172162306a36Sopenharmony_ci mutex_unlock(&iwlwifi_opmode_table_mtx); 172262306a36Sopenharmony_ci goto out_unbind; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci } else { 172562306a36Sopenharmony_ci load_module = true; 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci mutex_unlock(&iwlwifi_opmode_table_mtx); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci /* 173062306a36Sopenharmony_ci * Complete the firmware request last so that 173162306a36Sopenharmony_ci * a driver unbind (stop) doesn't run while we 173262306a36Sopenharmony_ci * are doing the start() above. 173362306a36Sopenharmony_ci */ 173462306a36Sopenharmony_ci complete(&drv->request_firmware_complete); 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci /* 173762306a36Sopenharmony_ci * Load the module last so we don't block anything 173862306a36Sopenharmony_ci * else from proceeding if the module fails to load 173962306a36Sopenharmony_ci * or hangs loading. 174062306a36Sopenharmony_ci */ 174162306a36Sopenharmony_ci if (load_module) 174262306a36Sopenharmony_ci request_module("%s", op->name); 174362306a36Sopenharmony_ci failure = false; 174462306a36Sopenharmony_ci goto free; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci try_again: 174762306a36Sopenharmony_ci /* try next, if any */ 174862306a36Sopenharmony_ci release_firmware(ucode_raw); 174962306a36Sopenharmony_ci if (iwl_request_firmware(drv, false)) 175062306a36Sopenharmony_ci goto out_unbind; 175162306a36Sopenharmony_ci goto free; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci out_free_fw: 175462306a36Sopenharmony_ci release_firmware(ucode_raw); 175562306a36Sopenharmony_ci out_unbind: 175662306a36Sopenharmony_ci complete(&drv->request_firmware_complete); 175762306a36Sopenharmony_ci device_release_driver(drv->trans->dev); 175862306a36Sopenharmony_ci /* drv has just been freed by the release */ 175962306a36Sopenharmony_ci failure = false; 176062306a36Sopenharmony_ci free: 176162306a36Sopenharmony_ci if (failure) 176262306a36Sopenharmony_ci iwl_dealloc_ucode(drv); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci if (pieces) { 176562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pieces->img); i++) 176662306a36Sopenharmony_ci kfree(pieces->img[i].sec); 176762306a36Sopenharmony_ci kfree(pieces->dbg_mem_tlv); 176862306a36Sopenharmony_ci kfree(pieces); 176962306a36Sopenharmony_ci } 177062306a36Sopenharmony_ci} 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_cistruct iwl_drv *iwl_drv_start(struct iwl_trans *trans) 177362306a36Sopenharmony_ci{ 177462306a36Sopenharmony_ci struct iwl_drv *drv; 177562306a36Sopenharmony_ci int ret; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci drv = kzalloc(sizeof(*drv), GFP_KERNEL); 177862306a36Sopenharmony_ci if (!drv) { 177962306a36Sopenharmony_ci ret = -ENOMEM; 178062306a36Sopenharmony_ci goto err; 178162306a36Sopenharmony_ci } 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci drv->trans = trans; 178462306a36Sopenharmony_ci drv->dev = trans->dev; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci init_completion(&drv->request_firmware_complete); 178762306a36Sopenharmony_ci INIT_LIST_HEAD(&drv->list); 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 179062306a36Sopenharmony_ci /* Create the device debugfs entries. */ 179162306a36Sopenharmony_ci drv->dbgfs_drv = debugfs_create_dir(dev_name(trans->dev), 179262306a36Sopenharmony_ci iwl_dbgfs_root); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci /* Create transport layer debugfs dir */ 179562306a36Sopenharmony_ci drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv); 179662306a36Sopenharmony_ci#endif 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci drv->trans->dbg.domains_bitmap = IWL_TRANS_FW_DBG_DOMAIN(drv->trans); 179962306a36Sopenharmony_ci if (iwlwifi_mod_params.enable_ini != ENABLE_INI) { 180062306a36Sopenharmony_ci /* We have a non-default value in the module parameter, 180162306a36Sopenharmony_ci * take its value 180262306a36Sopenharmony_ci */ 180362306a36Sopenharmony_ci drv->trans->dbg.domains_bitmap &= 0xffff; 180462306a36Sopenharmony_ci if (iwlwifi_mod_params.enable_ini != IWL_FW_INI_PRESET_DISABLE) { 180562306a36Sopenharmony_ci if (iwlwifi_mod_params.enable_ini > ENABLE_INI) { 180662306a36Sopenharmony_ci IWL_ERR(trans, 180762306a36Sopenharmony_ci "invalid enable_ini module parameter value: max = %d, using 0 instead\n", 180862306a36Sopenharmony_ci ENABLE_INI); 180962306a36Sopenharmony_ci iwlwifi_mod_params.enable_ini = 0; 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci drv->trans->dbg.domains_bitmap = 181262306a36Sopenharmony_ci BIT(IWL_FW_DBG_DOMAIN_POS + iwlwifi_mod_params.enable_ini); 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci ret = iwl_request_firmware(drv, true); 181762306a36Sopenharmony_ci if (ret) { 181862306a36Sopenharmony_ci IWL_ERR(trans, "Couldn't request the fw\n"); 181962306a36Sopenharmony_ci goto err_fw; 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci return drv; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_cierr_fw: 182562306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 182662306a36Sopenharmony_ci debugfs_remove_recursive(drv->dbgfs_drv); 182762306a36Sopenharmony_ci iwl_dbg_tlv_free(drv->trans); 182862306a36Sopenharmony_ci#endif 182962306a36Sopenharmony_ci kfree(drv); 183062306a36Sopenharmony_cierr: 183162306a36Sopenharmony_ci return ERR_PTR(ret); 183262306a36Sopenharmony_ci} 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_civoid iwl_drv_stop(struct iwl_drv *drv) 183562306a36Sopenharmony_ci{ 183662306a36Sopenharmony_ci wait_for_completion(&drv->request_firmware_complete); 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ci _iwl_op_mode_stop(drv); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci iwl_dealloc_ucode(drv); 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci mutex_lock(&iwlwifi_opmode_table_mtx); 184362306a36Sopenharmony_ci /* 184462306a36Sopenharmony_ci * List is empty (this item wasn't added) 184562306a36Sopenharmony_ci * when firmware loading failed -- in that 184662306a36Sopenharmony_ci * case we can't remove it from any list. 184762306a36Sopenharmony_ci */ 184862306a36Sopenharmony_ci if (!list_empty(&drv->list)) 184962306a36Sopenharmony_ci list_del(&drv->list); 185062306a36Sopenharmony_ci mutex_unlock(&iwlwifi_opmode_table_mtx); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 185362306a36Sopenharmony_ci drv->trans->ops->debugfs_cleanup(drv->trans); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci debugfs_remove_recursive(drv->dbgfs_drv); 185662306a36Sopenharmony_ci#endif 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci iwl_dbg_tlv_free(drv->trans); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci kfree(drv); 186162306a36Sopenharmony_ci} 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci/* shared module parameters */ 186462306a36Sopenharmony_cistruct iwl_mod_params iwlwifi_mod_params = { 186562306a36Sopenharmony_ci .fw_restart = true, 186662306a36Sopenharmony_ci .bt_coex_active = true, 186762306a36Sopenharmony_ci .power_level = IWL_POWER_INDEX_1, 186862306a36Sopenharmony_ci .uapsd_disable = IWL_DISABLE_UAPSD_BSS | IWL_DISABLE_UAPSD_P2P_CLIENT, 186962306a36Sopenharmony_ci .enable_ini = ENABLE_INI, 187062306a36Sopenharmony_ci /* the rest are 0 by default */ 187162306a36Sopenharmony_ci}; 187262306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwlwifi_mod_params); 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ciint iwl_opmode_register(const char *name, const struct iwl_op_mode_ops *ops) 187562306a36Sopenharmony_ci{ 187662306a36Sopenharmony_ci int i; 187762306a36Sopenharmony_ci struct iwl_drv *drv; 187862306a36Sopenharmony_ci struct iwlwifi_opmode_table *op; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci mutex_lock(&iwlwifi_opmode_table_mtx); 188162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { 188262306a36Sopenharmony_ci op = &iwlwifi_opmode_table[i]; 188362306a36Sopenharmony_ci if (strcmp(op->name, name)) 188462306a36Sopenharmony_ci continue; 188562306a36Sopenharmony_ci op->ops = ops; 188662306a36Sopenharmony_ci /* TODO: need to handle exceptional case */ 188762306a36Sopenharmony_ci list_for_each_entry(drv, &op->drv, list) 188862306a36Sopenharmony_ci drv->op_mode = _iwl_op_mode_start(drv, op); 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci mutex_unlock(&iwlwifi_opmode_table_mtx); 189162306a36Sopenharmony_ci return 0; 189262306a36Sopenharmony_ci } 189362306a36Sopenharmony_ci mutex_unlock(&iwlwifi_opmode_table_mtx); 189462306a36Sopenharmony_ci return -EIO; 189562306a36Sopenharmony_ci} 189662306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_opmode_register); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_civoid iwl_opmode_deregister(const char *name) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci int i; 190162306a36Sopenharmony_ci struct iwl_drv *drv; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci mutex_lock(&iwlwifi_opmode_table_mtx); 190462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { 190562306a36Sopenharmony_ci if (strcmp(iwlwifi_opmode_table[i].name, name)) 190662306a36Sopenharmony_ci continue; 190762306a36Sopenharmony_ci iwlwifi_opmode_table[i].ops = NULL; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci /* call the stop routine for all devices */ 191062306a36Sopenharmony_ci list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list) 191162306a36Sopenharmony_ci _iwl_op_mode_stop(drv); 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci mutex_unlock(&iwlwifi_opmode_table_mtx); 191462306a36Sopenharmony_ci return; 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci mutex_unlock(&iwlwifi_opmode_table_mtx); 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_opmode_deregister); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cistatic int __init iwl_drv_init(void) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci int i, err; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) 192562306a36Sopenharmony_ci INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci pr_info(DRV_DESCRIPTION "\n"); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 193062306a36Sopenharmony_ci /* Create the root of iwlwifi debugfs subsystem. */ 193162306a36Sopenharmony_ci iwl_dbgfs_root = debugfs_create_dir(DRV_NAME, NULL); 193262306a36Sopenharmony_ci#endif 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci err = iwl_pci_register_driver(); 193562306a36Sopenharmony_ci if (err) 193662306a36Sopenharmony_ci goto cleanup_debugfs; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci return 0; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_cicleanup_debugfs: 194162306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 194262306a36Sopenharmony_ci debugfs_remove_recursive(iwl_dbgfs_root); 194362306a36Sopenharmony_ci#endif 194462306a36Sopenharmony_ci return err; 194562306a36Sopenharmony_ci} 194662306a36Sopenharmony_cimodule_init(iwl_drv_init); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_cistatic void __exit iwl_drv_exit(void) 194962306a36Sopenharmony_ci{ 195062306a36Sopenharmony_ci iwl_pci_unregister_driver(); 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUGFS 195362306a36Sopenharmony_ci debugfs_remove_recursive(iwl_dbgfs_root); 195462306a36Sopenharmony_ci#endif 195562306a36Sopenharmony_ci} 195662306a36Sopenharmony_cimodule_exit(iwl_drv_exit); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci#ifdef CONFIG_IWLWIFI_DEBUG 195962306a36Sopenharmony_cimodule_param_named(debug, iwlwifi_mod_params.debug_level, uint, 0644); 196062306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "debug output mask"); 196162306a36Sopenharmony_ci#endif 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_cimodule_param_named(swcrypto, iwlwifi_mod_params.swcrypto, int, 0444); 196462306a36Sopenharmony_ciMODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); 196562306a36Sopenharmony_cimodule_param_named(11n_disable, iwlwifi_mod_params.disable_11n, uint, 0444); 196662306a36Sopenharmony_ciMODULE_PARM_DESC(11n_disable, 196762306a36Sopenharmony_ci "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX"); 196862306a36Sopenharmony_cimodule_param_named(amsdu_size, iwlwifi_mod_params.amsdu_size, int, 0444); 196962306a36Sopenharmony_ciMODULE_PARM_DESC(amsdu_size, 197062306a36Sopenharmony_ci "amsdu size 0: 12K for multi Rx queue devices, 2K for AX210 devices, " 197162306a36Sopenharmony_ci "4K for other devices 1:4K 2:8K 3:12K (16K buffers) 4: 2K (default 0)"); 197262306a36Sopenharmony_cimodule_param_named(fw_restart, iwlwifi_mod_params.fw_restart, bool, 0444); 197362306a36Sopenharmony_ciMODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)"); 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_cimodule_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, 0444); 197662306a36Sopenharmony_ciMODULE_PARM_DESC(nvm_file, "NVM file name"); 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_cimodule_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, uint, 0644); 197962306a36Sopenharmony_ciMODULE_PARM_DESC(uapsd_disable, 198062306a36Sopenharmony_ci "disable U-APSD functionality bitmap 1: BSS 2: P2P Client (default: 3)"); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_cimodule_param_named(enable_ini, iwlwifi_mod_params.enable_ini, uint, 0444); 198362306a36Sopenharmony_ciMODULE_PARM_DESC(enable_ini, 198462306a36Sopenharmony_ci "0:disable, 1-15:FW_DBG_PRESET Values, 16:enabled without preset value defined," 198562306a36Sopenharmony_ci "Debug INI TLV FW debug infrastructure (default: 16)"); 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci/* 198862306a36Sopenharmony_ci * set bt_coex_active to true, uCode will do kill/defer 198962306a36Sopenharmony_ci * every time the priority line is asserted (BT is sending signals on the 199062306a36Sopenharmony_ci * priority line in the PCIx). 199162306a36Sopenharmony_ci * set bt_coex_active to false, uCode will ignore the BT activity and 199262306a36Sopenharmony_ci * perform the normal operation 199362306a36Sopenharmony_ci * 199462306a36Sopenharmony_ci * User might experience transmit issue on some platform due to WiFi/BT 199562306a36Sopenharmony_ci * co-exist problem. The possible behaviors are: 199662306a36Sopenharmony_ci * Able to scan and finding all the available AP 199762306a36Sopenharmony_ci * Not able to associate with any AP 199862306a36Sopenharmony_ci * On those platforms, WiFi communication can be restored by set 199962306a36Sopenharmony_ci * "bt_coex_active" module parameter to "false" 200062306a36Sopenharmony_ci * 200162306a36Sopenharmony_ci * default: bt_coex_active = true (BT_COEX_ENABLE) 200262306a36Sopenharmony_ci */ 200362306a36Sopenharmony_cimodule_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active, 200462306a36Sopenharmony_ci bool, 0444); 200562306a36Sopenharmony_ciMODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)"); 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_cimodule_param_named(led_mode, iwlwifi_mod_params.led_mode, int, 0444); 200862306a36Sopenharmony_ciMODULE_PARM_DESC(led_mode, "0=system default, " 200962306a36Sopenharmony_ci "1=On(RF On)/Off(RF Off), 2=blinking, 3=Off (default: 0)"); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_cimodule_param_named(power_save, iwlwifi_mod_params.power_save, bool, 0444); 201262306a36Sopenharmony_ciMODULE_PARM_DESC(power_save, 201362306a36Sopenharmony_ci "enable WiFi power management (default: disable)"); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_cimodule_param_named(power_level, iwlwifi_mod_params.power_level, int, 0444); 201662306a36Sopenharmony_ciMODULE_PARM_DESC(power_level, 201762306a36Sopenharmony_ci "default power save level (range from 1 - 5, default: 1)"); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_cimodule_param_named(disable_11ac, iwlwifi_mod_params.disable_11ac, bool, 0444); 202062306a36Sopenharmony_ciMODULE_PARM_DESC(disable_11ac, "Disable VHT capabilities (default: false)"); 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_cimodule_param_named(remove_when_gone, 202362306a36Sopenharmony_ci iwlwifi_mod_params.remove_when_gone, bool, 202462306a36Sopenharmony_ci 0444); 202562306a36Sopenharmony_ciMODULE_PARM_DESC(remove_when_gone, 202662306a36Sopenharmony_ci "Remove dev from PCIe bus if it is deemed inaccessible (default: false)"); 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_cimodule_param_named(disable_11ax, iwlwifi_mod_params.disable_11ax, bool, 202962306a36Sopenharmony_ci S_IRUGO); 203062306a36Sopenharmony_ciMODULE_PARM_DESC(disable_11ax, "Disable HE capabilities (default: false)"); 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_cimodule_param_named(disable_11be, iwlwifi_mod_params.disable_11be, bool, 0444); 203362306a36Sopenharmony_ciMODULE_PARM_DESC(disable_11be, "Disable EHT capabilities (default: false)"); 2034