162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2018-2024 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/firmware.h> 662306a36Sopenharmony_ci#include "iwl-drv.h" 762306a36Sopenharmony_ci#include "iwl-trans.h" 862306a36Sopenharmony_ci#include "iwl-dbg-tlv.h" 962306a36Sopenharmony_ci#include "fw/dbg.h" 1062306a36Sopenharmony_ci#include "fw/runtime.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/** 1362306a36Sopenharmony_ci * enum iwl_dbg_tlv_type - debug TLV types 1462306a36Sopenharmony_ci * @IWL_DBG_TLV_TYPE_DEBUG_INFO: debug info TLV 1562306a36Sopenharmony_ci * @IWL_DBG_TLV_TYPE_BUF_ALLOC: buffer allocation TLV 1662306a36Sopenharmony_ci * @IWL_DBG_TLV_TYPE_HCMD: host command TLV 1762306a36Sopenharmony_ci * @IWL_DBG_TLV_TYPE_REGION: region TLV 1862306a36Sopenharmony_ci * @IWL_DBG_TLV_TYPE_TRIGGER: trigger TLV 1962306a36Sopenharmony_ci * @IWL_DBG_TLV_TYPE_CONF_SET: conf set TLV 2062306a36Sopenharmony_ci * @IWL_DBG_TLV_TYPE_NUM: number of debug TLVs 2162306a36Sopenharmony_ci */ 2262306a36Sopenharmony_cienum iwl_dbg_tlv_type { 2362306a36Sopenharmony_ci IWL_DBG_TLV_TYPE_DEBUG_INFO = 2462306a36Sopenharmony_ci IWL_UCODE_TLV_TYPE_DEBUG_INFO - IWL_UCODE_TLV_DEBUG_BASE, 2562306a36Sopenharmony_ci IWL_DBG_TLV_TYPE_BUF_ALLOC, 2662306a36Sopenharmony_ci IWL_DBG_TLV_TYPE_HCMD, 2762306a36Sopenharmony_ci IWL_DBG_TLV_TYPE_REGION, 2862306a36Sopenharmony_ci IWL_DBG_TLV_TYPE_TRIGGER, 2962306a36Sopenharmony_ci IWL_DBG_TLV_TYPE_CONF_SET, 3062306a36Sopenharmony_ci IWL_DBG_TLV_TYPE_NUM, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/** 3462306a36Sopenharmony_ci * struct iwl_dbg_tlv_ver_data - debug TLV version struct 3562306a36Sopenharmony_ci * @min_ver: min version supported 3662306a36Sopenharmony_ci * @max_ver: max version supported 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistruct iwl_dbg_tlv_ver_data { 3962306a36Sopenharmony_ci int min_ver; 4062306a36Sopenharmony_ci int max_ver; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/** 4462306a36Sopenharmony_ci * struct iwl_dbg_tlv_timer_node - timer node struct 4562306a36Sopenharmony_ci * @list: list of &struct iwl_dbg_tlv_timer_node 4662306a36Sopenharmony_ci * @timer: timer 4762306a36Sopenharmony_ci * @fwrt: &struct iwl_fw_runtime 4862306a36Sopenharmony_ci * @tlv: TLV attach to the timer node 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistruct iwl_dbg_tlv_timer_node { 5162306a36Sopenharmony_ci struct list_head list; 5262306a36Sopenharmony_ci struct timer_list timer; 5362306a36Sopenharmony_ci struct iwl_fw_runtime *fwrt; 5462306a36Sopenharmony_ci struct iwl_ucode_tlv *tlv; 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct iwl_dbg_tlv_ver_data 5862306a36Sopenharmony_cidbg_ver_table[IWL_DBG_TLV_TYPE_NUM] = { 5962306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_DEBUG_INFO] = {.min_ver = 1, .max_ver = 1,}, 6062306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_BUF_ALLOC] = {.min_ver = 1, .max_ver = 1,}, 6162306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_HCMD] = {.min_ver = 1, .max_ver = 1,}, 6262306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_REGION] = {.min_ver = 1, .max_ver = 3,}, 6362306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_TRIGGER] = {.min_ver = 1, .max_ver = 1,}, 6462306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_CONF_SET] = {.min_ver = 1, .max_ver = 1,}, 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic int iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv, 6862306a36Sopenharmony_ci struct list_head *list) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci u32 len = le32_to_cpu(tlv->length); 7162306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci node = kzalloc(sizeof(*node) + len, GFP_KERNEL); 7462306a36Sopenharmony_ci if (!node) 7562306a36Sopenharmony_ci return -ENOMEM; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci memcpy(&node->tlv, tlv, sizeof(node->tlv)); 7862306a36Sopenharmony_ci memcpy(node->tlv.data, tlv->data, len); 7962306a36Sopenharmony_ci list_add_tail(&node->list, list); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic bool iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv *tlv) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0]; 8762306a36Sopenharmony_ci u32 type = le32_to_cpu(tlv->type); 8862306a36Sopenharmony_ci u32 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE; 8962306a36Sopenharmony_ci u32 ver = le32_to_cpu(hdr->version); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (ver < dbg_ver_table[tlv_idx].min_ver || 9262306a36Sopenharmony_ci ver > dbg_ver_table[tlv_idx].max_ver) 9362306a36Sopenharmony_ci return false; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return true; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans, 9962306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci const struct iwl_fw_ini_debug_info_tlv *debug_info = (const void *)tlv->data; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (le32_to_cpu(tlv->length) != sizeof(*debug_info)) 10462306a36Sopenharmony_ci return -EINVAL; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* we use this as a string, ensure input was NUL terminated */ 10762306a36Sopenharmony_ci if (strnlen(debug_info->debug_cfg_name, 10862306a36Sopenharmony_ci sizeof(debug_info->debug_cfg_name)) == 10962306a36Sopenharmony_ci sizeof(debug_info->debug_cfg_name)) 11062306a36Sopenharmony_ci return -EINVAL; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n", 11362306a36Sopenharmony_ci debug_info->debug_cfg_name); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans, 11962306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci const struct iwl_fw_ini_allocation_tlv *alloc = (const void *)tlv->data; 12262306a36Sopenharmony_ci u32 buf_location; 12362306a36Sopenharmony_ci u32 alloc_id; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (le32_to_cpu(tlv->length) != sizeof(*alloc)) 12662306a36Sopenharmony_ci return -EINVAL; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci buf_location = le32_to_cpu(alloc->buf_location); 12962306a36Sopenharmony_ci alloc_id = le32_to_cpu(alloc->alloc_id); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (buf_location == IWL_FW_INI_LOCATION_INVALID || 13262306a36Sopenharmony_ci buf_location >= IWL_FW_INI_LOCATION_NUM) 13362306a36Sopenharmony_ci goto err; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (alloc_id == IWL_FW_INI_ALLOCATION_INVALID || 13662306a36Sopenharmony_ci alloc_id >= IWL_FW_INI_ALLOCATION_NUM) 13762306a36Sopenharmony_ci goto err; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (buf_location == IWL_FW_INI_LOCATION_NPK_PATH && 14062306a36Sopenharmony_ci alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1) 14162306a36Sopenharmony_ci goto err; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH && 14462306a36Sopenharmony_ci alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1) 14562306a36Sopenharmony_ci goto err; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (buf_location == IWL_FW_INI_LOCATION_DRAM_PATH && 14862306a36Sopenharmony_ci alloc->req_size == 0) { 14962306a36Sopenharmony_ci IWL_ERR(trans, "WRT: Invalid DRAM buffer allocation requested size (0)\n"); 15062306a36Sopenharmony_ci return -EINVAL; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci trans->dbg.fw_mon_cfg[alloc_id] = *alloc; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return 0; 15662306a36Sopenharmony_cierr: 15762306a36Sopenharmony_ci IWL_ERR(trans, 15862306a36Sopenharmony_ci "WRT: Invalid allocation id %u and/or location id %u for allocation TLV\n", 15962306a36Sopenharmony_ci alloc_id, buf_location); 16062306a36Sopenharmony_ci return -EINVAL; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans *trans, 16462306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci const struct iwl_fw_ini_hcmd_tlv *hcmd = (const void *)tlv->data; 16762306a36Sopenharmony_ci u32 tp = le32_to_cpu(hcmd->time_point); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (le32_to_cpu(tlv->length) <= sizeof(*hcmd)) 17062306a36Sopenharmony_ci return -EINVAL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* Host commands can not be sent in early time point since the FW 17362306a36Sopenharmony_ci * is not ready 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci if (tp == IWL_FW_INI_TIME_POINT_INVALID || 17662306a36Sopenharmony_ci tp >= IWL_FW_INI_TIME_POINT_NUM || 17762306a36Sopenharmony_ci tp == IWL_FW_INI_TIME_POINT_EARLY) { 17862306a36Sopenharmony_ci IWL_ERR(trans, 17962306a36Sopenharmony_ci "WRT: Invalid time point %u for host command TLV\n", 18062306a36Sopenharmony_ci tp); 18162306a36Sopenharmony_ci return -EINVAL; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans, 18862306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci const struct iwl_fw_ini_region_tlv *reg = (const void *)tlv->data; 19162306a36Sopenharmony_ci struct iwl_ucode_tlv **active_reg; 19262306a36Sopenharmony_ci u32 id = le32_to_cpu(reg->id); 19362306a36Sopenharmony_ci u8 type = reg->type; 19462306a36Sopenharmony_ci u32 tlv_len = sizeof(*tlv) + le32_to_cpu(tlv->length); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci /* 19762306a36Sopenharmony_ci * The higher part of the ID from version 2 is debug policy. 19862306a36Sopenharmony_ci * The id will be only lsb 16 bits, so mask it out. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_ci if (le32_to_cpu(reg->hdr.version) >= 2) 20162306a36Sopenharmony_ci id &= IWL_FW_INI_REGION_ID_MASK; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (le32_to_cpu(tlv->length) < sizeof(*reg)) 20462306a36Sopenharmony_ci return -EINVAL; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* for safe use of a string from FW, limit it to IWL_FW_INI_MAX_NAME */ 20762306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "WRT: parsing region: %.*s\n", 20862306a36Sopenharmony_ci IWL_FW_INI_MAX_NAME, reg->name); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (id >= IWL_FW_INI_MAX_REGION_ID) { 21162306a36Sopenharmony_ci IWL_ERR(trans, "WRT: Invalid region id %u\n", id); 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (type <= IWL_FW_INI_REGION_INVALID || 21662306a36Sopenharmony_ci type >= IWL_FW_INI_REGION_NUM) { 21762306a36Sopenharmony_ci IWL_ERR(trans, "WRT: Invalid region type %u\n", type); 21862306a36Sopenharmony_ci return -EINVAL; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (type == IWL_FW_INI_REGION_PCI_IOSF_CONFIG && 22262306a36Sopenharmony_ci !trans->ops->read_config32) { 22362306a36Sopenharmony_ci IWL_ERR(trans, "WRT: Unsupported region type %u\n", type); 22462306a36Sopenharmony_ci return -EOPNOTSUPP; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (type == IWL_FW_INI_REGION_INTERNAL_BUFFER) { 22862306a36Sopenharmony_ci trans->dbg.imr_data.sram_addr = 22962306a36Sopenharmony_ci le32_to_cpu(reg->internal_buffer.base_addr); 23062306a36Sopenharmony_ci trans->dbg.imr_data.sram_size = 23162306a36Sopenharmony_ci le32_to_cpu(reg->internal_buffer.size); 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci active_reg = &trans->dbg.active_regions[id]; 23662306a36Sopenharmony_ci if (*active_reg) { 23762306a36Sopenharmony_ci IWL_WARN(trans, "WRT: Overriding region id %u\n", id); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci kfree(*active_reg); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci *active_reg = kmemdup(tlv, tlv_len, GFP_KERNEL); 24362306a36Sopenharmony_ci if (!*active_reg) 24462306a36Sopenharmony_ci return -ENOMEM; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "WRT: Enabling region id %u type %u\n", id, type); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans, 25262306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data; 25562306a36Sopenharmony_ci struct iwl_fw_ini_trigger_tlv *dup_trig; 25662306a36Sopenharmony_ci u32 tp = le32_to_cpu(trig->time_point); 25762306a36Sopenharmony_ci u32 rf = le32_to_cpu(trig->reset_fw); 25862306a36Sopenharmony_ci struct iwl_ucode_tlv *dup = NULL; 25962306a36Sopenharmony_ci int ret; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (le32_to_cpu(tlv->length) < sizeof(*trig)) 26262306a36Sopenharmony_ci return -EINVAL; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (tp <= IWL_FW_INI_TIME_POINT_INVALID || 26562306a36Sopenharmony_ci tp >= IWL_FW_INI_TIME_POINT_NUM) { 26662306a36Sopenharmony_ci IWL_ERR(trans, 26762306a36Sopenharmony_ci "WRT: Invalid time point %u for trigger TLV\n", 26862306a36Sopenharmony_ci tp); 26962306a36Sopenharmony_ci return -EINVAL; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci IWL_DEBUG_FW(trans, 27362306a36Sopenharmony_ci "WRT: time point %u for trigger TLV with reset_fw %u\n", 27462306a36Sopenharmony_ci tp, rf); 27562306a36Sopenharmony_ci trans->dbg.last_tp_resetfw = 0xFF; 27662306a36Sopenharmony_ci if (!le32_to_cpu(trig->occurrences)) { 27762306a36Sopenharmony_ci dup = kmemdup(tlv, sizeof(*tlv) + le32_to_cpu(tlv->length), 27862306a36Sopenharmony_ci GFP_KERNEL); 27962306a36Sopenharmony_ci if (!dup) 28062306a36Sopenharmony_ci return -ENOMEM; 28162306a36Sopenharmony_ci dup_trig = (void *)dup->data; 28262306a36Sopenharmony_ci dup_trig->occurrences = cpu_to_le32(-1); 28362306a36Sopenharmony_ci tlv = dup; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ret = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list); 28762306a36Sopenharmony_ci kfree(dup); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int iwl_dbg_tlv_config_set(struct iwl_trans *trans, 29362306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci const struct iwl_fw_ini_conf_set_tlv *conf_set = (const void *)tlv->data; 29662306a36Sopenharmony_ci u32 tp = le32_to_cpu(conf_set->time_point); 29762306a36Sopenharmony_ci u32 type = le32_to_cpu(conf_set->set_type); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (tp <= IWL_FW_INI_TIME_POINT_INVALID || 30062306a36Sopenharmony_ci tp >= IWL_FW_INI_TIME_POINT_NUM) { 30162306a36Sopenharmony_ci IWL_DEBUG_FW(trans, 30262306a36Sopenharmony_ci "WRT: Invalid time point %u for config set TLV\n", tp); 30362306a36Sopenharmony_ci return -EINVAL; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (type <= IWL_FW_INI_CONFIG_SET_TYPE_INVALID || 30762306a36Sopenharmony_ci type >= IWL_FW_INI_CONFIG_SET_TYPE_MAX_NUM) { 30862306a36Sopenharmony_ci IWL_DEBUG_FW(trans, 30962306a36Sopenharmony_ci "WRT: Invalid config set type %u for config set TLV\n", type); 31062306a36Sopenharmony_ci return -EINVAL; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].config_list); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic int (*dbg_tlv_alloc[])(struct iwl_trans *trans, 31762306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv) = { 31862306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_DEBUG_INFO] = iwl_dbg_tlv_alloc_debug_info, 31962306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_BUF_ALLOC] = iwl_dbg_tlv_alloc_buf_alloc, 32062306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_HCMD] = iwl_dbg_tlv_alloc_hcmd, 32162306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_REGION] = iwl_dbg_tlv_alloc_region, 32262306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_TRIGGER] = iwl_dbg_tlv_alloc_trigger, 32362306a36Sopenharmony_ci [IWL_DBG_TLV_TYPE_CONF_SET] = iwl_dbg_tlv_config_set, 32462306a36Sopenharmony_ci}; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv, 32762306a36Sopenharmony_ci bool ext) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci enum iwl_ini_cfg_state *cfg_state = ext ? 33062306a36Sopenharmony_ci &trans->dbg.external_ini_cfg : &trans->dbg.internal_ini_cfg; 33162306a36Sopenharmony_ci const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0]; 33262306a36Sopenharmony_ci u32 type; 33362306a36Sopenharmony_ci u32 tlv_idx; 33462306a36Sopenharmony_ci u32 domain; 33562306a36Sopenharmony_ci int ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (le32_to_cpu(tlv->length) < sizeof(*hdr)) 33862306a36Sopenharmony_ci return; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci type = le32_to_cpu(tlv->type); 34162306a36Sopenharmony_ci tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE; 34262306a36Sopenharmony_ci domain = le32_to_cpu(hdr->domain); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (domain != IWL_FW_INI_DOMAIN_ALWAYS_ON && 34562306a36Sopenharmony_ci !(domain & trans->dbg.domains_bitmap)) { 34662306a36Sopenharmony_ci IWL_DEBUG_FW(trans, 34762306a36Sopenharmony_ci "WRT: Skipping TLV with disabled domain 0x%0x (0x%0x)\n", 34862306a36Sopenharmony_ci domain, trans->dbg.domains_bitmap); 34962306a36Sopenharmony_ci return; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (tlv_idx >= ARRAY_SIZE(dbg_tlv_alloc) || !dbg_tlv_alloc[tlv_idx]) { 35362306a36Sopenharmony_ci IWL_ERR(trans, "WRT: Unsupported TLV type 0x%x\n", type); 35462306a36Sopenharmony_ci goto out_err; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!iwl_dbg_tlv_ver_support(tlv)) { 35862306a36Sopenharmony_ci IWL_ERR(trans, "WRT: Unsupported TLV 0x%x version %u\n", type, 35962306a36Sopenharmony_ci le32_to_cpu(hdr->version)); 36062306a36Sopenharmony_ci goto out_err; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = dbg_tlv_alloc[tlv_idx](trans, tlv); 36462306a36Sopenharmony_ci if (ret) { 36562306a36Sopenharmony_ci IWL_WARN(trans, 36662306a36Sopenharmony_ci "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n", 36762306a36Sopenharmony_ci type, ret, ext); 36862306a36Sopenharmony_ci goto out_err; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (*cfg_state == IWL_INI_CFG_STATE_NOT_LOADED) 37262306a36Sopenharmony_ci *cfg_state = IWL_INI_CFG_STATE_LOADED; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciout_err: 37762306a36Sopenharmony_ci *cfg_state = IWL_INI_CFG_STATE_CORRUPTED; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_civoid iwl_dbg_tlv_del_timers(struct iwl_trans *trans) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct list_head *timer_list = &trans->dbg.periodic_trig_list; 38362306a36Sopenharmony_ci struct iwl_dbg_tlv_timer_node *node, *tmp; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci list_for_each_entry_safe(node, tmp, timer_list, list) { 38662306a36Sopenharmony_ci timer_shutdown_sync(&node->timer); 38762306a36Sopenharmony_ci list_del(&node->list); 38862306a36Sopenharmony_ci kfree(node); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_dbg_tlv_del_timers); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic void iwl_dbg_tlv_fragments_free(struct iwl_trans *trans, 39462306a36Sopenharmony_ci enum iwl_fw_ini_allocation_id alloc_id) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct iwl_fw_mon *fw_mon; 39762306a36Sopenharmony_ci int i; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (alloc_id <= IWL_FW_INI_ALLOCATION_INVALID || 40062306a36Sopenharmony_ci alloc_id >= IWL_FW_INI_ALLOCATION_NUM) 40162306a36Sopenharmony_ci return; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci fw_mon = &trans->dbg.fw_mon_ini[alloc_id]; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci for (i = 0; i < fw_mon->num_frags; i++) { 40662306a36Sopenharmony_ci struct iwl_dram_data *frag = &fw_mon->frags[i]; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci dma_free_coherent(trans->dev, frag->size, frag->block, 40962306a36Sopenharmony_ci frag->physical); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci frag->physical = 0; 41262306a36Sopenharmony_ci frag->block = NULL; 41362306a36Sopenharmony_ci frag->size = 0; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci kfree(fw_mon->frags); 41762306a36Sopenharmony_ci fw_mon->frags = NULL; 41862306a36Sopenharmony_ci fw_mon->num_frags = 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_civoid iwl_dbg_tlv_free(struct iwl_trans *trans) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct iwl_dbg_tlv_node *tlv_node, *tlv_node_tmp; 42462306a36Sopenharmony_ci int i; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci iwl_dbg_tlv_del_timers(trans); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) { 42962306a36Sopenharmony_ci struct iwl_ucode_tlv **active_reg = 43062306a36Sopenharmony_ci &trans->dbg.active_regions[i]; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci kfree(*active_reg); 43362306a36Sopenharmony_ci *active_reg = NULL; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci list_for_each_entry_safe(tlv_node, tlv_node_tmp, 43762306a36Sopenharmony_ci &trans->dbg.debug_info_tlv_list, list) { 43862306a36Sopenharmony_ci list_del(&tlv_node->list); 43962306a36Sopenharmony_ci kfree(tlv_node); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) { 44362306a36Sopenharmony_ci struct iwl_dbg_tlv_time_point_data *tp = 44462306a36Sopenharmony_ci &trans->dbg.time_point[i]; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->trig_list, 44762306a36Sopenharmony_ci list) { 44862306a36Sopenharmony_ci list_del(&tlv_node->list); 44962306a36Sopenharmony_ci kfree(tlv_node); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->hcmd_list, 45362306a36Sopenharmony_ci list) { 45462306a36Sopenharmony_ci list_del(&tlv_node->list); 45562306a36Sopenharmony_ci kfree(tlv_node); 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci list_for_each_entry_safe(tlv_node, tlv_node_tmp, 45962306a36Sopenharmony_ci &tp->active_trig_list, list) { 46062306a36Sopenharmony_ci list_del(&tlv_node->list); 46162306a36Sopenharmony_ci kfree(tlv_node); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci list_for_each_entry_safe(tlv_node, tlv_node_tmp, 46562306a36Sopenharmony_ci &tp->config_list, list) { 46662306a36Sopenharmony_ci list_del(&tlv_node->list); 46762306a36Sopenharmony_ci kfree(tlv_node); 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(trans->dbg.fw_mon_ini); i++) 47362306a36Sopenharmony_ci iwl_dbg_tlv_fragments_free(trans, i); 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data, 47762306a36Sopenharmony_ci size_t len) 47862306a36Sopenharmony_ci{ 47962306a36Sopenharmony_ci const struct iwl_ucode_tlv *tlv; 48062306a36Sopenharmony_ci u32 tlv_len; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci while (len >= sizeof(*tlv)) { 48362306a36Sopenharmony_ci len -= sizeof(*tlv); 48462306a36Sopenharmony_ci tlv = (const void *)data; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci tlv_len = le32_to_cpu(tlv->length); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (len < tlv_len) { 48962306a36Sopenharmony_ci IWL_ERR(trans, "invalid TLV len: %zd/%u\n", 49062306a36Sopenharmony_ci len, tlv_len); 49162306a36Sopenharmony_ci return -EINVAL; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci len -= ALIGN(tlv_len, 4); 49462306a36Sopenharmony_ci data += sizeof(*tlv) + ALIGN(tlv_len, 4); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci iwl_dbg_tlv_alloc(trans, tlv, true); 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_civoid iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci const struct firmware *fw; 50562306a36Sopenharmony_ci const char *yoyo_bin = "iwl-debug-yoyo.bin"; 50662306a36Sopenharmony_ci int res; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!iwlwifi_mod_params.enable_ini || 50962306a36Sopenharmony_ci trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000) 51062306a36Sopenharmony_ci return; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci res = firmware_request_nowarn(&fw, yoyo_bin, dev); 51362306a36Sopenharmony_ci IWL_DEBUG_FW(trans, "%s %s\n", res ? "didn't load" : "loaded", yoyo_bin); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (res) 51662306a36Sopenharmony_ci return; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci iwl_dbg_tlv_parse_bin(trans, fw->data, fw->size); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci release_firmware(fw); 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_civoid iwl_dbg_tlv_init(struct iwl_trans *trans) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci int i; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci INIT_LIST_HEAD(&trans->dbg.debug_info_tlv_list); 52862306a36Sopenharmony_ci INIT_LIST_HEAD(&trans->dbg.periodic_trig_list); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) { 53162306a36Sopenharmony_ci struct iwl_dbg_tlv_time_point_data *tp = 53262306a36Sopenharmony_ci &trans->dbg.time_point[i]; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci INIT_LIST_HEAD(&tp->trig_list); 53562306a36Sopenharmony_ci INIT_LIST_HEAD(&tp->hcmd_list); 53662306a36Sopenharmony_ci INIT_LIST_HEAD(&tp->active_trig_list); 53762306a36Sopenharmony_ci INIT_LIST_HEAD(&tp->config_list); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic int iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime *fwrt, 54262306a36Sopenharmony_ci struct iwl_dram_data *frag, u32 pages) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci void *block = NULL; 54562306a36Sopenharmony_ci dma_addr_t physical; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (!frag || frag->size || !pages) 54862306a36Sopenharmony_ci return -EIO; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * We try to allocate as many pages as we can, starting with 55262306a36Sopenharmony_ci * the requested amount and going down until we can allocate 55362306a36Sopenharmony_ci * something. Because of DIV_ROUND_UP(), pages will never go 55462306a36Sopenharmony_ci * down to 0 and stop the loop, so stop when pages reaches 1, 55562306a36Sopenharmony_ci * which is too small anyway. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci while (pages > 1) { 55862306a36Sopenharmony_ci block = dma_alloc_coherent(fwrt->dev, pages * PAGE_SIZE, 55962306a36Sopenharmony_ci &physical, 56062306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 56162306a36Sopenharmony_ci if (block) 56262306a36Sopenharmony_ci break; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci IWL_WARN(fwrt, "WRT: Failed to allocate fragment size %lu\n", 56562306a36Sopenharmony_ci pages * PAGE_SIZE); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci pages = DIV_ROUND_UP(pages, 2); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci if (!block) 57162306a36Sopenharmony_ci return -ENOMEM; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci frag->physical = physical; 57462306a36Sopenharmony_ci frag->block = block; 57562306a36Sopenharmony_ci frag->size = pages * PAGE_SIZE; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci return pages; 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt, 58162306a36Sopenharmony_ci enum iwl_fw_ini_allocation_id alloc_id) 58262306a36Sopenharmony_ci{ 58362306a36Sopenharmony_ci struct iwl_fw_mon *fw_mon; 58462306a36Sopenharmony_ci struct iwl_fw_ini_allocation_tlv *fw_mon_cfg; 58562306a36Sopenharmony_ci u32 num_frags, remain_pages, frag_pages; 58662306a36Sopenharmony_ci int i; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID || 58962306a36Sopenharmony_ci alloc_id >= IWL_FW_INI_ALLOCATION_NUM) 59062306a36Sopenharmony_ci return -EIO; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci fw_mon_cfg = &fwrt->trans->dbg.fw_mon_cfg[alloc_id]; 59362306a36Sopenharmony_ci fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id]; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (fw_mon->num_frags) { 59662306a36Sopenharmony_ci for (i = 0; i < fw_mon->num_frags; i++) 59762306a36Sopenharmony_ci memset(fw_mon->frags[i].block, 0, 59862306a36Sopenharmony_ci fw_mon->frags[i].size); 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (fw_mon_cfg->buf_location != 60362306a36Sopenharmony_ci cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH)) 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num); 60762306a36Sopenharmony_ci if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { 60862306a36Sopenharmony_ci if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1) 60962306a36Sopenharmony_ci return -EIO; 61062306a36Sopenharmony_ci num_frags = 1; 61162306a36Sopenharmony_ci } else if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ && 61262306a36Sopenharmony_ci alloc_id > IWL_FW_INI_ALLOCATION_ID_DBGC3) { 61362306a36Sopenharmony_ci return -EIO; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size), 61762306a36Sopenharmony_ci PAGE_SIZE); 61862306a36Sopenharmony_ci num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS); 61962306a36Sopenharmony_ci num_frags = min_t(u32, num_frags, remain_pages); 62062306a36Sopenharmony_ci frag_pages = DIV_ROUND_UP(remain_pages, num_frags); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci fw_mon->frags = kcalloc(num_frags, sizeof(*fw_mon->frags), GFP_KERNEL); 62362306a36Sopenharmony_ci if (!fw_mon->frags) 62462306a36Sopenharmony_ci return -ENOMEM; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci for (i = 0; i < num_frags; i++) { 62762306a36Sopenharmony_ci int pages = min_t(u32, frag_pages, remain_pages); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 63062306a36Sopenharmony_ci "WRT: Allocating DRAM buffer (alloc_id=%u, fragment=%u, size=0x%lx)\n", 63162306a36Sopenharmony_ci alloc_id, i, pages * PAGE_SIZE); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci pages = iwl_dbg_tlv_alloc_fragment(fwrt, &fw_mon->frags[i], 63462306a36Sopenharmony_ci pages); 63562306a36Sopenharmony_ci if (pages < 0) { 63662306a36Sopenharmony_ci u32 alloc_size = le32_to_cpu(fw_mon_cfg->req_size) - 63762306a36Sopenharmony_ci (remain_pages * PAGE_SIZE); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (alloc_size < le32_to_cpu(fw_mon_cfg->min_size)) { 64062306a36Sopenharmony_ci iwl_dbg_tlv_fragments_free(fwrt->trans, 64162306a36Sopenharmony_ci alloc_id); 64262306a36Sopenharmony_ci return pages; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci break; 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci remain_pages -= pages; 64862306a36Sopenharmony_ci fw_mon->num_frags++; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic int iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime *fwrt, 65562306a36Sopenharmony_ci enum iwl_fw_ini_allocation_id alloc_id) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct iwl_fw_mon *fw_mon; 65862306a36Sopenharmony_ci u32 remain_frags, num_commands; 65962306a36Sopenharmony_ci int i, fw_mon_idx = 0; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (!fw_has_capa(&fwrt->fw->ucode_capa, 66262306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP)) 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID || 66662306a36Sopenharmony_ci alloc_id >= IWL_FW_INI_ALLOCATION_NUM) 66762306a36Sopenharmony_ci return -EIO; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) != 67062306a36Sopenharmony_ci IWL_FW_INI_LOCATION_DRAM_PATH) 67162306a36Sopenharmony_ci return 0; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id]; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* the first fragment of DBGC1 is given to the FW via register 67662306a36Sopenharmony_ci * or context info 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1) 67962306a36Sopenharmony_ci fw_mon_idx++; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci remain_frags = fw_mon->num_frags - fw_mon_idx; 68262306a36Sopenharmony_ci if (!remain_frags) 68362306a36Sopenharmony_ci return 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci num_commands = DIV_ROUND_UP(remain_frags, BUF_ALLOC_MAX_NUM_FRAGS); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: Applying DRAM destination (alloc_id=%u)\n", 68862306a36Sopenharmony_ci alloc_id); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci for (i = 0; i < num_commands; i++) { 69162306a36Sopenharmony_ci u32 num_frags = min_t(u32, remain_frags, 69262306a36Sopenharmony_ci BUF_ALLOC_MAX_NUM_FRAGS); 69362306a36Sopenharmony_ci struct iwl_buf_alloc_cmd data = { 69462306a36Sopenharmony_ci .alloc_id = cpu_to_le32(alloc_id), 69562306a36Sopenharmony_ci .num_frags = cpu_to_le32(num_frags), 69662306a36Sopenharmony_ci .buf_location = 69762306a36Sopenharmony_ci cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH), 69862306a36Sopenharmony_ci }; 69962306a36Sopenharmony_ci struct iwl_host_cmd hcmd = { 70062306a36Sopenharmony_ci .id = WIDE_ID(DEBUG_GROUP, BUFFER_ALLOCATION), 70162306a36Sopenharmony_ci .data[0] = &data, 70262306a36Sopenharmony_ci .len[0] = sizeof(data), 70362306a36Sopenharmony_ci .flags = CMD_SEND_IN_RFKILL, 70462306a36Sopenharmony_ci }; 70562306a36Sopenharmony_ci int ret, j; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci for (j = 0; j < num_frags; j++) { 70862306a36Sopenharmony_ci struct iwl_buf_alloc_frag *frag = &data.frags[j]; 70962306a36Sopenharmony_ci struct iwl_dram_data *fw_mon_frag = 71062306a36Sopenharmony_ci &fw_mon->frags[fw_mon_idx++]; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci frag->addr = cpu_to_le64(fw_mon_frag->physical); 71362306a36Sopenharmony_ci frag->size = cpu_to_le32(fw_mon_frag->size); 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci ret = iwl_trans_send_cmd(fwrt->trans, &hcmd); 71662306a36Sopenharmony_ci if (ret) 71762306a36Sopenharmony_ci return ret; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci remain_frags -= num_frags; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci return 0; 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic void iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime *fwrt) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci int ret, i; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (fw_has_capa(&fwrt->fw->ucode_capa, 73062306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT)) 73162306a36Sopenharmony_ci return; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) { 73462306a36Sopenharmony_ci ret = iwl_dbg_tlv_apply_buffer(fwrt, i); 73562306a36Sopenharmony_ci if (ret) 73662306a36Sopenharmony_ci IWL_WARN(fwrt, 73762306a36Sopenharmony_ci "WRT: Failed to apply DRAM buffer for allocation id %d, ret=%d\n", 73862306a36Sopenharmony_ci i, ret); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic int iwl_dbg_tlv_update_dram(struct iwl_fw_runtime *fwrt, 74362306a36Sopenharmony_ci enum iwl_fw_ini_allocation_id alloc_id, 74462306a36Sopenharmony_ci struct iwl_dram_info *dram_info) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct iwl_fw_mon *fw_mon; 74762306a36Sopenharmony_ci u32 remain_frags, num_frags; 74862306a36Sopenharmony_ci int j, fw_mon_idx = 0; 74962306a36Sopenharmony_ci struct iwl_buf_alloc_cmd *data; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) != 75262306a36Sopenharmony_ci IWL_FW_INI_LOCATION_DRAM_PATH) { 75362306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: alloc_id %u location is not in DRAM_PATH\n", 75462306a36Sopenharmony_ci alloc_id); 75562306a36Sopenharmony_ci return -1; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id]; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* the first fragment of DBGC1 is given to the FW via register 76162306a36Sopenharmony_ci * or context info 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_ci if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1) 76462306a36Sopenharmony_ci fw_mon_idx++; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci remain_frags = fw_mon->num_frags - fw_mon_idx; 76762306a36Sopenharmony_ci if (!remain_frags) 76862306a36Sopenharmony_ci return -1; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci num_frags = min_t(u32, remain_frags, BUF_ALLOC_MAX_NUM_FRAGS); 77162306a36Sopenharmony_ci data = &dram_info->dram_frags[alloc_id - 1]; 77262306a36Sopenharmony_ci data->alloc_id = cpu_to_le32(alloc_id); 77362306a36Sopenharmony_ci data->num_frags = cpu_to_le32(num_frags); 77462306a36Sopenharmony_ci data->buf_location = cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: DRAM buffer details alloc_id=%u, num_frags=%u\n", 77762306a36Sopenharmony_ci cpu_to_le32(alloc_id), cpu_to_le32(num_frags)); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci for (j = 0; j < num_frags; j++) { 78062306a36Sopenharmony_ci struct iwl_buf_alloc_frag *frag = &data->frags[j]; 78162306a36Sopenharmony_ci struct iwl_dram_data *fw_mon_frag = &fw_mon->frags[fw_mon_idx++]; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci frag->addr = cpu_to_le64(fw_mon_frag->physical); 78462306a36Sopenharmony_ci frag->size = cpu_to_le32(fw_mon_frag->size); 78562306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: DRAM fragment details\n"); 78662306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "frag=%u, addr=0x%016llx, size=0x%x)\n", 78762306a36Sopenharmony_ci j, cpu_to_le64(fw_mon_frag->physical), 78862306a36Sopenharmony_ci cpu_to_le32(fw_mon_frag->size)); 78962306a36Sopenharmony_ci } 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_cistatic void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci int ret, i; 79662306a36Sopenharmony_ci bool dram_alloc = false; 79762306a36Sopenharmony_ci struct iwl_dram_data *frags = 79862306a36Sopenharmony_ci &fwrt->trans->dbg.fw_mon_ini[IWL_FW_INI_ALLOCATION_ID_DBGC1].frags[0]; 79962306a36Sopenharmony_ci struct iwl_dram_info *dram_info; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (!frags || !frags->block) 80262306a36Sopenharmony_ci return; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci dram_info = frags->block; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (!fw_has_capa(&fwrt->fw->ucode_capa, 80762306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT)) 80862306a36Sopenharmony_ci return; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci memset(dram_info, 0, sizeof(*dram_info)); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci for (i = IWL_FW_INI_ALLOCATION_ID_DBGC1; 81362306a36Sopenharmony_ci i < IWL_FW_INI_ALLOCATION_NUM; i++) { 81462306a36Sopenharmony_ci if (fwrt->trans->dbg.fw_mon_cfg[i].buf_location == 81562306a36Sopenharmony_ci IWL_FW_INI_LOCATION_INVALID) 81662306a36Sopenharmony_ci continue; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci ret = iwl_dbg_tlv_update_dram(fwrt, i, dram_info); 81962306a36Sopenharmony_ci if (!ret) 82062306a36Sopenharmony_ci dram_alloc = true; 82162306a36Sopenharmony_ci else 82262306a36Sopenharmony_ci IWL_INFO(fwrt, 82362306a36Sopenharmony_ci "WRT: Failed to set DRAM buffer for alloc id %d, ret=%d\n", 82462306a36Sopenharmony_ci i, ret); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (dram_alloc) { 82862306a36Sopenharmony_ci dram_info->first_word = cpu_to_le32(DRAM_INFO_FIRST_MAGIC_WORD); 82962306a36Sopenharmony_ci dram_info->second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci} 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_cistatic void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt, 83462306a36Sopenharmony_ci struct list_head *hcmd_list) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci list_for_each_entry(node, hcmd_list, list) { 83962306a36Sopenharmony_ci struct iwl_fw_ini_hcmd_tlv *hcmd = (void *)node->tlv.data; 84062306a36Sopenharmony_ci struct iwl_fw_ini_hcmd *hcmd_data = &hcmd->hcmd; 84162306a36Sopenharmony_ci u16 hcmd_len = le32_to_cpu(node->tlv.length) - sizeof(*hcmd); 84262306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 84362306a36Sopenharmony_ci .id = WIDE_ID(hcmd_data->group, hcmd_data->id), 84462306a36Sopenharmony_ci .len = { hcmd_len, }, 84562306a36Sopenharmony_ci .data = { hcmd_data->data, }, 84662306a36Sopenharmony_ci }; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci iwl_trans_send_cmd(fwrt->trans, &cmd); 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic void iwl_dbg_tlv_apply_config(struct iwl_fw_runtime *fwrt, 85362306a36Sopenharmony_ci struct list_head *conf_list) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci list_for_each_entry(node, conf_list, list) { 85862306a36Sopenharmony_ci struct iwl_fw_ini_conf_set_tlv *config_list = (void *)node->tlv.data; 85962306a36Sopenharmony_ci u32 count, address, value; 86062306a36Sopenharmony_ci u32 len = (le32_to_cpu(node->tlv.length) - sizeof(*config_list)) / 8; 86162306a36Sopenharmony_ci u32 type = le32_to_cpu(config_list->set_type); 86262306a36Sopenharmony_ci u32 offset = le32_to_cpu(config_list->addr_offset); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci switch (type) { 86562306a36Sopenharmony_ci case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_PERIPHERY_MAC: { 86662306a36Sopenharmony_ci if (!iwl_trans_grab_nic_access(fwrt->trans)) { 86762306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: failed to get nic access\n"); 86862306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: skipping MAC PERIPHERY config\n"); 86962306a36Sopenharmony_ci continue; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: MAC PERIPHERY config len: len %u\n", len); 87262306a36Sopenharmony_ci for (count = 0; count < len; count++) { 87362306a36Sopenharmony_ci address = le32_to_cpu(config_list->addr_val[count].address); 87462306a36Sopenharmony_ci value = le32_to_cpu(config_list->addr_val[count].value); 87562306a36Sopenharmony_ci iwl_trans_write_prph(fwrt->trans, address + offset, value); 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci iwl_trans_release_nic_access(fwrt->trans); 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_MEMORY: { 88162306a36Sopenharmony_ci for (count = 0; count < len; count++) { 88262306a36Sopenharmony_ci address = le32_to_cpu(config_list->addr_val[count].address); 88362306a36Sopenharmony_ci value = le32_to_cpu(config_list->addr_val[count].value); 88462306a36Sopenharmony_ci iwl_trans_write_mem32(fwrt->trans, address + offset, value); 88562306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: DEV_MEM: count %u, add: %u val: %u\n", 88662306a36Sopenharmony_ci count, address, value); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci case IWL_FW_INI_CONFIG_SET_TYPE_CSR: { 89162306a36Sopenharmony_ci for (count = 0; count < len; count++) { 89262306a36Sopenharmony_ci address = le32_to_cpu(config_list->addr_val[count].address); 89362306a36Sopenharmony_ci value = le32_to_cpu(config_list->addr_val[count].value); 89462306a36Sopenharmony_ci iwl_write32(fwrt->trans, address + offset, value); 89562306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: CSR: count %u, add: %u val: %u\n", 89662306a36Sopenharmony_ci count, address, value); 89762306a36Sopenharmony_ci } 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci case IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: { 90162306a36Sopenharmony_ci struct iwl_dbgc1_info dram_info = {}; 90262306a36Sopenharmony_ci struct iwl_dram_data *frags = &fwrt->trans->dbg.fw_mon_ini[1].frags[0]; 90362306a36Sopenharmony_ci __le64 dram_base_addr; 90462306a36Sopenharmony_ci __le32 dram_size; 90562306a36Sopenharmony_ci u64 dram_addr; 90662306a36Sopenharmony_ci u32 ret; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (!frags) 90962306a36Sopenharmony_ci break; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci dram_base_addr = cpu_to_le64(frags->physical); 91262306a36Sopenharmony_ci dram_size = cpu_to_le32(frags->size); 91362306a36Sopenharmony_ci dram_addr = le64_to_cpu(dram_base_addr); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: dram_base_addr 0x%016llx, dram_size 0x%x\n", 91662306a36Sopenharmony_ci dram_base_addr, dram_size); 91762306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: config_list->addr_offset: %u\n", 91862306a36Sopenharmony_ci le32_to_cpu(config_list->addr_offset)); 91962306a36Sopenharmony_ci for (count = 0; count < len; count++) { 92062306a36Sopenharmony_ci address = le32_to_cpu(config_list->addr_val[count].address); 92162306a36Sopenharmony_ci dram_info.dbgc1_add_lsb = 92262306a36Sopenharmony_ci cpu_to_le32((dram_addr & 0x00000000FFFFFFFFULL) + 0x400); 92362306a36Sopenharmony_ci dram_info.dbgc1_add_msb = 92462306a36Sopenharmony_ci cpu_to_le32((dram_addr & 0xFFFFFFFF00000000ULL) >> 32); 92562306a36Sopenharmony_ci dram_info.dbgc1_size = cpu_to_le32(le32_to_cpu(dram_size) - 0x400); 92662306a36Sopenharmony_ci ret = iwl_trans_write_mem(fwrt->trans, 92762306a36Sopenharmony_ci address + offset, &dram_info, 4); 92862306a36Sopenharmony_ci if (ret) { 92962306a36Sopenharmony_ci IWL_ERR(fwrt, "Failed to write dram_info to HW_SMEM\n"); 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci break; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci case IWL_FW_INI_CONFIG_SET_TYPE_PERIPH_SCRATCH_HWM: { 93662306a36Sopenharmony_ci u32 debug_token_config = 93762306a36Sopenharmony_ci le32_to_cpu(config_list->addr_val[0].value); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: Setting HWM debug token config: %u\n", 94062306a36Sopenharmony_ci debug_token_config); 94162306a36Sopenharmony_ci fwrt->trans->dbg.ucode_preset = debug_token_config; 94262306a36Sopenharmony_ci break; 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci default: 94562306a36Sopenharmony_ci break; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci} 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_cistatic void iwl_dbg_tlv_periodic_trig_handler(struct timer_list *t) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci struct iwl_dbg_tlv_timer_node *timer_node = 95362306a36Sopenharmony_ci from_timer(timer_node, t, timer); 95462306a36Sopenharmony_ci struct iwl_fwrt_dump_data dump_data = { 95562306a36Sopenharmony_ci .trig = (void *)timer_node->tlv->data, 95662306a36Sopenharmony_ci }; 95762306a36Sopenharmony_ci int ret; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data, false); 96062306a36Sopenharmony_ci if (!ret || ret == -EBUSY) { 96162306a36Sopenharmony_ci u32 occur = le32_to_cpu(dump_data.trig->occurrences); 96262306a36Sopenharmony_ci u32 collect_interval = le32_to_cpu(dump_data.trig->data[0]); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (!occur) 96562306a36Sopenharmony_ci return; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci mod_timer(t, jiffies + msecs_to_jiffies(collect_interval)); 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci} 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic void iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime *fwrt) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node; 97462306a36Sopenharmony_ci struct list_head *trig_list = 97562306a36Sopenharmony_ci &fwrt->trans->dbg.time_point[IWL_FW_INI_TIME_POINT_PERIODIC].active_trig_list; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci list_for_each_entry(node, trig_list, list) { 97862306a36Sopenharmony_ci struct iwl_fw_ini_trigger_tlv *trig = (void *)node->tlv.data; 97962306a36Sopenharmony_ci struct iwl_dbg_tlv_timer_node *timer_node; 98062306a36Sopenharmony_ci u32 occur = le32_to_cpu(trig->occurrences), collect_interval; 98162306a36Sopenharmony_ci u32 min_interval = 100; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci if (!occur) 98462306a36Sopenharmony_ci continue; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci /* make sure there is at least one dword of data for the 98762306a36Sopenharmony_ci * interval value 98862306a36Sopenharmony_ci */ 98962306a36Sopenharmony_ci if (le32_to_cpu(node->tlv.length) < 99062306a36Sopenharmony_ci sizeof(*trig) + sizeof(__le32)) { 99162306a36Sopenharmony_ci IWL_ERR(fwrt, 99262306a36Sopenharmony_ci "WRT: Invalid periodic trigger data was not given\n"); 99362306a36Sopenharmony_ci continue; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci if (le32_to_cpu(trig->data[0]) < min_interval) { 99762306a36Sopenharmony_ci IWL_WARN(fwrt, 99862306a36Sopenharmony_ci "WRT: Override min interval from %u to %u msec\n", 99962306a36Sopenharmony_ci le32_to_cpu(trig->data[0]), min_interval); 100062306a36Sopenharmony_ci trig->data[0] = cpu_to_le32(min_interval); 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci collect_interval = le32_to_cpu(trig->data[0]); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci timer_node = kzalloc(sizeof(*timer_node), GFP_KERNEL); 100662306a36Sopenharmony_ci if (!timer_node) { 100762306a36Sopenharmony_ci IWL_ERR(fwrt, 100862306a36Sopenharmony_ci "WRT: Failed to allocate periodic trigger\n"); 100962306a36Sopenharmony_ci continue; 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci timer_node->fwrt = fwrt; 101362306a36Sopenharmony_ci timer_node->tlv = &node->tlv; 101462306a36Sopenharmony_ci timer_setup(&timer_node->timer, 101562306a36Sopenharmony_ci iwl_dbg_tlv_periodic_trig_handler, 0); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci list_add_tail(&timer_node->list, 101862306a36Sopenharmony_ci &fwrt->trans->dbg.periodic_trig_list); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: Enabling periodic trigger\n"); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci mod_timer(&timer_node->timer, 102362306a36Sopenharmony_ci jiffies + msecs_to_jiffies(collect_interval)); 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic bool is_trig_data_contained(const struct iwl_ucode_tlv *new, 102862306a36Sopenharmony_ci const struct iwl_ucode_tlv *old) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci const struct iwl_fw_ini_trigger_tlv *new_trig = (const void *)new->data; 103162306a36Sopenharmony_ci const struct iwl_fw_ini_trigger_tlv *old_trig = (const void *)old->data; 103262306a36Sopenharmony_ci const __le32 *new_data = new_trig->data, *old_data = old_trig->data; 103362306a36Sopenharmony_ci u32 new_dwords_num = iwl_tlv_array_len(new, new_trig, data); 103462306a36Sopenharmony_ci u32 old_dwords_num = iwl_tlv_array_len(old, old_trig, data); 103562306a36Sopenharmony_ci int i, j; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci for (i = 0; i < new_dwords_num; i++) { 103862306a36Sopenharmony_ci bool match = false; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci for (j = 0; j < old_dwords_num; j++) { 104162306a36Sopenharmony_ci if (new_data[i] == old_data[j]) { 104262306a36Sopenharmony_ci match = true; 104362306a36Sopenharmony_ci break; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci if (!match) 104762306a36Sopenharmony_ci return false; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci return true; 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime *fwrt, 105462306a36Sopenharmony_ci struct iwl_ucode_tlv *trig_tlv, 105562306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct iwl_ucode_tlv *node_tlv = &node->tlv; 105862306a36Sopenharmony_ci struct iwl_fw_ini_trigger_tlv *node_trig = (void *)node_tlv->data; 105962306a36Sopenharmony_ci struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data; 106062306a36Sopenharmony_ci u32 policy = le32_to_cpu(trig->apply_policy); 106162306a36Sopenharmony_ci u32 size = le32_to_cpu(trig_tlv->length); 106262306a36Sopenharmony_ci u32 trig_data_len = size - sizeof(*trig); 106362306a36Sopenharmony_ci u32 offset = 0; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci if (!(policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA)) { 106662306a36Sopenharmony_ci u32 data_len = le32_to_cpu(node_tlv->length) - 106762306a36Sopenharmony_ci sizeof(*node_trig); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 107062306a36Sopenharmony_ci "WRT: Appending trigger data (time point %u)\n", 107162306a36Sopenharmony_ci le32_to_cpu(trig->time_point)); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci offset += data_len; 107462306a36Sopenharmony_ci size += data_len; 107562306a36Sopenharmony_ci } else { 107662306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 107762306a36Sopenharmony_ci "WRT: Overriding trigger data (time point %u)\n", 107862306a36Sopenharmony_ci le32_to_cpu(trig->time_point)); 107962306a36Sopenharmony_ci } 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci if (size != le32_to_cpu(node_tlv->length)) { 108262306a36Sopenharmony_ci struct list_head *prev = node->list.prev; 108362306a36Sopenharmony_ci struct iwl_dbg_tlv_node *tmp; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci list_del(&node->list); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci tmp = krealloc(node, sizeof(*node) + size, GFP_KERNEL); 108862306a36Sopenharmony_ci if (!tmp) { 108962306a36Sopenharmony_ci IWL_WARN(fwrt, 109062306a36Sopenharmony_ci "WRT: No memory to override trigger (time point %u)\n", 109162306a36Sopenharmony_ci le32_to_cpu(trig->time_point)); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci list_add(&node->list, prev); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return -ENOMEM; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci list_add(&tmp->list, prev); 109962306a36Sopenharmony_ci node_tlv = &tmp->tlv; 110062306a36Sopenharmony_ci node_trig = (void *)node_tlv->data; 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci memcpy((u8 *)node_trig->data + offset, trig->data, trig_data_len); 110462306a36Sopenharmony_ci node_tlv->length = cpu_to_le32(size); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG) { 110762306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 110862306a36Sopenharmony_ci "WRT: Overriding trigger configuration (time point %u)\n", 110962306a36Sopenharmony_ci le32_to_cpu(trig->time_point)); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* the first 11 dwords are configuration related */ 111262306a36Sopenharmony_ci memcpy(node_trig, trig, sizeof(__le32) * 11); 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS) { 111662306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 111762306a36Sopenharmony_ci "WRT: Overriding trigger regions (time point %u)\n", 111862306a36Sopenharmony_ci le32_to_cpu(trig->time_point)); 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci node_trig->regions_mask = trig->regions_mask; 112162306a36Sopenharmony_ci } else { 112262306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 112362306a36Sopenharmony_ci "WRT: Appending trigger regions (time point %u)\n", 112462306a36Sopenharmony_ci le32_to_cpu(trig->time_point)); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci node_trig->regions_mask |= trig->regions_mask; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci return 0; 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistatic int 113362306a36Sopenharmony_ciiwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime *fwrt, 113462306a36Sopenharmony_ci struct list_head *trig_list, 113562306a36Sopenharmony_ci struct iwl_ucode_tlv *trig_tlv) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data; 113862306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node, *match = NULL; 113962306a36Sopenharmony_ci u32 policy = le32_to_cpu(trig->apply_policy); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci list_for_each_entry(node, trig_list, list) { 114262306a36Sopenharmony_ci if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT)) 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_DATA) || 114662306a36Sopenharmony_ci is_trig_data_contained(trig_tlv, &node->tlv)) { 114762306a36Sopenharmony_ci match = node; 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (!match) { 115362306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: Enabling trigger (time point %u)\n", 115462306a36Sopenharmony_ci le32_to_cpu(trig->time_point)); 115562306a36Sopenharmony_ci return iwl_dbg_tlv_add(trig_tlv, trig_list); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci return iwl_dbg_tlv_override_trig_node(fwrt, trig_tlv, match); 115962306a36Sopenharmony_ci} 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_cistatic void 116262306a36Sopenharmony_ciiwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime *fwrt, 116362306a36Sopenharmony_ci struct iwl_dbg_tlv_time_point_data *tp) 116462306a36Sopenharmony_ci{ 116562306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node; 116662306a36Sopenharmony_ci struct list_head *trig_list = &tp->trig_list; 116762306a36Sopenharmony_ci struct list_head *active_trig_list = &tp->active_trig_list; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci list_for_each_entry(node, trig_list, list) { 117062306a36Sopenharmony_ci struct iwl_ucode_tlv *tlv = &node->tlv; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci iwl_dbg_tlv_add_active_trigger(fwrt, active_trig_list, tlv); 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime *fwrt, 117762306a36Sopenharmony_ci struct iwl_fwrt_dump_data *dump_data, 117862306a36Sopenharmony_ci union iwl_dbg_tlv_tp_data *tp_data, 117962306a36Sopenharmony_ci u32 trig_data) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct iwl_rx_packet *pkt = tp_data->fw_pkt; 118262306a36Sopenharmony_ci struct iwl_cmd_header *wanted_hdr = (void *)&trig_data; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (pkt && (pkt->hdr.cmd == wanted_hdr->cmd && 118562306a36Sopenharmony_ci pkt->hdr.group_id == wanted_hdr->group_id)) { 118662306a36Sopenharmony_ci struct iwl_rx_packet *fw_pkt = 118762306a36Sopenharmony_ci kmemdup(pkt, 118862306a36Sopenharmony_ci sizeof(*pkt) + iwl_rx_packet_payload_len(pkt), 118962306a36Sopenharmony_ci GFP_ATOMIC); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (!fw_pkt) 119262306a36Sopenharmony_ci return false; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci dump_data->fw_pkt = fw_pkt; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci return true; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci return false; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic int 120362306a36Sopenharmony_ciiwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync, 120462306a36Sopenharmony_ci struct list_head *active_trig_list, 120562306a36Sopenharmony_ci union iwl_dbg_tlv_tp_data *tp_data, 120662306a36Sopenharmony_ci bool (*data_check)(struct iwl_fw_runtime *fwrt, 120762306a36Sopenharmony_ci struct iwl_fwrt_dump_data *dump_data, 120862306a36Sopenharmony_ci union iwl_dbg_tlv_tp_data *tp_data, 120962306a36Sopenharmony_ci u32 trig_data)) 121062306a36Sopenharmony_ci{ 121162306a36Sopenharmony_ci struct iwl_dbg_tlv_node *node; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci list_for_each_entry(node, active_trig_list, list) { 121462306a36Sopenharmony_ci struct iwl_fwrt_dump_data dump_data = { 121562306a36Sopenharmony_ci .trig = (void *)node->tlv.data, 121662306a36Sopenharmony_ci }; 121762306a36Sopenharmony_ci u32 num_data = iwl_tlv_array_len(&node->tlv, dump_data.trig, 121862306a36Sopenharmony_ci data); 121962306a36Sopenharmony_ci int ret, i; 122062306a36Sopenharmony_ci u32 tp = le32_to_cpu(dump_data.trig->time_point); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (!num_data) { 122462306a36Sopenharmony_ci ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync); 122562306a36Sopenharmony_ci if (ret) 122662306a36Sopenharmony_ci return ret; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci for (i = 0; i < num_data; i++) { 123062306a36Sopenharmony_ci if (!data_check || 123162306a36Sopenharmony_ci data_check(fwrt, &dump_data, tp_data, 123262306a36Sopenharmony_ci le32_to_cpu(dump_data.trig->data[i]))) { 123362306a36Sopenharmony_ci ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync); 123462306a36Sopenharmony_ci if (ret) 123562306a36Sopenharmony_ci return ret; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci break; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci fwrt->trans->dbg.restart_required = FALSE; 124262306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: tp %d, reset_fw %d\n", 124362306a36Sopenharmony_ci tp, dump_data.trig->reset_fw); 124462306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 124562306a36Sopenharmony_ci "WRT: restart_required %d, last_tp_resetfw %d\n", 124662306a36Sopenharmony_ci fwrt->trans->dbg.restart_required, 124762306a36Sopenharmony_ci fwrt->trans->dbg.last_tp_resetfw); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci if (fwrt->trans->trans_cfg->device_family == 125062306a36Sopenharmony_ci IWL_DEVICE_FAMILY_9000) { 125162306a36Sopenharmony_ci fwrt->trans->dbg.restart_required = TRUE; 125262306a36Sopenharmony_ci } else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT && 125362306a36Sopenharmony_ci fwrt->trans->dbg.last_tp_resetfw == 125462306a36Sopenharmony_ci IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) { 125562306a36Sopenharmony_ci fwrt->trans->dbg.restart_required = FALSE; 125662306a36Sopenharmony_ci fwrt->trans->dbg.last_tp_resetfw = 0xFF; 125762306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: FW_ASSERT due to reset_fw_mode-no restart\n"); 125862306a36Sopenharmony_ci } else if (le32_to_cpu(dump_data.trig->reset_fw) == 125962306a36Sopenharmony_ci IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) { 126062306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, "WRT: stop and reload firmware\n"); 126162306a36Sopenharmony_ci fwrt->trans->dbg.restart_required = TRUE; 126262306a36Sopenharmony_ci } else if (le32_to_cpu(dump_data.trig->reset_fw) == 126362306a36Sopenharmony_ci IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) { 126462306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 126562306a36Sopenharmony_ci "WRT: stop only and no reload firmware\n"); 126662306a36Sopenharmony_ci fwrt->trans->dbg.restart_required = FALSE; 126762306a36Sopenharmony_ci fwrt->trans->dbg.last_tp_resetfw = 126862306a36Sopenharmony_ci le32_to_cpu(dump_data.trig->reset_fw); 126962306a36Sopenharmony_ci } else if (le32_to_cpu(dump_data.trig->reset_fw) == 127062306a36Sopenharmony_ci IWL_FW_INI_RESET_FW_MODE_NOTHING) { 127162306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 127262306a36Sopenharmony_ci "WRT: nothing need to be done after debug collection\n"); 127362306a36Sopenharmony_ci } else { 127462306a36Sopenharmony_ci IWL_ERR(fwrt, "WRT: wrong resetfw %d\n", 127562306a36Sopenharmony_ci le32_to_cpu(dump_data.trig->reset_fw)); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci return 0; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci enum iwl_fw_ini_buffer_location *ini_dest = &fwrt->trans->dbg.ini_dest; 128462306a36Sopenharmony_ci int ret, i; 128562306a36Sopenharmony_ci u32 failed_alloc = 0; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci if (*ini_dest == IWL_FW_INI_LOCATION_INVALID) { 128862306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 128962306a36Sopenharmony_ci "WRT: Generating active triggers list, domain 0x%x\n", 129062306a36Sopenharmony_ci fwrt->trans->dbg.domains_bitmap); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.time_point); i++) { 129362306a36Sopenharmony_ci struct iwl_dbg_tlv_time_point_data *tp = 129462306a36Sopenharmony_ci &fwrt->trans->dbg.time_point[i]; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci iwl_dbg_tlv_gen_active_trig_list(fwrt, tp); 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci } else if (*ini_dest != IWL_FW_INI_LOCATION_DRAM_PATH) { 129962306a36Sopenharmony_ci /* For DRAM, go through the loop below to clear all the buffers 130062306a36Sopenharmony_ci * properly on restart, otherwise garbage may be left there and 130162306a36Sopenharmony_ci * leak into new debug dumps. 130262306a36Sopenharmony_ci */ 130362306a36Sopenharmony_ci return; 130462306a36Sopenharmony_ci } 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci *ini_dest = IWL_FW_INI_LOCATION_INVALID; 130762306a36Sopenharmony_ci for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) { 130862306a36Sopenharmony_ci struct iwl_fw_ini_allocation_tlv *fw_mon_cfg = 130962306a36Sopenharmony_ci &fwrt->trans->dbg.fw_mon_cfg[i]; 131062306a36Sopenharmony_ci u32 dest = le32_to_cpu(fw_mon_cfg->buf_location); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci if (dest == IWL_FW_INI_LOCATION_INVALID) { 131362306a36Sopenharmony_ci failed_alloc |= BIT(i); 131462306a36Sopenharmony_ci continue; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (*ini_dest == IWL_FW_INI_LOCATION_INVALID) 131862306a36Sopenharmony_ci *ini_dest = dest; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci if (dest != *ini_dest) 132162306a36Sopenharmony_ci continue; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci ret = iwl_dbg_tlv_alloc_fragments(fwrt, i); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci if (ret) { 132662306a36Sopenharmony_ci IWL_WARN(fwrt, 132762306a36Sopenharmony_ci "WRT: Failed to allocate DRAM buffer for allocation id %d, ret=%d\n", 132862306a36Sopenharmony_ci i, ret); 132962306a36Sopenharmony_ci failed_alloc |= BIT(i); 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci if (!failed_alloc) 133462306a36Sopenharmony_ci return; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions) && failed_alloc; i++) { 133762306a36Sopenharmony_ci struct iwl_fw_ini_region_tlv *reg; 133862306a36Sopenharmony_ci struct iwl_ucode_tlv **active_reg = 133962306a36Sopenharmony_ci &fwrt->trans->dbg.active_regions[i]; 134062306a36Sopenharmony_ci u32 reg_type; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (!*active_reg) { 134362306a36Sopenharmony_ci fwrt->trans->dbg.unsupported_region_msk |= BIT(i); 134462306a36Sopenharmony_ci continue; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci reg = (void *)(*active_reg)->data; 134862306a36Sopenharmony_ci reg_type = reg->type; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (reg_type != IWL_FW_INI_REGION_DRAM_BUFFER || 135162306a36Sopenharmony_ci !(BIT(le32_to_cpu(reg->dram_alloc_id)) & failed_alloc)) 135262306a36Sopenharmony_ci continue; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci IWL_DEBUG_FW(fwrt, 135562306a36Sopenharmony_ci "WRT: removing allocation id %d from region id %d\n", 135662306a36Sopenharmony_ci le32_to_cpu(reg->dram_alloc_id), i); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci failed_alloc &= ~BIT(le32_to_cpu(reg->dram_alloc_id)); 135962306a36Sopenharmony_ci fwrt->trans->dbg.unsupported_region_msk |= BIT(i); 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci kfree(*active_reg); 136262306a36Sopenharmony_ci *active_reg = NULL; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci} 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_civoid _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt, 136762306a36Sopenharmony_ci enum iwl_fw_ini_time_point tp_id, 136862306a36Sopenharmony_ci union iwl_dbg_tlv_tp_data *tp_data, 136962306a36Sopenharmony_ci bool sync) 137062306a36Sopenharmony_ci{ 137162306a36Sopenharmony_ci struct list_head *hcmd_list, *trig_list, *conf_list; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (!iwl_trans_dbg_ini_valid(fwrt->trans) || 137462306a36Sopenharmony_ci tp_id == IWL_FW_INI_TIME_POINT_INVALID || 137562306a36Sopenharmony_ci tp_id >= IWL_FW_INI_TIME_POINT_NUM) 137662306a36Sopenharmony_ci return; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci hcmd_list = &fwrt->trans->dbg.time_point[tp_id].hcmd_list; 137962306a36Sopenharmony_ci trig_list = &fwrt->trans->dbg.time_point[tp_id].active_trig_list; 138062306a36Sopenharmony_ci conf_list = &fwrt->trans->dbg.time_point[tp_id].config_list; 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci switch (tp_id) { 138362306a36Sopenharmony_ci case IWL_FW_INI_TIME_POINT_EARLY: 138462306a36Sopenharmony_ci iwl_dbg_tlv_init_cfg(fwrt); 138562306a36Sopenharmony_ci iwl_dbg_tlv_apply_config(fwrt, conf_list); 138662306a36Sopenharmony_ci iwl_dbg_tlv_update_drams(fwrt); 138762306a36Sopenharmony_ci iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); 138862306a36Sopenharmony_ci break; 138962306a36Sopenharmony_ci case IWL_FW_INI_TIME_POINT_AFTER_ALIVE: 139062306a36Sopenharmony_ci iwl_dbg_tlv_apply_buffers(fwrt); 139162306a36Sopenharmony_ci iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); 139262306a36Sopenharmony_ci iwl_dbg_tlv_apply_config(fwrt, conf_list); 139362306a36Sopenharmony_ci iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); 139462306a36Sopenharmony_ci break; 139562306a36Sopenharmony_ci case IWL_FW_INI_TIME_POINT_PERIODIC: 139662306a36Sopenharmony_ci iwl_dbg_tlv_set_periodic_trigs(fwrt); 139762306a36Sopenharmony_ci iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); 139862306a36Sopenharmony_ci break; 139962306a36Sopenharmony_ci case IWL_FW_INI_TIME_POINT_FW_RSP_OR_NOTIF: 140062306a36Sopenharmony_ci case IWL_FW_INI_TIME_POINT_MISSED_BEACONS: 140162306a36Sopenharmony_ci case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION: 140262306a36Sopenharmony_ci iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); 140362306a36Sopenharmony_ci iwl_dbg_tlv_apply_config(fwrt, conf_list); 140462306a36Sopenharmony_ci iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, 140562306a36Sopenharmony_ci iwl_dbg_tlv_check_fw_pkt); 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci default: 140862306a36Sopenharmony_ci iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); 140962306a36Sopenharmony_ci iwl_dbg_tlv_apply_config(fwrt, conf_list); 141062306a36Sopenharmony_ci iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); 141162306a36Sopenharmony_ci break; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(_iwl_dbg_tlv_time_point); 1415