Path: blob/main/sys/contrib/dev/iwlwifi/iwl-dbg-tlv.c
48253 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2018-2025 Intel Corporation3*/4#include <linux/firmware.h>5#include "iwl-drv.h"6#include "iwl-trans.h"7#include "iwl-dbg-tlv.h"8#include "fw/dbg.h"9#include "fw/runtime.h"1011/**12* enum iwl_dbg_tlv_type - debug TLV types13* @IWL_DBG_TLV_TYPE_DEBUG_INFO: debug info TLV14* @IWL_DBG_TLV_TYPE_BUF_ALLOC: buffer allocation TLV15* @IWL_DBG_TLV_TYPE_HCMD: host command TLV16* @IWL_DBG_TLV_TYPE_REGION: region TLV17* @IWL_DBG_TLV_TYPE_TRIGGER: trigger TLV18* @IWL_DBG_TLV_TYPE_CONF_SET: conf set TLV19* @IWL_DBG_TLV_TYPE_NUM: number of debug TLVs20*/21enum iwl_dbg_tlv_type {22IWL_DBG_TLV_TYPE_DEBUG_INFO =23IWL_UCODE_TLV_TYPE_DEBUG_INFO - IWL_UCODE_TLV_DEBUG_BASE,24IWL_DBG_TLV_TYPE_BUF_ALLOC,25IWL_DBG_TLV_TYPE_HCMD,26IWL_DBG_TLV_TYPE_REGION,27IWL_DBG_TLV_TYPE_TRIGGER,28IWL_DBG_TLV_TYPE_CONF_SET,29IWL_DBG_TLV_TYPE_NUM,30};3132/**33* struct iwl_dbg_tlv_ver_data - debug TLV version struct34* @min_ver: min version supported35* @max_ver: max version supported36*/37struct iwl_dbg_tlv_ver_data {38int min_ver;39int max_ver;40};4142/**43* struct iwl_dbg_tlv_timer_node - timer node struct44* @list: list of &struct iwl_dbg_tlv_timer_node45* @timer: timer46* @fwrt: &struct iwl_fw_runtime47* @tlv: TLV attach to the timer node48*/49struct iwl_dbg_tlv_timer_node {50struct list_head list;51struct timer_list timer;52struct iwl_fw_runtime *fwrt;53struct iwl_ucode_tlv *tlv;54};5556static const struct iwl_dbg_tlv_ver_data57dbg_ver_table[IWL_DBG_TLV_TYPE_NUM] = {58[IWL_DBG_TLV_TYPE_DEBUG_INFO] = {.min_ver = 1, .max_ver = 1,},59[IWL_DBG_TLV_TYPE_BUF_ALLOC] = {.min_ver = 1, .max_ver = 1,},60[IWL_DBG_TLV_TYPE_HCMD] = {.min_ver = 1, .max_ver = 1,},61[IWL_DBG_TLV_TYPE_REGION] = {.min_ver = 1, .max_ver = 3,},62[IWL_DBG_TLV_TYPE_TRIGGER] = {.min_ver = 1, .max_ver = 1,},63[IWL_DBG_TLV_TYPE_CONF_SET] = {.min_ver = 1, .max_ver = 1,},64};6566/* add a new TLV node, returning it so it can be modified */67static struct iwl_ucode_tlv *iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv,68struct list_head *list)69{70u32 len = le32_to_cpu(tlv->length);71struct iwl_dbg_tlv_node *node;7273node = kzalloc(struct_size(node, tlv.data, len), GFP_KERNEL);74if (!node)75return NULL;7677memcpy(&node->tlv, tlv, sizeof(node->tlv));78memcpy(node->tlv.data, tlv->data, len);79list_add_tail(&node->list, list);8081return &node->tlv;82}8384static bool iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv *tlv)85{86const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];87u32 type = le32_to_cpu(tlv->type);88u32 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;89u32 ver = le32_to_cpu(hdr->version);9091if (ver < dbg_ver_table[tlv_idx].min_ver ||92ver > dbg_ver_table[tlv_idx].max_ver)93return false;9495return true;96}9798static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans,99const struct iwl_ucode_tlv *tlv)100{101const struct iwl_fw_ini_debug_info_tlv *debug_info = (const void *)tlv->data;102103if (le32_to_cpu(tlv->length) != sizeof(*debug_info))104return -EINVAL;105106/* we use this as a string, ensure input was NUL terminated */107if (strnlen(debug_info->debug_cfg_name,108sizeof(debug_info->debug_cfg_name)) ==109sizeof(debug_info->debug_cfg_name))110return -EINVAL;111112IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n",113debug_info->debug_cfg_name);114115if (!iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list))116return -ENOMEM;117return 0;118}119120static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans,121const struct iwl_ucode_tlv *tlv)122{123const struct iwl_fw_ini_allocation_tlv *alloc = (const void *)tlv->data;124u32 buf_location;125u32 alloc_id;126127if (le32_to_cpu(tlv->length) != sizeof(*alloc))128return -EINVAL;129130buf_location = le32_to_cpu(alloc->buf_location);131alloc_id = le32_to_cpu(alloc->alloc_id);132133if (buf_location == IWL_FW_INI_LOCATION_INVALID ||134buf_location >= IWL_FW_INI_LOCATION_NUM)135goto err;136137if (alloc_id == IWL_FW_INI_ALLOCATION_INVALID ||138alloc_id >= IWL_FW_INI_ALLOCATION_NUM)139goto err;140141if (buf_location == IWL_FW_INI_LOCATION_NPK_PATH &&142alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)143goto err;144145if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH &&146alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)147goto err;148149if (buf_location == IWL_FW_INI_LOCATION_DRAM_PATH &&150alloc->req_size == 0) {151IWL_ERR(trans, "WRT: Invalid DRAM buffer allocation requested size (0)\n");152return -EINVAL;153}154155trans->dbg.fw_mon_cfg[alloc_id] = *alloc;156157return 0;158err:159IWL_ERR(trans,160"WRT: Invalid allocation id %u and/or location id %u for allocation TLV\n",161alloc_id, buf_location);162return -EINVAL;163}164165static int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans *trans,166const struct iwl_ucode_tlv *tlv)167{168const struct iwl_fw_ini_hcmd_tlv *hcmd = (const void *)tlv->data;169u32 tp = le32_to_cpu(hcmd->time_point);170171if (le32_to_cpu(tlv->length) <= sizeof(*hcmd))172return -EINVAL;173174/* Host commands can not be sent in early time point since the FW175* is not ready176*/177if (tp == IWL_FW_INI_TIME_POINT_INVALID ||178tp >= IWL_FW_INI_TIME_POINT_NUM ||179tp == IWL_FW_INI_TIME_POINT_EARLY) {180IWL_ERR(trans,181"WRT: Invalid time point %u for host command TLV\n",182tp);183return -EINVAL;184}185186if (!iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list))187return -ENOMEM;188return 0;189}190191static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans,192const struct iwl_ucode_tlv *tlv)193{194const struct iwl_fw_ini_region_tlv *reg = (const void *)tlv->data;195struct iwl_ucode_tlv **active_reg;196u32 id = le32_to_cpu(reg->id);197u8 type = reg->type;198u32 tlv_len = sizeof(*tlv) + le32_to_cpu(tlv->length);199200/*201* The higher part of the ID from version 2 is debug policy.202* The id will be only lsb 16 bits, so mask it out.203*/204if (le32_to_cpu(reg->hdr.version) >= 2)205id &= IWL_FW_INI_REGION_ID_MASK;206207if (le32_to_cpu(tlv->length) < sizeof(*reg))208return -EINVAL;209210/* for safe use of a string from FW, limit it to IWL_FW_INI_MAX_NAME */211IWL_DEBUG_FW(trans, "WRT: parsing region: %.*s\n",212IWL_FW_INI_MAX_NAME, reg->name);213214if (id >= IWL_FW_INI_MAX_REGION_ID) {215IWL_ERR(trans, "WRT: Invalid region id %u\n", id);216return -EINVAL;217}218219if (type <= IWL_FW_INI_REGION_INVALID ||220type >= IWL_FW_INI_REGION_NUM) {221IWL_ERR(trans, "WRT: Invalid region type %u\n", type);222return -EINVAL;223}224225if (type == IWL_FW_INI_REGION_INTERNAL_BUFFER) {226trans->dbg.imr_data.sram_addr =227le32_to_cpu(reg->internal_buffer.base_addr);228trans->dbg.imr_data.sram_size =229le32_to_cpu(reg->internal_buffer.size);230}231232233active_reg = &trans->dbg.active_regions[id];234if (*active_reg) {235IWL_WARN(trans, "WRT: Overriding region id %u\n", id);236237kfree(*active_reg);238}239240*active_reg = kmemdup(tlv, tlv_len, GFP_KERNEL);241if (!*active_reg)242return -ENOMEM;243244IWL_DEBUG_FW(trans, "WRT: Enabling region id %u type %u\n", id, type);245246return 0;247}248249static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans,250const struct iwl_ucode_tlv *tlv)251{252const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data;253u32 tp = le32_to_cpu(trig->time_point);254u32 rf = le32_to_cpu(trig->reset_fw);255struct iwl_ucode_tlv *new_tlv;256257if (le32_to_cpu(tlv->length) < sizeof(*trig))258return -EINVAL;259260if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||261tp >= IWL_FW_INI_TIME_POINT_NUM) {262IWL_ERR(trans,263"WRT: Invalid time point %u for trigger TLV\n",264tp);265return -EINVAL;266}267268IWL_DEBUG_FW(trans,269"WRT: time point %u for trigger TLV with reset_fw %u\n",270tp, rf);271trans->dbg.last_tp_resetfw = 0xFF;272273new_tlv = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list);274if (!new_tlv)275return -ENOMEM;276277if (!le32_to_cpu(trig->occurrences)) {278struct iwl_fw_ini_trigger_tlv *new_trig = (void *)new_tlv->data;279280new_trig->occurrences = cpu_to_le32(-1);281}282283return 0;284}285286static int iwl_dbg_tlv_config_set(struct iwl_trans *trans,287const struct iwl_ucode_tlv *tlv)288{289const struct iwl_fw_ini_conf_set_tlv *conf_set = (const void *)tlv->data;290u32 tp = le32_to_cpu(conf_set->time_point);291u32 type = le32_to_cpu(conf_set->set_type);292293if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||294tp >= IWL_FW_INI_TIME_POINT_NUM) {295IWL_DEBUG_FW(trans,296"WRT: Invalid time point %u for config set TLV\n", tp);297return -EINVAL;298}299300if (type <= IWL_FW_INI_CONFIG_SET_TYPE_INVALID ||301type >= IWL_FW_INI_CONFIG_SET_TYPE_MAX_NUM) {302IWL_DEBUG_FW(trans,303"WRT: Invalid config set type %u for config set TLV\n", type);304return -EINVAL;305}306307if (!iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].config_list))308return -ENOMEM;309return 0;310}311312static int (*dbg_tlv_alloc[])(struct iwl_trans *trans,313const struct iwl_ucode_tlv *tlv) = {314[IWL_DBG_TLV_TYPE_DEBUG_INFO] = iwl_dbg_tlv_alloc_debug_info,315[IWL_DBG_TLV_TYPE_BUF_ALLOC] = iwl_dbg_tlv_alloc_buf_alloc,316[IWL_DBG_TLV_TYPE_HCMD] = iwl_dbg_tlv_alloc_hcmd,317[IWL_DBG_TLV_TYPE_REGION] = iwl_dbg_tlv_alloc_region,318[IWL_DBG_TLV_TYPE_TRIGGER] = iwl_dbg_tlv_alloc_trigger,319[IWL_DBG_TLV_TYPE_CONF_SET] = iwl_dbg_tlv_config_set,320};321322void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv,323bool ext)324{325enum iwl_ini_cfg_state *cfg_state = ext ?326&trans->dbg.external_ini_cfg : &trans->dbg.internal_ini_cfg;327const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];328u32 type;329u32 tlv_idx;330u32 domain;331int ret;332333if (le32_to_cpu(tlv->length) < sizeof(*hdr))334return;335336type = le32_to_cpu(tlv->type);337tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;338domain = le32_to_cpu(hdr->domain);339340if (domain != IWL_FW_INI_DOMAIN_ALWAYS_ON &&341!(domain & trans->dbg.domains_bitmap)) {342IWL_DEBUG_FW(trans,343"WRT: Skipping TLV with disabled domain 0x%0x (0x%0x)\n",344domain, trans->dbg.domains_bitmap);345return;346}347348if (tlv_idx >= ARRAY_SIZE(dbg_tlv_alloc) || !dbg_tlv_alloc[tlv_idx]) {349IWL_ERR(trans, "WRT: Unsupported TLV type 0x%x\n", type);350goto out_err;351}352353if (!iwl_dbg_tlv_ver_support(tlv)) {354IWL_ERR(trans, "WRT: Unsupported TLV 0x%x version %u\n", type,355le32_to_cpu(hdr->version));356goto out_err;357}358359ret = dbg_tlv_alloc[tlv_idx](trans, tlv);360if (ret) {361IWL_WARN(trans,362"WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n",363type, ret, ext);364goto out_err;365}366367if (*cfg_state == IWL_INI_CFG_STATE_NOT_LOADED)368*cfg_state = IWL_INI_CFG_STATE_LOADED;369370return;371372out_err:373*cfg_state = IWL_INI_CFG_STATE_CORRUPTED;374}375376void iwl_dbg_tlv_del_timers(struct iwl_trans *trans)377{378struct list_head *timer_list = &trans->dbg.periodic_trig_list;379struct iwl_dbg_tlv_timer_node *node, *tmp;380381list_for_each_entry_safe(node, tmp, timer_list, list) {382timer_shutdown_sync(&node->timer);383list_del(&node->list);384kfree(node);385}386}387IWL_EXPORT_SYMBOL(iwl_dbg_tlv_del_timers);388389static void iwl_dbg_tlv_fragments_free(struct iwl_trans *trans,390enum iwl_fw_ini_allocation_id alloc_id)391{392struct iwl_fw_mon *fw_mon;393int i;394395if (alloc_id <= IWL_FW_INI_ALLOCATION_INVALID ||396alloc_id >= IWL_FW_INI_ALLOCATION_NUM)397return;398399fw_mon = &trans->dbg.fw_mon_ini[alloc_id];400401for (i = 0; i < fw_mon->num_frags; i++) {402struct iwl_dram_data *frag = &fw_mon->frags[i];403404dma_free_coherent(trans->dev, frag->size, frag->block,405frag->physical);406407frag->physical = 0;408frag->block = NULL;409frag->size = 0;410}411412kfree(fw_mon->frags);413fw_mon->frags = NULL;414fw_mon->num_frags = 0;415}416417void iwl_dbg_tlv_free(struct iwl_trans *trans)418{419struct iwl_dbg_tlv_node *tlv_node, *tlv_node_tmp;420int i;421422iwl_dbg_tlv_del_timers(trans);423424for (i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {425struct iwl_ucode_tlv **active_reg =426&trans->dbg.active_regions[i];427428kfree(*active_reg);429*active_reg = NULL;430}431432list_for_each_entry_safe(tlv_node, tlv_node_tmp,433&trans->dbg.debug_info_tlv_list, list) {434list_del(&tlv_node->list);435kfree(tlv_node);436}437438for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {439struct iwl_dbg_tlv_time_point_data *tp =440&trans->dbg.time_point[i];441442list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->trig_list,443list) {444list_del(&tlv_node->list);445kfree(tlv_node);446}447448list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->hcmd_list,449list) {450list_del(&tlv_node->list);451kfree(tlv_node);452}453454list_for_each_entry_safe(tlv_node, tlv_node_tmp,455&tp->active_trig_list, list) {456list_del(&tlv_node->list);457kfree(tlv_node);458}459460list_for_each_entry_safe(tlv_node, tlv_node_tmp,461&tp->config_list, list) {462list_del(&tlv_node->list);463kfree(tlv_node);464}465466}467468for (i = 0; i < ARRAY_SIZE(trans->dbg.fw_mon_ini); i++)469iwl_dbg_tlv_fragments_free(trans, i);470}471472static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data,473size_t len)474{475const struct iwl_ucode_tlv *tlv;476u32 tlv_len;477478while (len >= sizeof(*tlv)) {479len -= sizeof(*tlv);480tlv = (const void *)data;481482tlv_len = le32_to_cpu(tlv->length);483484if (len < tlv_len) {485IWL_ERR(trans, "invalid TLV len: %zd/%u\n",486len, tlv_len);487return -EINVAL;488}489len -= ALIGN(tlv_len, 4);490data += sizeof(*tlv) + ALIGN(tlv_len, 4);491492iwl_dbg_tlv_alloc(trans, tlv, true);493}494495return 0;496}497498void iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans)499{500const struct firmware *fw;501const char *yoyo_bin = "iwl-debug-yoyo.bin";502int res;503504if (!iwlwifi_mod_params.enable_ini ||505trans->mac_cfg->device_family <= IWL_DEVICE_FAMILY_8000)506return;507508res = firmware_request_nowarn(&fw, yoyo_bin, dev);509IWL_DEBUG_FW(trans, "%s %s\n", res ? "didn't load" : "loaded", yoyo_bin);510511if (res)512return;513514trans->dbg.yoyo_bin_loaded = true;515516iwl_dbg_tlv_parse_bin(trans, fw->data, fw->size);517518release_firmware(fw);519}520521void iwl_dbg_tlv_init(struct iwl_trans *trans)522{523int i;524525INIT_LIST_HEAD(&trans->dbg.debug_info_tlv_list);526INIT_LIST_HEAD(&trans->dbg.periodic_trig_list);527528for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {529struct iwl_dbg_tlv_time_point_data *tp =530&trans->dbg.time_point[i];531532INIT_LIST_HEAD(&tp->trig_list);533INIT_LIST_HEAD(&tp->hcmd_list);534INIT_LIST_HEAD(&tp->active_trig_list);535INIT_LIST_HEAD(&tp->config_list);536}537}538539static int iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime *fwrt,540struct iwl_dram_data *frag, u32 pages)541{542void *block = NULL;543dma_addr_t physical;544545if (!frag || frag->size || !pages)546return -EIO;547548/*549* We try to allocate as many pages as we can, starting with550* the requested amount and going down until we can allocate551* something. Because of DIV_ROUND_UP(), pages will never go552* down to 0 and stop the loop, so stop when pages reaches 1,553* which is too small anyway.554*/555while (pages > 1) {556block = dma_alloc_coherent(fwrt->dev, pages * PAGE_SIZE,557&physical,558GFP_KERNEL | __GFP_NOWARN);559if (block)560break;561562IWL_WARN(fwrt, "WRT: Failed to allocate fragment size %lu\n",563pages * PAGE_SIZE);564565pages = DIV_ROUND_UP(pages, 2);566}567568if (!block)569return -ENOMEM;570571frag->physical = physical;572frag->block = block;573frag->size = pages * PAGE_SIZE;574575return pages;576}577578static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,579enum iwl_fw_ini_allocation_id alloc_id)580{581struct iwl_fw_mon *fw_mon;582struct iwl_fw_ini_allocation_tlv *fw_mon_cfg;583u32 num_frags, remain_pages, frag_pages;584int i;585586if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||587alloc_id >= IWL_FW_INI_ALLOCATION_NUM)588return -EIO;589590fw_mon_cfg = &fwrt->trans->dbg.fw_mon_cfg[alloc_id];591fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];592593if (fw_mon->num_frags) {594for (i = 0; i < fw_mon->num_frags; i++)595memset(fw_mon->frags[i].block, 0,596fw_mon->frags[i].size);597return 0;598}599600if (fw_mon_cfg->buf_location !=601cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH))602return 0;603604num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num);605if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {606if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)607return -EIO;608num_frags = 1;609} else if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ &&610alloc_id > IWL_FW_INI_ALLOCATION_ID_DBGC3) {611return -EIO;612}613614remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size),615PAGE_SIZE);616num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS);617num_frags = min_t(u32, num_frags, remain_pages);618frag_pages = DIV_ROUND_UP(remain_pages, num_frags);619620fw_mon->frags = kcalloc(num_frags, sizeof(*fw_mon->frags), GFP_KERNEL);621if (!fw_mon->frags)622return -ENOMEM;623624for (i = 0; i < num_frags; i++) {625int pages = min_t(u32, frag_pages, remain_pages);626627IWL_DEBUG_FW(fwrt,628"WRT: Allocating DRAM buffer (alloc_id=%u, fragment=%u, size=0x%lx)\n",629alloc_id, i, pages * PAGE_SIZE);630631pages = iwl_dbg_tlv_alloc_fragment(fwrt, &fw_mon->frags[i],632pages);633if (pages < 0) {634u32 alloc_size = le32_to_cpu(fw_mon_cfg->req_size) -635(remain_pages * PAGE_SIZE);636637if (alloc_size < le32_to_cpu(fw_mon_cfg->min_size)) {638iwl_dbg_tlv_fragments_free(fwrt->trans,639alloc_id);640return pages;641}642break;643}644645remain_pages -= pages;646fw_mon->num_frags++;647}648649return 0;650}651652static int iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime *fwrt,653enum iwl_fw_ini_allocation_id alloc_id)654{655struct iwl_fw_mon *fw_mon;656u32 remain_frags, num_commands;657int i, fw_mon_idx = 0;658659if (!fw_has_capa(&fwrt->fw->ucode_capa,660IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP))661return 0;662663if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||664alloc_id >= IWL_FW_INI_ALLOCATION_NUM)665return -EIO;666667if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=668IWL_FW_INI_LOCATION_DRAM_PATH)669return 0;670671fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];672673/* the first fragment of DBGC1 is given to the FW via register674* or context info675*/676if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)677fw_mon_idx++;678679remain_frags = fw_mon->num_frags - fw_mon_idx;680if (!remain_frags)681return 0;682683num_commands = DIV_ROUND_UP(remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);684685IWL_DEBUG_FW(fwrt, "WRT: Applying DRAM destination (alloc_id=%u)\n",686alloc_id);687688for (i = 0; i < num_commands; i++) {689u32 num_frags = min_t(u32, remain_frags,690BUF_ALLOC_MAX_NUM_FRAGS);691struct iwl_buf_alloc_cmd data = {692.alloc_id = cpu_to_le32(alloc_id),693.num_frags = cpu_to_le32(num_frags),694.buf_location =695cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH),696};697struct iwl_host_cmd hcmd = {698.id = WIDE_ID(DEBUG_GROUP, BUFFER_ALLOCATION),699.data[0] = &data,700.len[0] = sizeof(data),701.flags = CMD_SEND_IN_RFKILL,702};703int ret, j;704705for (j = 0; j < num_frags; j++) {706struct iwl_buf_alloc_frag *frag = &data.frags[j];707struct iwl_dram_data *fw_mon_frag =708&fw_mon->frags[fw_mon_idx++];709710frag->addr = cpu_to_le64(fw_mon_frag->physical);711frag->size = cpu_to_le32(fw_mon_frag->size);712}713ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);714if (ret)715return ret;716717remain_frags -= num_frags;718}719720return 0;721}722723static void iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime *fwrt)724{725int ret, i;726727if (fw_has_capa(&fwrt->fw->ucode_capa,728IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))729return;730731for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {732ret = iwl_dbg_tlv_apply_buffer(fwrt, i);733if (ret)734IWL_WARN(fwrt,735"WRT: Failed to apply DRAM buffer for allocation id %d, ret=%d\n",736i, ret);737}738}739740static int iwl_dbg_tlv_update_dram(struct iwl_fw_runtime *fwrt,741enum iwl_fw_ini_allocation_id alloc_id,742struct iwl_dram_info *dram_info)743{744struct iwl_fw_mon *fw_mon;745u32 remain_frags, num_frags;746int j, fw_mon_idx = 0;747struct iwl_buf_alloc_cmd *data;748749if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=750IWL_FW_INI_LOCATION_DRAM_PATH) {751IWL_DEBUG_FW(fwrt, "WRT: alloc_id %u location is not in DRAM_PATH\n",752alloc_id);753return -1;754}755756fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];757758/* the first fragment of DBGC1 is given to the FW via register759* or context info760*/761if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)762fw_mon_idx++;763764remain_frags = fw_mon->num_frags - fw_mon_idx;765if (!remain_frags)766return -1;767768num_frags = min_t(u32, remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);769data = &dram_info->dram_frags[alloc_id - 1];770data->alloc_id = cpu_to_le32(alloc_id);771data->num_frags = cpu_to_le32(num_frags);772data->buf_location = cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH);773774IWL_DEBUG_FW(fwrt, "WRT: DRAM buffer details alloc_id=%u, num_frags=%u\n",775cpu_to_le32(alloc_id), cpu_to_le32(num_frags));776777for (j = 0; j < num_frags; j++) {778struct iwl_buf_alloc_frag *frag = &data->frags[j];779struct iwl_dram_data *fw_mon_frag = &fw_mon->frags[fw_mon_idx++];780781frag->addr = cpu_to_le64(fw_mon_frag->physical);782frag->size = cpu_to_le32(fw_mon_frag->size);783IWL_DEBUG_FW(fwrt, "WRT: DRAM fragment details\n");784IWL_DEBUG_FW(fwrt, "frag=%u, addr=0x%016llx, size=0x%x)\n",785j, cpu_to_le64(fw_mon_frag->physical),786cpu_to_le32(fw_mon_frag->size));787}788return 0;789}790791static void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt)792{793int ret, i;794bool dram_alloc = false;795struct iwl_dram_data *frags =796&fwrt->trans->dbg.fw_mon_ini[IWL_FW_INI_ALLOCATION_ID_DBGC1].frags[0];797struct iwl_dram_info *dram_info;798799if (!frags || !frags->block)800return;801802dram_info = frags->block;803804if (!fw_has_capa(&fwrt->fw->ucode_capa,805IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))806return;807808memset(dram_info, 0, sizeof(*dram_info));809810for (i = IWL_FW_INI_ALLOCATION_ID_DBGC1;811i < IWL_FW_INI_ALLOCATION_NUM; i++) {812if (fwrt->trans->dbg.fw_mon_cfg[i].buf_location ==813IWL_FW_INI_LOCATION_INVALID)814continue;815816ret = iwl_dbg_tlv_update_dram(fwrt, i, dram_info);817if (!ret)818dram_alloc = true;819else820IWL_INFO(fwrt,821"WRT: Failed to set DRAM buffer for alloc id %d, ret=%d\n",822i, ret);823}824825if (dram_alloc) {826dram_info->first_word = cpu_to_le32(DRAM_INFO_FIRST_MAGIC_WORD);827dram_info->second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD);828}829}830831static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt,832struct list_head *hcmd_list)833{834struct iwl_dbg_tlv_node *node;835836list_for_each_entry(node, hcmd_list, list) {837struct iwl_fw_ini_hcmd_tlv *hcmd = (void *)node->tlv.data;838struct iwl_fw_ini_hcmd *hcmd_data = &hcmd->hcmd;839u16 hcmd_len = le32_to_cpu(node->tlv.length) - sizeof(*hcmd);840struct iwl_host_cmd cmd = {841.id = WIDE_ID(hcmd_data->group, hcmd_data->id),842.len = { hcmd_len, },843.data = { hcmd_data->data, },844};845846iwl_trans_send_cmd(fwrt->trans, &cmd);847}848}849850static void iwl_dbg_tlv_apply_config(struct iwl_fw_runtime *fwrt,851struct list_head *conf_list)852{853struct iwl_dbg_tlv_node *node;854855list_for_each_entry(node, conf_list, list) {856struct iwl_fw_ini_conf_set_tlv *config_list = (void *)node->tlv.data;857u32 count, address, value;858u32 len = (le32_to_cpu(node->tlv.length) - sizeof(*config_list)) / 8;859u32 type = le32_to_cpu(config_list->set_type);860u32 offset = le32_to_cpu(config_list->addr_offset);861862switch (type) {863case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_PERIPHERY_MAC: {864if (!iwl_trans_grab_nic_access(fwrt->trans)) {865IWL_DEBUG_FW(fwrt, "WRT: failed to get nic access\n");866IWL_DEBUG_FW(fwrt, "WRT: skipping MAC PERIPHERY config\n");867continue;868}869IWL_DEBUG_FW(fwrt, "WRT: MAC PERIPHERY config len: len %u\n", len);870for (count = 0; count < len; count++) {871address = le32_to_cpu(config_list->addr_val[count].address);872value = le32_to_cpu(config_list->addr_val[count].value);873iwl_trans_write_prph(fwrt->trans, address + offset, value);874}875iwl_trans_release_nic_access(fwrt->trans);876break;877}878case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_MEMORY: {879for (count = 0; count < len; count++) {880address = le32_to_cpu(config_list->addr_val[count].address);881value = le32_to_cpu(config_list->addr_val[count].value);882iwl_trans_write_mem32(fwrt->trans, address + offset, value);883IWL_DEBUG_FW(fwrt, "WRT: DEV_MEM: count %u, add: %u val: %u\n",884count, address, value);885}886break;887}888case IWL_FW_INI_CONFIG_SET_TYPE_CSR: {889for (count = 0; count < len; count++) {890address = le32_to_cpu(config_list->addr_val[count].address);891value = le32_to_cpu(config_list->addr_val[count].value);892iwl_write32(fwrt->trans, address + offset, value);893IWL_DEBUG_FW(fwrt, "WRT: CSR: count %u, add: %u val: %u\n",894count, address, value);895}896break;897}898case IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: {899struct iwl_dbgc1_info dram_info = {};900struct iwl_dram_data *frags = &fwrt->trans->dbg.fw_mon_ini[1].frags[0];901__le64 dram_base_addr;902__le32 dram_size;903u64 dram_addr;904u32 ret;905906if (!frags)907break;908909dram_base_addr = cpu_to_le64(frags->physical);910dram_size = cpu_to_le32(frags->size);911dram_addr = le64_to_cpu(dram_base_addr);912913IWL_DEBUG_FW(fwrt, "WRT: dram_base_addr 0x%016llx, dram_size 0x%x\n",914dram_base_addr, dram_size);915IWL_DEBUG_FW(fwrt, "WRT: config_list->addr_offset: %u\n",916le32_to_cpu(config_list->addr_offset));917for (count = 0; count < len; count++) {918address = le32_to_cpu(config_list->addr_val[count].address);919dram_info.dbgc1_add_lsb =920cpu_to_le32((dram_addr & 0x00000000FFFFFFFFULL) + 0x400);921dram_info.dbgc1_add_msb =922cpu_to_le32((dram_addr & 0xFFFFFFFF00000000ULL) >> 32);923dram_info.dbgc1_size = cpu_to_le32(le32_to_cpu(dram_size) - 0x400);924ret = iwl_trans_write_mem(fwrt->trans,925address + offset, &dram_info, 4);926if (ret) {927IWL_ERR(fwrt, "Failed to write dram_info to HW_SMEM\n");928break;929}930}931break;932}933case IWL_FW_INI_CONFIG_SET_TYPE_PERIPH_SCRATCH_HWM: {934u32 debug_token_config =935le32_to_cpu(config_list->addr_val[0].value);936937IWL_DEBUG_FW(fwrt, "WRT: Setting HWM debug token config: %u\n",938debug_token_config);939fwrt->trans->dbg.ucode_preset = debug_token_config;940break;941}942default:943break;944}945}946}947948static void iwl_dbg_tlv_periodic_trig_handler(struct timer_list *t)949{950struct iwl_dbg_tlv_timer_node *timer_node =951timer_container_of(timer_node, t, timer);952struct iwl_fwrt_dump_data dump_data = {953.trig = (void *)timer_node->tlv->data,954};955int ret;956957ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data, false);958if (!ret || ret == -EBUSY) {959u32 occur = le32_to_cpu(dump_data.trig->occurrences);960u32 collect_interval = le32_to_cpu(dump_data.trig->data[0]);961962if (!occur)963return;964965mod_timer(t, jiffies + msecs_to_jiffies(collect_interval));966}967}968969static void iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime *fwrt)970{971struct iwl_dbg_tlv_node *node;972struct list_head *trig_list =973&fwrt->trans->dbg.time_point[IWL_FW_INI_TIME_POINT_PERIODIC].active_trig_list;974975list_for_each_entry(node, trig_list, list) {976struct iwl_fw_ini_trigger_tlv *trig = (void *)node->tlv.data;977struct iwl_dbg_tlv_timer_node *timer_node;978u32 occur = le32_to_cpu(trig->occurrences), collect_interval;979u32 min_interval = 100;980981if (!occur)982continue;983984/* make sure there is at least one dword of data for the985* interval value986*/987if (le32_to_cpu(node->tlv.length) <988sizeof(*trig) + sizeof(__le32)) {989IWL_ERR(fwrt,990"WRT: Invalid periodic trigger data was not given\n");991continue;992}993994if (le32_to_cpu(trig->data[0]) < min_interval) {995IWL_WARN(fwrt,996"WRT: Override min interval from %u to %u msec\n",997le32_to_cpu(trig->data[0]), min_interval);998trig->data[0] = cpu_to_le32(min_interval);999}10001001collect_interval = le32_to_cpu(trig->data[0]);10021003timer_node = kzalloc(sizeof(*timer_node), GFP_KERNEL);1004if (!timer_node) {1005IWL_ERR(fwrt,1006"WRT: Failed to allocate periodic trigger\n");1007continue;1008}10091010timer_node->fwrt = fwrt;1011timer_node->tlv = &node->tlv;1012timer_setup(&timer_node->timer,1013iwl_dbg_tlv_periodic_trig_handler, 0);10141015list_add_tail(&timer_node->list,1016&fwrt->trans->dbg.periodic_trig_list);10171018IWL_DEBUG_FW(fwrt, "WRT: Enabling periodic trigger\n");10191020mod_timer(&timer_node->timer,1021jiffies + msecs_to_jiffies(collect_interval));1022}1023}10241025static bool is_trig_data_contained(const struct iwl_ucode_tlv *new,1026const struct iwl_ucode_tlv *old)1027{1028const struct iwl_fw_ini_trigger_tlv *new_trig = (const void *)new->data;1029const struct iwl_fw_ini_trigger_tlv *old_trig = (const void *)old->data;1030const __le32 *new_data = new_trig->data, *old_data = old_trig->data;1031u32 new_dwords_num = iwl_tlv_array_len(new, new_trig, data);1032u32 old_dwords_num = iwl_tlv_array_len(old, old_trig, data);1033int i, j;10341035for (i = 0; i < new_dwords_num; i++) {1036bool match = false;10371038for (j = 0; j < old_dwords_num; j++) {1039if (new_data[i] == old_data[j]) {1040match = true;1041break;1042}1043}1044if (!match)1045return false;1046}10471048return true;1049}10501051static int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime *fwrt,1052struct iwl_ucode_tlv *trig_tlv,1053struct iwl_dbg_tlv_node *node)1054{1055struct iwl_ucode_tlv *node_tlv = &node->tlv;1056struct iwl_fw_ini_trigger_tlv *node_trig = (void *)node_tlv->data;1057struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;1058u32 policy = le32_to_cpu(trig->apply_policy);1059u32 size = le32_to_cpu(trig_tlv->length);1060u32 trig_data_len = size - sizeof(*trig);1061u32 offset = 0;10621063if (!(policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA)) {1064u32 data_len = le32_to_cpu(node_tlv->length) -1065sizeof(*node_trig);10661067IWL_DEBUG_FW(fwrt,1068"WRT: Appending trigger data (time point %u)\n",1069le32_to_cpu(trig->time_point));10701071offset += data_len;1072size += data_len;1073} else {1074IWL_DEBUG_FW(fwrt,1075"WRT: Overriding trigger data (time point %u)\n",1076le32_to_cpu(trig->time_point));1077}10781079if (size != le32_to_cpu(node_tlv->length)) {1080struct list_head *prev = node->list.prev;1081struct iwl_dbg_tlv_node *tmp;10821083list_del(&node->list);10841085tmp = krealloc(node, sizeof(*node) + size, GFP_KERNEL);1086if (!tmp) {1087IWL_WARN(fwrt,1088"WRT: No memory to override trigger (time point %u)\n",1089le32_to_cpu(trig->time_point));10901091list_add(&node->list, prev);10921093return -ENOMEM;1094}10951096list_add(&tmp->list, prev);1097node_tlv = &tmp->tlv;1098node_trig = (void *)node_tlv->data;1099}11001101memcpy((u8 *)node_trig->data + offset, trig->data, trig_data_len);1102node_tlv->length = cpu_to_le32(size);11031104if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG) {1105IWL_DEBUG_FW(fwrt,1106"WRT: Overriding trigger configuration (time point %u)\n",1107le32_to_cpu(trig->time_point));11081109/* the first 11 dwords are configuration related */1110memcpy(node_trig, trig, sizeof(__le32) * 11);1111}11121113if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS) {1114IWL_DEBUG_FW(fwrt,1115"WRT: Overriding trigger regions (time point %u)\n",1116le32_to_cpu(trig->time_point));11171118node_trig->regions_mask = trig->regions_mask;1119} else {1120IWL_DEBUG_FW(fwrt,1121"WRT: Appending trigger regions (time point %u)\n",1122le32_to_cpu(trig->time_point));11231124node_trig->regions_mask |= trig->regions_mask;1125}11261127return 0;1128}11291130static int1131iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime *fwrt,1132struct list_head *trig_list,1133struct iwl_ucode_tlv *trig_tlv)1134{1135struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;1136struct iwl_dbg_tlv_node *node, *match = NULL;1137u32 policy = le32_to_cpu(trig->apply_policy);11381139list_for_each_entry(node, trig_list, list) {1140if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT))1141break;11421143if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_DATA) ||1144is_trig_data_contained(trig_tlv, &node->tlv)) {1145match = node;1146break;1147}1148}11491150if (!match) {1151IWL_DEBUG_FW(fwrt, "WRT: Enabling trigger (time point %u)\n",1152le32_to_cpu(trig->time_point));1153if (!iwl_dbg_tlv_add(trig_tlv, trig_list))1154return -ENOMEM;1155return 0;1156}11571158return iwl_dbg_tlv_override_trig_node(fwrt, trig_tlv, match);1159}11601161static void1162iwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime *fwrt,1163struct iwl_dbg_tlv_time_point_data *tp)1164{1165struct iwl_dbg_tlv_node *node;1166struct list_head *trig_list = &tp->trig_list;1167struct list_head *active_trig_list = &tp->active_trig_list;11681169list_for_each_entry(node, trig_list, list) {1170struct iwl_ucode_tlv *tlv = &node->tlv;11711172iwl_dbg_tlv_add_active_trigger(fwrt, active_trig_list, tlv);1173}1174}11751176static bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime *fwrt,1177struct iwl_fwrt_dump_data *dump_data,1178union iwl_dbg_tlv_tp_data *tp_data,1179u32 trig_data)1180{1181struct iwl_rx_packet *pkt = tp_data->fw_pkt;1182struct iwl_cmd_header *wanted_hdr = (void *)&trig_data;11831184if (pkt && (pkt->hdr.cmd == wanted_hdr->cmd &&1185pkt->hdr.group_id == wanted_hdr->group_id)) {1186struct iwl_rx_packet *fw_pkt =1187kmemdup(pkt,1188sizeof(*pkt) + iwl_rx_packet_payload_len(pkt),1189GFP_ATOMIC);11901191if (!fw_pkt)1192return false;11931194dump_data->fw_pkt = fw_pkt;11951196return true;1197}11981199return false;1200}12011202static int1203iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,1204struct list_head *active_trig_list,1205union iwl_dbg_tlv_tp_data *tp_data,1206bool (*data_check)(struct iwl_fw_runtime *fwrt,1207struct iwl_fwrt_dump_data *dump_data,1208union iwl_dbg_tlv_tp_data *tp_data,1209u32 trig_data))1210{1211struct iwl_dbg_tlv_node *node;12121213list_for_each_entry(node, active_trig_list, list) {1214struct iwl_fwrt_dump_data dump_data = {1215.trig = (void *)node->tlv.data,1216};1217u32 num_data = iwl_tlv_array_len(&node->tlv, dump_data.trig,1218data);1219int ret, i;1220u32 tp = le32_to_cpu(dump_data.trig->time_point);122112221223if (!num_data) {1224ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);1225if (ret)1226return ret;1227}12281229for (i = 0; i < num_data; i++) {1230if (!data_check ||1231data_check(fwrt, &dump_data, tp_data,1232le32_to_cpu(dump_data.trig->data[i]))) {1233ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);1234if (ret)1235return ret;12361237break;1238}1239}12401241fwrt->trans->dbg.restart_required = false;12421243if (fwrt->trans->mac_cfg->device_family ==1244IWL_DEVICE_FAMILY_9000) {1245fwrt->trans->dbg.restart_required = true;1246} else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT &&1247fwrt->trans->dbg.last_tp_resetfw ==1248IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {1249fwrt->trans->dbg.restart_required = false;1250fwrt->trans->dbg.last_tp_resetfw = 0xFF;1251} else if (le32_to_cpu(dump_data.trig->reset_fw) ==1252IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) {1253fwrt->trans->dbg.restart_required = true;1254} else if (le32_to_cpu(dump_data.trig->reset_fw) ==1255IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {1256fwrt->trans->dbg.restart_required = false;1257fwrt->trans->dbg.last_tp_resetfw =1258le32_to_cpu(dump_data.trig->reset_fw);1259} else if (le32_to_cpu(dump_data.trig->reset_fw) ==1260IWL_FW_INI_RESET_FW_MODE_NOTHING) {1261/* nothing */1262} else {1263IWL_ERR(fwrt, "WRT: wrong resetfw %d\n",1264le32_to_cpu(dump_data.trig->reset_fw));1265}1266}1267return 0;1268}12691270void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt)1271{1272enum iwl_fw_ini_buffer_location *ini_dest = &fwrt->trans->dbg.ini_dest;1273int ret, i;1274u32 failed_alloc = 0;12751276if (*ini_dest == IWL_FW_INI_LOCATION_INVALID) {1277IWL_DEBUG_FW(fwrt,1278"WRT: Generating active triggers list, domain 0x%x\n",1279fwrt->trans->dbg.domains_bitmap);12801281for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.time_point); i++) {1282struct iwl_dbg_tlv_time_point_data *tp =1283&fwrt->trans->dbg.time_point[i];12841285iwl_dbg_tlv_gen_active_trig_list(fwrt, tp);1286}1287} else if (*ini_dest != IWL_FW_INI_LOCATION_DRAM_PATH) {1288/* For DRAM, go through the loop below to clear all the buffers1289* properly on restart, otherwise garbage may be left there and1290* leak into new debug dumps.1291*/1292return;1293}12941295*ini_dest = IWL_FW_INI_LOCATION_INVALID;1296for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {1297struct iwl_fw_ini_allocation_tlv *fw_mon_cfg =1298&fwrt->trans->dbg.fw_mon_cfg[i];1299u32 dest = le32_to_cpu(fw_mon_cfg->buf_location);13001301if (dest == IWL_FW_INI_LOCATION_INVALID) {1302failed_alloc |= BIT(i);1303continue;1304}13051306if (*ini_dest == IWL_FW_INI_LOCATION_INVALID)1307*ini_dest = dest;13081309if (dest != *ini_dest)1310continue;13111312ret = iwl_dbg_tlv_alloc_fragments(fwrt, i);13131314if (ret) {1315IWL_WARN(fwrt,1316"WRT: Failed to allocate DRAM buffer for allocation id %d, ret=%d\n",1317i, ret);1318failed_alloc |= BIT(i);1319}1320}13211322if (!failed_alloc)1323return;13241325for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions) && failed_alloc; i++) {1326struct iwl_fw_ini_region_tlv *reg;1327struct iwl_ucode_tlv **active_reg =1328&fwrt->trans->dbg.active_regions[i];1329u32 reg_type;13301331if (!*active_reg) {1332fwrt->trans->dbg.unsupported_region_msk |= BIT(i);1333continue;1334}13351336reg = (void *)(*active_reg)->data;1337reg_type = reg->type;13381339if (reg_type != IWL_FW_INI_REGION_DRAM_BUFFER ||1340!(BIT(le32_to_cpu(reg->dram_alloc_id)) & failed_alloc))1341continue;13421343IWL_DEBUG_FW(fwrt,1344"WRT: removing allocation id %d from region id %d\n",1345le32_to_cpu(reg->dram_alloc_id), i);13461347failed_alloc &= ~BIT(le32_to_cpu(reg->dram_alloc_id));1348fwrt->trans->dbg.unsupported_region_msk |= BIT(i);13491350kfree(*active_reg);1351*active_reg = NULL;1352}1353}13541355void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,1356enum iwl_fw_ini_time_point tp_id,1357union iwl_dbg_tlv_tp_data *tp_data,1358bool sync)1359{1360struct list_head *hcmd_list, *trig_list, *conf_list;13611362if (!iwl_trans_dbg_ini_valid(fwrt->trans) ||1363tp_id == IWL_FW_INI_TIME_POINT_INVALID ||1364tp_id >= IWL_FW_INI_TIME_POINT_NUM)1365return;13661367hcmd_list = &fwrt->trans->dbg.time_point[tp_id].hcmd_list;1368trig_list = &fwrt->trans->dbg.time_point[tp_id].active_trig_list;1369conf_list = &fwrt->trans->dbg.time_point[tp_id].config_list;13701371switch (tp_id) {1372case IWL_FW_INI_TIME_POINT_EARLY:1373iwl_dbg_tlv_init_cfg(fwrt);1374iwl_dbg_tlv_update_drams(fwrt);1375iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);1376iwl_dbg_tlv_apply_config(fwrt, conf_list);1377break;1378case IWL_FW_INI_TIME_POINT_AFTER_ALIVE:1379iwl_dbg_tlv_apply_buffers(fwrt);1380iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1381iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);1382iwl_dbg_tlv_apply_config(fwrt, conf_list);1383break;1384case IWL_FW_INI_TIME_POINT_PERIODIC:1385iwl_dbg_tlv_set_periodic_trigs(fwrt);1386iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1387break;1388case IWL_FW_INI_TIME_POINT_FW_RSP_OR_NOTIF:1389case IWL_FW_INI_TIME_POINT_MISSED_BEACONS:1390case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION:1391iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1392iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data,1393iwl_dbg_tlv_check_fw_pkt);1394iwl_dbg_tlv_apply_config(fwrt, conf_list);1395break;1396default:1397iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);1398iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);1399iwl_dbg_tlv_apply_config(fwrt, conf_list);1400break;1401}1402}1403IWL_EXPORT_SYMBOL(_iwl_dbg_tlv_time_point);140414051406