123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389 |
- // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
- /*
- * Copyright (C) 2018-2022 Intel Corporation
- */
- #include <linux/firmware.h>
- #include "iwl-drv.h"
- #include "iwl-trans.h"
- #include "iwl-dbg-tlv.h"
- #include "fw/dbg.h"
- #include "fw/runtime.h"
- /**
- * enum iwl_dbg_tlv_type - debug TLV types
- * @IWL_DBG_TLV_TYPE_DEBUG_INFO: debug info TLV
- * @IWL_DBG_TLV_TYPE_BUF_ALLOC: buffer allocation TLV
- * @IWL_DBG_TLV_TYPE_HCMD: host command TLV
- * @IWL_DBG_TLV_TYPE_REGION: region TLV
- * @IWL_DBG_TLV_TYPE_TRIGGER: trigger TLV
- * @IWL_DBG_TLV_TYPE_CONF_SET: conf set TLV
- * @IWL_DBG_TLV_TYPE_NUM: number of debug TLVs
- */
- enum iwl_dbg_tlv_type {
- IWL_DBG_TLV_TYPE_DEBUG_INFO =
- IWL_UCODE_TLV_TYPE_DEBUG_INFO - IWL_UCODE_TLV_DEBUG_BASE,
- IWL_DBG_TLV_TYPE_BUF_ALLOC,
- IWL_DBG_TLV_TYPE_HCMD,
- IWL_DBG_TLV_TYPE_REGION,
- IWL_DBG_TLV_TYPE_TRIGGER,
- IWL_DBG_TLV_TYPE_CONF_SET,
- IWL_DBG_TLV_TYPE_NUM,
- };
- /**
- * struct iwl_dbg_tlv_ver_data - debug TLV version struct
- * @min_ver: min version supported
- * @max_ver: max version supported
- */
- struct iwl_dbg_tlv_ver_data {
- int min_ver;
- int max_ver;
- };
- /**
- * struct iwl_dbg_tlv_timer_node - timer node struct
- * @list: list of &struct iwl_dbg_tlv_timer_node
- * @timer: timer
- * @fwrt: &struct iwl_fw_runtime
- * @tlv: TLV attach to the timer node
- */
- struct iwl_dbg_tlv_timer_node {
- struct list_head list;
- struct timer_list timer;
- struct iwl_fw_runtime *fwrt;
- struct iwl_ucode_tlv *tlv;
- };
- static const struct iwl_dbg_tlv_ver_data
- dbg_ver_table[IWL_DBG_TLV_TYPE_NUM] = {
- [IWL_DBG_TLV_TYPE_DEBUG_INFO] = {.min_ver = 1, .max_ver = 1,},
- [IWL_DBG_TLV_TYPE_BUF_ALLOC] = {.min_ver = 1, .max_ver = 1,},
- [IWL_DBG_TLV_TYPE_HCMD] = {.min_ver = 1, .max_ver = 1,},
- [IWL_DBG_TLV_TYPE_REGION] = {.min_ver = 1, .max_ver = 3,},
- [IWL_DBG_TLV_TYPE_TRIGGER] = {.min_ver = 1, .max_ver = 1,},
- [IWL_DBG_TLV_TYPE_CONF_SET] = {.min_ver = 1, .max_ver = 1,},
- };
- static int iwl_dbg_tlv_add(const struct iwl_ucode_tlv *tlv,
- struct list_head *list)
- {
- u32 len = le32_to_cpu(tlv->length);
- struct iwl_dbg_tlv_node *node;
- node = kzalloc(sizeof(*node) + len, GFP_KERNEL);
- if (!node)
- return -ENOMEM;
- memcpy(&node->tlv, tlv, sizeof(node->tlv));
- memcpy(node->tlv.data, tlv->data, len);
- list_add_tail(&node->list, list);
- return 0;
- }
- static bool iwl_dbg_tlv_ver_support(const struct iwl_ucode_tlv *tlv)
- {
- const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];
- u32 type = le32_to_cpu(tlv->type);
- u32 tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;
- u32 ver = le32_to_cpu(hdr->version);
- if (ver < dbg_ver_table[tlv_idx].min_ver ||
- ver > dbg_ver_table[tlv_idx].max_ver)
- return false;
- return true;
- }
- static int iwl_dbg_tlv_alloc_debug_info(struct iwl_trans *trans,
- const struct iwl_ucode_tlv *tlv)
- {
- const struct iwl_fw_ini_debug_info_tlv *debug_info = (const void *)tlv->data;
- if (le32_to_cpu(tlv->length) != sizeof(*debug_info))
- return -EINVAL;
- IWL_DEBUG_FW(trans, "WRT: Loading debug cfg: %s\n",
- debug_info->debug_cfg_name);
- return iwl_dbg_tlv_add(tlv, &trans->dbg.debug_info_tlv_list);
- }
- static int iwl_dbg_tlv_alloc_buf_alloc(struct iwl_trans *trans,
- const struct iwl_ucode_tlv *tlv)
- {
- const struct iwl_fw_ini_allocation_tlv *alloc = (const void *)tlv->data;
- u32 buf_location;
- u32 alloc_id;
- if (le32_to_cpu(tlv->length) != sizeof(*alloc))
- return -EINVAL;
- buf_location = le32_to_cpu(alloc->buf_location);
- alloc_id = le32_to_cpu(alloc->alloc_id);
- if (buf_location == IWL_FW_INI_LOCATION_INVALID ||
- buf_location >= IWL_FW_INI_LOCATION_NUM)
- goto err;
- if (alloc_id == IWL_FW_INI_ALLOCATION_INVALID ||
- alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
- goto err;
- if (buf_location == IWL_FW_INI_LOCATION_NPK_PATH &&
- alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
- goto err;
- if (buf_location == IWL_FW_INI_LOCATION_SRAM_PATH &&
- alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
- goto err;
- if (buf_location == IWL_FW_INI_LOCATION_DRAM_PATH &&
- alloc->req_size == 0) {
- IWL_ERR(trans, "WRT: Invalid DRAM buffer allocation requested size (0)\n");
- return -EINVAL;
- }
- trans->dbg.fw_mon_cfg[alloc_id] = *alloc;
- return 0;
- err:
- IWL_ERR(trans,
- "WRT: Invalid allocation id %u and/or location id %u for allocation TLV\n",
- alloc_id, buf_location);
- return -EINVAL;
- }
- static int iwl_dbg_tlv_alloc_hcmd(struct iwl_trans *trans,
- const struct iwl_ucode_tlv *tlv)
- {
- const struct iwl_fw_ini_hcmd_tlv *hcmd = (const void *)tlv->data;
- u32 tp = le32_to_cpu(hcmd->time_point);
- if (le32_to_cpu(tlv->length) <= sizeof(*hcmd))
- return -EINVAL;
- /* Host commands can not be sent in early time point since the FW
- * is not ready
- */
- if (tp == IWL_FW_INI_TIME_POINT_INVALID ||
- tp >= IWL_FW_INI_TIME_POINT_NUM ||
- tp == IWL_FW_INI_TIME_POINT_EARLY) {
- IWL_ERR(trans,
- "WRT: Invalid time point %u for host command TLV\n",
- tp);
- return -EINVAL;
- }
- return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].hcmd_list);
- }
- static int iwl_dbg_tlv_alloc_region(struct iwl_trans *trans,
- const struct iwl_ucode_tlv *tlv)
- {
- const struct iwl_fw_ini_region_tlv *reg = (const void *)tlv->data;
- struct iwl_ucode_tlv **active_reg;
- u32 id = le32_to_cpu(reg->id);
- u8 type = reg->type;
- u32 tlv_len = sizeof(*tlv) + le32_to_cpu(tlv->length);
- /*
- * The higher part of the ID from version 2 is debug policy.
- * The id will be only lsb 16 bits, so mask it out.
- */
- if (le32_to_cpu(reg->hdr.version) >= 2)
- id &= IWL_FW_INI_REGION_ID_MASK;
- if (le32_to_cpu(tlv->length) < sizeof(*reg))
- return -EINVAL;
- /* for safe use of a string from FW, limit it to IWL_FW_INI_MAX_NAME */
- IWL_DEBUG_FW(trans, "WRT: parsing region: %.*s\n",
- IWL_FW_INI_MAX_NAME, reg->name);
- if (id >= IWL_FW_INI_MAX_REGION_ID) {
- IWL_ERR(trans, "WRT: Invalid region id %u\n", id);
- return -EINVAL;
- }
- if (type <= IWL_FW_INI_REGION_INVALID ||
- type >= IWL_FW_INI_REGION_NUM) {
- IWL_ERR(trans, "WRT: Invalid region type %u\n", type);
- return -EINVAL;
- }
- if (type == IWL_FW_INI_REGION_PCI_IOSF_CONFIG &&
- !trans->ops->read_config32) {
- IWL_ERR(trans, "WRT: Unsupported region type %u\n", type);
- return -EOPNOTSUPP;
- }
- if (type == IWL_FW_INI_REGION_INTERNAL_BUFFER) {
- trans->dbg.imr_data.sram_addr =
- le32_to_cpu(reg->internal_buffer.base_addr);
- trans->dbg.imr_data.sram_size =
- le32_to_cpu(reg->internal_buffer.size);
- }
- active_reg = &trans->dbg.active_regions[id];
- if (*active_reg) {
- IWL_WARN(trans, "WRT: Overriding region id %u\n", id);
- kfree(*active_reg);
- }
- *active_reg = kmemdup(tlv, tlv_len, GFP_KERNEL);
- if (!*active_reg)
- return -ENOMEM;
- IWL_DEBUG_FW(trans, "WRT: Enabling region id %u type %u\n", id, type);
- return 0;
- }
- static int iwl_dbg_tlv_alloc_trigger(struct iwl_trans *trans,
- const struct iwl_ucode_tlv *tlv)
- {
- const struct iwl_fw_ini_trigger_tlv *trig = (const void *)tlv->data;
- struct iwl_fw_ini_trigger_tlv *dup_trig;
- u32 tp = le32_to_cpu(trig->time_point);
- u32 rf = le32_to_cpu(trig->reset_fw);
- struct iwl_ucode_tlv *dup = NULL;
- int ret;
- if (le32_to_cpu(tlv->length) < sizeof(*trig))
- return -EINVAL;
- if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||
- tp >= IWL_FW_INI_TIME_POINT_NUM) {
- IWL_ERR(trans,
- "WRT: Invalid time point %u for trigger TLV\n",
- tp);
- return -EINVAL;
- }
- IWL_DEBUG_FW(trans,
- "WRT: time point %u for trigger TLV with reset_fw %u\n",
- tp, rf);
- trans->dbg.last_tp_resetfw = 0xFF;
- if (!le32_to_cpu(trig->occurrences)) {
- dup = kmemdup(tlv, sizeof(*tlv) + le32_to_cpu(tlv->length),
- GFP_KERNEL);
- if (!dup)
- return -ENOMEM;
- dup_trig = (void *)dup->data;
- dup_trig->occurrences = cpu_to_le32(-1);
- tlv = dup;
- }
- ret = iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].trig_list);
- kfree(dup);
- return ret;
- }
- static int iwl_dbg_tlv_config_set(struct iwl_trans *trans,
- const struct iwl_ucode_tlv *tlv)
- {
- const struct iwl_fw_ini_conf_set_tlv *conf_set = (const void *)tlv->data;
- u32 tp = le32_to_cpu(conf_set->time_point);
- u32 type = le32_to_cpu(conf_set->set_type);
- if (tp <= IWL_FW_INI_TIME_POINT_INVALID ||
- tp >= IWL_FW_INI_TIME_POINT_NUM) {
- IWL_DEBUG_FW(trans,
- "WRT: Invalid time point %u for config set TLV\n", tp);
- return -EINVAL;
- }
- if (type <= IWL_FW_INI_CONFIG_SET_TYPE_INVALID ||
- type >= IWL_FW_INI_CONFIG_SET_TYPE_MAX_NUM) {
- IWL_DEBUG_FW(trans,
- "WRT: Invalid config set type %u for config set TLV\n", type);
- return -EINVAL;
- }
- return iwl_dbg_tlv_add(tlv, &trans->dbg.time_point[tp].config_list);
- }
- static int (*dbg_tlv_alloc[])(struct iwl_trans *trans,
- const struct iwl_ucode_tlv *tlv) = {
- [IWL_DBG_TLV_TYPE_DEBUG_INFO] = iwl_dbg_tlv_alloc_debug_info,
- [IWL_DBG_TLV_TYPE_BUF_ALLOC] = iwl_dbg_tlv_alloc_buf_alloc,
- [IWL_DBG_TLV_TYPE_HCMD] = iwl_dbg_tlv_alloc_hcmd,
- [IWL_DBG_TLV_TYPE_REGION] = iwl_dbg_tlv_alloc_region,
- [IWL_DBG_TLV_TYPE_TRIGGER] = iwl_dbg_tlv_alloc_trigger,
- [IWL_DBG_TLV_TYPE_CONF_SET] = iwl_dbg_tlv_config_set,
- };
- void iwl_dbg_tlv_alloc(struct iwl_trans *trans, const struct iwl_ucode_tlv *tlv,
- bool ext)
- {
- enum iwl_ini_cfg_state *cfg_state = ext ?
- &trans->dbg.external_ini_cfg : &trans->dbg.internal_ini_cfg;
- const struct iwl_fw_ini_header *hdr = (const void *)&tlv->data[0];
- u32 type;
- u32 tlv_idx;
- u32 domain;
- int ret;
- if (le32_to_cpu(tlv->length) < sizeof(*hdr))
- return;
- type = le32_to_cpu(tlv->type);
- tlv_idx = type - IWL_UCODE_TLV_DEBUG_BASE;
- domain = le32_to_cpu(hdr->domain);
- if (domain != IWL_FW_INI_DOMAIN_ALWAYS_ON &&
- !(domain & trans->dbg.domains_bitmap)) {
- IWL_DEBUG_FW(trans,
- "WRT: Skipping TLV with disabled domain 0x%0x (0x%0x)\n",
- domain, trans->dbg.domains_bitmap);
- return;
- }
- if (tlv_idx >= ARRAY_SIZE(dbg_tlv_alloc) || !dbg_tlv_alloc[tlv_idx]) {
- IWL_ERR(trans, "WRT: Unsupported TLV type 0x%x\n", type);
- goto out_err;
- }
- if (!iwl_dbg_tlv_ver_support(tlv)) {
- IWL_ERR(trans, "WRT: Unsupported TLV 0x%x version %u\n", type,
- le32_to_cpu(hdr->version));
- goto out_err;
- }
- ret = dbg_tlv_alloc[tlv_idx](trans, tlv);
- if (ret) {
- IWL_ERR(trans,
- "WRT: Failed to allocate TLV 0x%x, ret %d, (ext=%d)\n",
- type, ret, ext);
- goto out_err;
- }
- if (*cfg_state == IWL_INI_CFG_STATE_NOT_LOADED)
- *cfg_state = IWL_INI_CFG_STATE_LOADED;
- return;
- out_err:
- *cfg_state = IWL_INI_CFG_STATE_CORRUPTED;
- }
- void iwl_dbg_tlv_del_timers(struct iwl_trans *trans)
- {
- struct list_head *timer_list = &trans->dbg.periodic_trig_list;
- struct iwl_dbg_tlv_timer_node *node, *tmp;
- list_for_each_entry_safe(node, tmp, timer_list, list) {
- del_timer_sync(&node->timer);
- list_del(&node->list);
- kfree(node);
- }
- }
- IWL_EXPORT_SYMBOL(iwl_dbg_tlv_del_timers);
- static void iwl_dbg_tlv_fragments_free(struct iwl_trans *trans,
- enum iwl_fw_ini_allocation_id alloc_id)
- {
- struct iwl_fw_mon *fw_mon;
- int i;
- if (alloc_id <= IWL_FW_INI_ALLOCATION_INVALID ||
- alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
- return;
- fw_mon = &trans->dbg.fw_mon_ini[alloc_id];
- for (i = 0; i < fw_mon->num_frags; i++) {
- struct iwl_dram_data *frag = &fw_mon->frags[i];
- dma_free_coherent(trans->dev, frag->size, frag->block,
- frag->physical);
- frag->physical = 0;
- frag->block = NULL;
- frag->size = 0;
- }
- kfree(fw_mon->frags);
- fw_mon->frags = NULL;
- fw_mon->num_frags = 0;
- }
- void iwl_dbg_tlv_free(struct iwl_trans *trans)
- {
- struct iwl_dbg_tlv_node *tlv_node, *tlv_node_tmp;
- int i;
- iwl_dbg_tlv_del_timers(trans);
- for (i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) {
- struct iwl_ucode_tlv **active_reg =
- &trans->dbg.active_regions[i];
- kfree(*active_reg);
- *active_reg = NULL;
- }
- list_for_each_entry_safe(tlv_node, tlv_node_tmp,
- &trans->dbg.debug_info_tlv_list, list) {
- list_del(&tlv_node->list);
- kfree(tlv_node);
- }
- for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {
- struct iwl_dbg_tlv_time_point_data *tp =
- &trans->dbg.time_point[i];
- list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->trig_list,
- list) {
- list_del(&tlv_node->list);
- kfree(tlv_node);
- }
- list_for_each_entry_safe(tlv_node, tlv_node_tmp, &tp->hcmd_list,
- list) {
- list_del(&tlv_node->list);
- kfree(tlv_node);
- }
- list_for_each_entry_safe(tlv_node, tlv_node_tmp,
- &tp->active_trig_list, list) {
- list_del(&tlv_node->list);
- kfree(tlv_node);
- }
- list_for_each_entry_safe(tlv_node, tlv_node_tmp,
- &tp->config_list, list) {
- list_del(&tlv_node->list);
- kfree(tlv_node);
- }
- }
- for (i = 0; i < ARRAY_SIZE(trans->dbg.fw_mon_ini); i++)
- iwl_dbg_tlv_fragments_free(trans, i);
- }
- static int iwl_dbg_tlv_parse_bin(struct iwl_trans *trans, const u8 *data,
- size_t len)
- {
- const struct iwl_ucode_tlv *tlv;
- u32 tlv_len;
- while (len >= sizeof(*tlv)) {
- len -= sizeof(*tlv);
- tlv = (const void *)data;
- tlv_len = le32_to_cpu(tlv->length);
- if (len < tlv_len) {
- IWL_ERR(trans, "invalid TLV len: %zd/%u\n",
- len, tlv_len);
- return -EINVAL;
- }
- len -= ALIGN(tlv_len, 4);
- data += sizeof(*tlv) + ALIGN(tlv_len, 4);
- iwl_dbg_tlv_alloc(trans, tlv, true);
- }
- return 0;
- }
- void iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans)
- {
- const struct firmware *fw;
- const char *yoyo_bin = "iwl-debug-yoyo.bin";
- int res;
- if (!iwlwifi_mod_params.enable_ini ||
- trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000)
- return;
- res = firmware_request_nowarn(&fw, yoyo_bin, dev);
- IWL_DEBUG_FW(trans, "%s %s\n", res ? "didn't load" : "loaded", yoyo_bin);
- if (res)
- return;
- iwl_dbg_tlv_parse_bin(trans, fw->data, fw->size);
- release_firmware(fw);
- }
- void iwl_dbg_tlv_init(struct iwl_trans *trans)
- {
- int i;
- INIT_LIST_HEAD(&trans->dbg.debug_info_tlv_list);
- INIT_LIST_HEAD(&trans->dbg.periodic_trig_list);
- for (i = 0; i < ARRAY_SIZE(trans->dbg.time_point); i++) {
- struct iwl_dbg_tlv_time_point_data *tp =
- &trans->dbg.time_point[i];
- INIT_LIST_HEAD(&tp->trig_list);
- INIT_LIST_HEAD(&tp->hcmd_list);
- INIT_LIST_HEAD(&tp->active_trig_list);
- INIT_LIST_HEAD(&tp->config_list);
- }
- }
- static int iwl_dbg_tlv_alloc_fragment(struct iwl_fw_runtime *fwrt,
- struct iwl_dram_data *frag, u32 pages)
- {
- void *block = NULL;
- dma_addr_t physical;
- if (!frag || frag->size || !pages)
- return -EIO;
- /*
- * We try to allocate as many pages as we can, starting with
- * the requested amount and going down until we can allocate
- * something. Because of DIV_ROUND_UP(), pages will never go
- * down to 0 and stop the loop, so stop when pages reaches 1,
- * which is too small anyway.
- */
- while (pages > 1) {
- block = dma_alloc_coherent(fwrt->dev, pages * PAGE_SIZE,
- &physical,
- GFP_KERNEL | __GFP_NOWARN);
- if (block)
- break;
- IWL_WARN(fwrt, "WRT: Failed to allocate fragment size %lu\n",
- pages * PAGE_SIZE);
- pages = DIV_ROUND_UP(pages, 2);
- }
- if (!block)
- return -ENOMEM;
- frag->physical = physical;
- frag->block = block;
- frag->size = pages * PAGE_SIZE;
- return pages;
- }
- static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt,
- enum iwl_fw_ini_allocation_id alloc_id)
- {
- struct iwl_fw_mon *fw_mon;
- struct iwl_fw_ini_allocation_tlv *fw_mon_cfg;
- u32 num_frags, remain_pages, frag_pages;
- int i;
- if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||
- alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
- return -EIO;
- fw_mon_cfg = &fwrt->trans->dbg.fw_mon_cfg[alloc_id];
- fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
- if (fw_mon->num_frags ||
- fw_mon_cfg->buf_location !=
- cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH))
- return 0;
- num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num);
- if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) {
- if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1)
- return -EIO;
- num_frags = 1;
- }
- remain_pages = DIV_ROUND_UP(le32_to_cpu(fw_mon_cfg->req_size),
- PAGE_SIZE);
- num_frags = min_t(u32, num_frags, BUF_ALLOC_MAX_NUM_FRAGS);
- num_frags = min_t(u32, num_frags, remain_pages);
- frag_pages = DIV_ROUND_UP(remain_pages, num_frags);
- fw_mon->frags = kcalloc(num_frags, sizeof(*fw_mon->frags), GFP_KERNEL);
- if (!fw_mon->frags)
- return -ENOMEM;
- for (i = 0; i < num_frags; i++) {
- int pages = min_t(u32, frag_pages, remain_pages);
- IWL_DEBUG_FW(fwrt,
- "WRT: Allocating DRAM buffer (alloc_id=%u, fragment=%u, size=0x%lx)\n",
- alloc_id, i, pages * PAGE_SIZE);
- pages = iwl_dbg_tlv_alloc_fragment(fwrt, &fw_mon->frags[i],
- pages);
- if (pages < 0) {
- u32 alloc_size = le32_to_cpu(fw_mon_cfg->req_size) -
- (remain_pages * PAGE_SIZE);
- if (alloc_size < le32_to_cpu(fw_mon_cfg->min_size)) {
- iwl_dbg_tlv_fragments_free(fwrt->trans,
- alloc_id);
- return pages;
- }
- break;
- }
- remain_pages -= pages;
- fw_mon->num_frags++;
- }
- return 0;
- }
- static int iwl_dbg_tlv_apply_buffer(struct iwl_fw_runtime *fwrt,
- enum iwl_fw_ini_allocation_id alloc_id)
- {
- struct iwl_fw_mon *fw_mon;
- u32 remain_frags, num_commands;
- int i, fw_mon_idx = 0;
- if (!fw_has_capa(&fwrt->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_DBG_BUF_ALLOC_CMD_SUPP))
- return 0;
- if (alloc_id < IWL_FW_INI_ALLOCATION_INVALID ||
- alloc_id >= IWL_FW_INI_ALLOCATION_NUM)
- return -EIO;
- if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=
- IWL_FW_INI_LOCATION_DRAM_PATH)
- return 0;
- fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
- /* the first fragment of DBGC1 is given to the FW via register
- * or context info
- */
- if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)
- fw_mon_idx++;
- remain_frags = fw_mon->num_frags - fw_mon_idx;
- if (!remain_frags)
- return 0;
- num_commands = DIV_ROUND_UP(remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);
- IWL_DEBUG_FW(fwrt, "WRT: Applying DRAM destination (alloc_id=%u)\n",
- alloc_id);
- for (i = 0; i < num_commands; i++) {
- u32 num_frags = min_t(u32, remain_frags,
- BUF_ALLOC_MAX_NUM_FRAGS);
- struct iwl_buf_alloc_cmd data = {
- .alloc_id = cpu_to_le32(alloc_id),
- .num_frags = cpu_to_le32(num_frags),
- .buf_location =
- cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH),
- };
- struct iwl_host_cmd hcmd = {
- .id = WIDE_ID(DEBUG_GROUP, BUFFER_ALLOCATION),
- .data[0] = &data,
- .len[0] = sizeof(data),
- .flags = CMD_SEND_IN_RFKILL,
- };
- int ret, j;
- for (j = 0; j < num_frags; j++) {
- struct iwl_buf_alloc_frag *frag = &data.frags[j];
- struct iwl_dram_data *fw_mon_frag =
- &fw_mon->frags[fw_mon_idx++];
- frag->addr = cpu_to_le64(fw_mon_frag->physical);
- frag->size = cpu_to_le32(fw_mon_frag->size);
- }
- ret = iwl_trans_send_cmd(fwrt->trans, &hcmd);
- if (ret)
- return ret;
- remain_frags -= num_frags;
- }
- return 0;
- }
- static void iwl_dbg_tlv_apply_buffers(struct iwl_fw_runtime *fwrt)
- {
- int ret, i;
- if (fw_has_capa(&fwrt->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))
- return;
- for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {
- ret = iwl_dbg_tlv_apply_buffer(fwrt, i);
- if (ret)
- IWL_WARN(fwrt,
- "WRT: Failed to apply DRAM buffer for allocation id %d, ret=%d\n",
- i, ret);
- }
- }
- static int iwl_dbg_tlv_update_dram(struct iwl_fw_runtime *fwrt,
- enum iwl_fw_ini_allocation_id alloc_id,
- struct iwl_dram_info *dram_info)
- {
- struct iwl_fw_mon *fw_mon;
- u32 remain_frags, num_frags;
- int j, fw_mon_idx = 0;
- struct iwl_buf_alloc_cmd *data;
- if (le32_to_cpu(fwrt->trans->dbg.fw_mon_cfg[alloc_id].buf_location) !=
- IWL_FW_INI_LOCATION_DRAM_PATH) {
- IWL_DEBUG_FW(fwrt, "DRAM_PATH is not supported alloc_id %u\n", alloc_id);
- return -1;
- }
- fw_mon = &fwrt->trans->dbg.fw_mon_ini[alloc_id];
- /* the first fragment of DBGC1 is given to the FW via register
- * or context info
- */
- if (alloc_id == IWL_FW_INI_ALLOCATION_ID_DBGC1)
- fw_mon_idx++;
- remain_frags = fw_mon->num_frags - fw_mon_idx;
- if (!remain_frags)
- return -1;
- num_frags = min_t(u32, remain_frags, BUF_ALLOC_MAX_NUM_FRAGS);
- data = &dram_info->dram_frags[alloc_id - 1];
- data->alloc_id = cpu_to_le32(alloc_id);
- data->num_frags = cpu_to_le32(num_frags);
- data->buf_location = cpu_to_le32(IWL_FW_INI_LOCATION_DRAM_PATH);
- IWL_DEBUG_FW(fwrt, "WRT: DRAM buffer details alloc_id=%u, num_frags=%u\n",
- cpu_to_le32(alloc_id), cpu_to_le32(num_frags));
- for (j = 0; j < num_frags; j++) {
- struct iwl_buf_alloc_frag *frag = &data->frags[j];
- struct iwl_dram_data *fw_mon_frag = &fw_mon->frags[fw_mon_idx++];
- frag->addr = cpu_to_le64(fw_mon_frag->physical);
- frag->size = cpu_to_le32(fw_mon_frag->size);
- IWL_DEBUG_FW(fwrt, "WRT: DRAM fragment details\n");
- IWL_DEBUG_FW(fwrt, "frag=%u, addr=0x%016llx, size=0x%x)\n",
- j, cpu_to_le64(fw_mon_frag->physical),
- cpu_to_le32(fw_mon_frag->size));
- }
- return 0;
- }
- static void iwl_dbg_tlv_update_drams(struct iwl_fw_runtime *fwrt)
- {
- int ret, i;
- bool dram_alloc = false;
- struct iwl_dram_data *frags =
- &fwrt->trans->dbg.fw_mon_ini[IWL_FW_INI_ALLOCATION_ID_DBGC1].frags[0];
- struct iwl_dram_info *dram_info;
- if (!frags || !frags->block)
- return;
- dram_info = frags->block;
- if (!fw_has_capa(&fwrt->fw->ucode_capa,
- IWL_UCODE_TLV_CAPA_DRAM_FRAG_SUPPORT))
- return;
- dram_info->first_word = cpu_to_le32(DRAM_INFO_FIRST_MAGIC_WORD);
- dram_info->second_word = cpu_to_le32(DRAM_INFO_SECOND_MAGIC_WORD);
- for (i = IWL_FW_INI_ALLOCATION_ID_DBGC1;
- i <= IWL_FW_INI_ALLOCATION_ID_DBGC3; i++) {
- ret = iwl_dbg_tlv_update_dram(fwrt, i, dram_info);
- if (!ret)
- dram_alloc = true;
- else
- IWL_WARN(fwrt,
- "WRT: Failed to set DRAM buffer for alloc id %d, ret=%d\n",
- i, ret);
- }
- if (dram_alloc)
- IWL_DEBUG_FW(fwrt, "block data after %08x\n",
- dram_info->first_word);
- else
- memset(frags->block, 0, sizeof(*dram_info));
- }
- static void iwl_dbg_tlv_send_hcmds(struct iwl_fw_runtime *fwrt,
- struct list_head *hcmd_list)
- {
- struct iwl_dbg_tlv_node *node;
- list_for_each_entry(node, hcmd_list, list) {
- struct iwl_fw_ini_hcmd_tlv *hcmd = (void *)node->tlv.data;
- struct iwl_fw_ini_hcmd *hcmd_data = &hcmd->hcmd;
- u16 hcmd_len = le32_to_cpu(node->tlv.length) - sizeof(*hcmd);
- struct iwl_host_cmd cmd = {
- .id = WIDE_ID(hcmd_data->group, hcmd_data->id),
- .len = { hcmd_len, },
- .data = { hcmd_data->data, },
- };
- iwl_trans_send_cmd(fwrt->trans, &cmd);
- }
- }
- static void iwl_dbg_tlv_apply_config(struct iwl_fw_runtime *fwrt,
- struct list_head *conf_list)
- {
- struct iwl_dbg_tlv_node *node;
- list_for_each_entry(node, conf_list, list) {
- struct iwl_fw_ini_conf_set_tlv *config_list = (void *)node->tlv.data;
- u32 count, address, value;
- u32 len = (le32_to_cpu(node->tlv.length) - sizeof(*config_list)) / 8;
- u32 type = le32_to_cpu(config_list->set_type);
- u32 offset = le32_to_cpu(config_list->addr_offset);
- switch (type) {
- case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_PERIPHERY_MAC: {
- if (!iwl_trans_grab_nic_access(fwrt->trans)) {
- IWL_DEBUG_FW(fwrt, "WRT: failed to get nic access\n");
- IWL_DEBUG_FW(fwrt, "WRT: skipping MAC PERIPHERY config\n");
- continue;
- }
- IWL_DEBUG_FW(fwrt, "WRT: MAC PERIPHERY config len: len %u\n", len);
- for (count = 0; count < len; count++) {
- address = le32_to_cpu(config_list->addr_val[count].address);
- value = le32_to_cpu(config_list->addr_val[count].value);
- iwl_trans_write_prph(fwrt->trans, address + offset, value);
- }
- iwl_trans_release_nic_access(fwrt->trans);
- break;
- }
- case IWL_FW_INI_CONFIG_SET_TYPE_DEVICE_MEMORY: {
- for (count = 0; count < len; count++) {
- address = le32_to_cpu(config_list->addr_val[count].address);
- value = le32_to_cpu(config_list->addr_val[count].value);
- iwl_trans_write_mem32(fwrt->trans, address + offset, value);
- IWL_DEBUG_FW(fwrt, "WRT: DEV_MEM: count %u, add: %u val: %u\n",
- count, address, value);
- }
- break;
- }
- case IWL_FW_INI_CONFIG_SET_TYPE_CSR: {
- for (count = 0; count < len; count++) {
- address = le32_to_cpu(config_list->addr_val[count].address);
- value = le32_to_cpu(config_list->addr_val[count].value);
- iwl_write32(fwrt->trans, address + offset, value);
- IWL_DEBUG_FW(fwrt, "WRT: CSR: count %u, add: %u val: %u\n",
- count, address, value);
- }
- break;
- }
- case IWL_FW_INI_CONFIG_SET_TYPE_DBGC_DRAM_ADDR: {
- struct iwl_dbgc1_info dram_info = {};
- struct iwl_dram_data *frags = &fwrt->trans->dbg.fw_mon_ini[1].frags[0];
- __le64 dram_base_addr;
- __le32 dram_size;
- u64 dram_addr;
- u32 ret;
- if (!frags)
- break;
- dram_base_addr = cpu_to_le64(frags->physical);
- dram_size = cpu_to_le32(frags->size);
- dram_addr = le64_to_cpu(dram_base_addr);
- IWL_DEBUG_FW(fwrt, "WRT: dram_base_addr 0x%016llx, dram_size 0x%x\n",
- dram_base_addr, dram_size);
- IWL_DEBUG_FW(fwrt, "WRT: config_list->addr_offset: %u\n",
- le32_to_cpu(config_list->addr_offset));
- for (count = 0; count < len; count++) {
- address = le32_to_cpu(config_list->addr_val[count].address);
- dram_info.dbgc1_add_lsb =
- cpu_to_le32((dram_addr & 0x00000000FFFFFFFFULL) + 0x400);
- dram_info.dbgc1_add_msb =
- cpu_to_le32((dram_addr & 0xFFFFFFFF00000000ULL) >> 32);
- dram_info.dbgc1_size = cpu_to_le32(le32_to_cpu(dram_size) - 0x400);
- ret = iwl_trans_write_mem(fwrt->trans,
- address + offset, &dram_info, 4);
- if (ret) {
- IWL_ERR(fwrt, "Failed to write dram_info to HW_SMEM\n");
- break;
- }
- }
- break;
- }
- case IWL_FW_INI_CONFIG_SET_TYPE_PERIPH_SCRATCH_HWM: {
- u32 debug_token_config =
- le32_to_cpu(config_list->addr_val[0].value);
- IWL_DEBUG_FW(fwrt, "WRT: Setting HWM debug token config: %u\n",
- debug_token_config);
- fwrt->trans->dbg.ucode_preset = debug_token_config;
- break;
- }
- default:
- break;
- }
- }
- }
- static void iwl_dbg_tlv_periodic_trig_handler(struct timer_list *t)
- {
- struct iwl_dbg_tlv_timer_node *timer_node =
- from_timer(timer_node, t, timer);
- struct iwl_fwrt_dump_data dump_data = {
- .trig = (void *)timer_node->tlv->data,
- };
- int ret;
- ret = iwl_fw_dbg_ini_collect(timer_node->fwrt, &dump_data, false);
- if (!ret || ret == -EBUSY) {
- u32 occur = le32_to_cpu(dump_data.trig->occurrences);
- u32 collect_interval = le32_to_cpu(dump_data.trig->data[0]);
- if (!occur)
- return;
- mod_timer(t, jiffies + msecs_to_jiffies(collect_interval));
- }
- }
- static void iwl_dbg_tlv_set_periodic_trigs(struct iwl_fw_runtime *fwrt)
- {
- struct iwl_dbg_tlv_node *node;
- struct list_head *trig_list =
- &fwrt->trans->dbg.time_point[IWL_FW_INI_TIME_POINT_PERIODIC].active_trig_list;
- list_for_each_entry(node, trig_list, list) {
- struct iwl_fw_ini_trigger_tlv *trig = (void *)node->tlv.data;
- struct iwl_dbg_tlv_timer_node *timer_node;
- u32 occur = le32_to_cpu(trig->occurrences), collect_interval;
- u32 min_interval = 100;
- if (!occur)
- continue;
- /* make sure there is at least one dword of data for the
- * interval value
- */
- if (le32_to_cpu(node->tlv.length) <
- sizeof(*trig) + sizeof(__le32)) {
- IWL_ERR(fwrt,
- "WRT: Invalid periodic trigger data was not given\n");
- continue;
- }
- if (le32_to_cpu(trig->data[0]) < min_interval) {
- IWL_WARN(fwrt,
- "WRT: Override min interval from %u to %u msec\n",
- le32_to_cpu(trig->data[0]), min_interval);
- trig->data[0] = cpu_to_le32(min_interval);
- }
- collect_interval = le32_to_cpu(trig->data[0]);
- timer_node = kzalloc(sizeof(*timer_node), GFP_KERNEL);
- if (!timer_node) {
- IWL_ERR(fwrt,
- "WRT: Failed to allocate periodic trigger\n");
- continue;
- }
- timer_node->fwrt = fwrt;
- timer_node->tlv = &node->tlv;
- timer_setup(&timer_node->timer,
- iwl_dbg_tlv_periodic_trig_handler, 0);
- list_add_tail(&timer_node->list,
- &fwrt->trans->dbg.periodic_trig_list);
- IWL_DEBUG_FW(fwrt, "WRT: Enabling periodic trigger\n");
- mod_timer(&timer_node->timer,
- jiffies + msecs_to_jiffies(collect_interval));
- }
- }
- static bool is_trig_data_contained(const struct iwl_ucode_tlv *new,
- const struct iwl_ucode_tlv *old)
- {
- const struct iwl_fw_ini_trigger_tlv *new_trig = (const void *)new->data;
- const struct iwl_fw_ini_trigger_tlv *old_trig = (const void *)old->data;
- const __le32 *new_data = new_trig->data, *old_data = old_trig->data;
- u32 new_dwords_num = iwl_tlv_array_len(new, new_trig, data);
- u32 old_dwords_num = iwl_tlv_array_len(old, old_trig, data);
- int i, j;
- for (i = 0; i < new_dwords_num; i++) {
- bool match = false;
- for (j = 0; j < old_dwords_num; j++) {
- if (new_data[i] == old_data[j]) {
- match = true;
- break;
- }
- }
- if (!match)
- return false;
- }
- return true;
- }
- static int iwl_dbg_tlv_override_trig_node(struct iwl_fw_runtime *fwrt,
- struct iwl_ucode_tlv *trig_tlv,
- struct iwl_dbg_tlv_node *node)
- {
- struct iwl_ucode_tlv *node_tlv = &node->tlv;
- struct iwl_fw_ini_trigger_tlv *node_trig = (void *)node_tlv->data;
- struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;
- u32 policy = le32_to_cpu(trig->apply_policy);
- u32 size = le32_to_cpu(trig_tlv->length);
- u32 trig_data_len = size - sizeof(*trig);
- u32 offset = 0;
- if (!(policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA)) {
- u32 data_len = le32_to_cpu(node_tlv->length) -
- sizeof(*node_trig);
- IWL_DEBUG_FW(fwrt,
- "WRT: Appending trigger data (time point %u)\n",
- le32_to_cpu(trig->time_point));
- offset += data_len;
- size += data_len;
- } else {
- IWL_DEBUG_FW(fwrt,
- "WRT: Overriding trigger data (time point %u)\n",
- le32_to_cpu(trig->time_point));
- }
- if (size != le32_to_cpu(node_tlv->length)) {
- struct list_head *prev = node->list.prev;
- struct iwl_dbg_tlv_node *tmp;
- list_del(&node->list);
- tmp = krealloc(node, sizeof(*node) + size, GFP_KERNEL);
- if (!tmp) {
- IWL_WARN(fwrt,
- "WRT: No memory to override trigger (time point %u)\n",
- le32_to_cpu(trig->time_point));
- list_add(&node->list, prev);
- return -ENOMEM;
- }
- list_add(&tmp->list, prev);
- node_tlv = &tmp->tlv;
- node_trig = (void *)node_tlv->data;
- }
- memcpy(node_trig->data + offset, trig->data, trig_data_len);
- node_tlv->length = cpu_to_le32(size);
- if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG) {
- IWL_DEBUG_FW(fwrt,
- "WRT: Overriding trigger configuration (time point %u)\n",
- le32_to_cpu(trig->time_point));
- /* the first 11 dwords are configuration related */
- memcpy(node_trig, trig, sizeof(__le32) * 11);
- }
- if (policy & IWL_FW_INI_APPLY_POLICY_OVERRIDE_REGIONS) {
- IWL_DEBUG_FW(fwrt,
- "WRT: Overriding trigger regions (time point %u)\n",
- le32_to_cpu(trig->time_point));
- node_trig->regions_mask = trig->regions_mask;
- } else {
- IWL_DEBUG_FW(fwrt,
- "WRT: Appending trigger regions (time point %u)\n",
- le32_to_cpu(trig->time_point));
- node_trig->regions_mask |= trig->regions_mask;
- }
- return 0;
- }
- static int
- iwl_dbg_tlv_add_active_trigger(struct iwl_fw_runtime *fwrt,
- struct list_head *trig_list,
- struct iwl_ucode_tlv *trig_tlv)
- {
- struct iwl_fw_ini_trigger_tlv *trig = (void *)trig_tlv->data;
- struct iwl_dbg_tlv_node *node, *match = NULL;
- u32 policy = le32_to_cpu(trig->apply_policy);
- list_for_each_entry(node, trig_list, list) {
- if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT))
- break;
- if (!(policy & IWL_FW_INI_APPLY_POLICY_MATCH_DATA) ||
- is_trig_data_contained(trig_tlv, &node->tlv)) {
- match = node;
- break;
- }
- }
- if (!match) {
- IWL_DEBUG_FW(fwrt, "WRT: Enabling trigger (time point %u)\n",
- le32_to_cpu(trig->time_point));
- return iwl_dbg_tlv_add(trig_tlv, trig_list);
- }
- return iwl_dbg_tlv_override_trig_node(fwrt, trig_tlv, match);
- }
- static void
- iwl_dbg_tlv_gen_active_trig_list(struct iwl_fw_runtime *fwrt,
- struct iwl_dbg_tlv_time_point_data *tp)
- {
- struct iwl_dbg_tlv_node *node;
- struct list_head *trig_list = &tp->trig_list;
- struct list_head *active_trig_list = &tp->active_trig_list;
- list_for_each_entry(node, trig_list, list) {
- struct iwl_ucode_tlv *tlv = &node->tlv;
- iwl_dbg_tlv_add_active_trigger(fwrt, active_trig_list, tlv);
- }
- }
- static bool iwl_dbg_tlv_check_fw_pkt(struct iwl_fw_runtime *fwrt,
- struct iwl_fwrt_dump_data *dump_data,
- union iwl_dbg_tlv_tp_data *tp_data,
- u32 trig_data)
- {
- struct iwl_rx_packet *pkt = tp_data->fw_pkt;
- struct iwl_cmd_header *wanted_hdr = (void *)&trig_data;
- if (pkt && (pkt->hdr.cmd == wanted_hdr->cmd &&
- pkt->hdr.group_id == wanted_hdr->group_id)) {
- struct iwl_rx_packet *fw_pkt =
- kmemdup(pkt,
- sizeof(*pkt) + iwl_rx_packet_payload_len(pkt),
- GFP_ATOMIC);
- if (!fw_pkt)
- return false;
- dump_data->fw_pkt = fw_pkt;
- return true;
- }
- return false;
- }
- static int
- iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync,
- struct list_head *active_trig_list,
- union iwl_dbg_tlv_tp_data *tp_data,
- bool (*data_check)(struct iwl_fw_runtime *fwrt,
- struct iwl_fwrt_dump_data *dump_data,
- union iwl_dbg_tlv_tp_data *tp_data,
- u32 trig_data))
- {
- struct iwl_dbg_tlv_node *node;
- list_for_each_entry(node, active_trig_list, list) {
- struct iwl_fwrt_dump_data dump_data = {
- .trig = (void *)node->tlv.data,
- };
- u32 num_data = iwl_tlv_array_len(&node->tlv, dump_data.trig,
- data);
- int ret, i;
- u32 tp = le32_to_cpu(dump_data.trig->time_point);
- if (!num_data) {
- ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);
- if (ret)
- return ret;
- }
- for (i = 0; i < num_data; i++) {
- if (!data_check ||
- data_check(fwrt, &dump_data, tp_data,
- le32_to_cpu(dump_data.trig->data[i]))) {
- ret = iwl_fw_dbg_ini_collect(fwrt, &dump_data, sync);
- if (ret)
- return ret;
- break;
- }
- }
- fwrt->trans->dbg.restart_required = FALSE;
- IWL_DEBUG_INFO(fwrt, "WRT: tp %d, reset_fw %d\n",
- tp, dump_data.trig->reset_fw);
- IWL_DEBUG_INFO(fwrt, "WRT: restart_required %d, last_tp_resetfw %d\n",
- fwrt->trans->dbg.restart_required,
- fwrt->trans->dbg.last_tp_resetfw);
- if (fwrt->trans->trans_cfg->device_family ==
- IWL_DEVICE_FAMILY_9000) {
- fwrt->trans->dbg.restart_required = TRUE;
- } else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT &&
- fwrt->trans->dbg.last_tp_resetfw ==
- IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {
- fwrt->trans->dbg.restart_required = FALSE;
- fwrt->trans->dbg.last_tp_resetfw = 0xFF;
- IWL_DEBUG_FW(fwrt, "WRT: FW_ASSERT due to reset_fw_mode-no restart\n");
- } else if (le32_to_cpu(dump_data.trig->reset_fw) ==
- IWL_FW_INI_RESET_FW_MODE_STOP_AND_RELOAD_FW) {
- IWL_DEBUG_INFO(fwrt, "WRT: stop and reload firmware\n");
- fwrt->trans->dbg.restart_required = TRUE;
- } else if (le32_to_cpu(dump_data.trig->reset_fw) ==
- IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) {
- IWL_DEBUG_INFO(fwrt, "WRT: stop only and no reload firmware\n");
- fwrt->trans->dbg.restart_required = FALSE;
- fwrt->trans->dbg.last_tp_resetfw =
- le32_to_cpu(dump_data.trig->reset_fw);
- } else if (le32_to_cpu(dump_data.trig->reset_fw) ==
- IWL_FW_INI_RESET_FW_MODE_NOTHING) {
- IWL_DEBUG_INFO(fwrt,
- "WRT: nothing need to be done after debug collection\n");
- } else {
- IWL_ERR(fwrt, "WRT: wrong resetfw %d\n",
- le32_to_cpu(dump_data.trig->reset_fw));
- }
- }
- return 0;
- }
- static void iwl_dbg_tlv_init_cfg(struct iwl_fw_runtime *fwrt)
- {
- enum iwl_fw_ini_buffer_location *ini_dest = &fwrt->trans->dbg.ini_dest;
- int ret, i;
- u32 failed_alloc = 0;
- if (*ini_dest != IWL_FW_INI_LOCATION_INVALID)
- return;
- IWL_DEBUG_FW(fwrt,
- "WRT: Generating active triggers list, domain 0x%x\n",
- fwrt->trans->dbg.domains_bitmap);
- for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.time_point); i++) {
- struct iwl_dbg_tlv_time_point_data *tp =
- &fwrt->trans->dbg.time_point[i];
- iwl_dbg_tlv_gen_active_trig_list(fwrt, tp);
- }
- *ini_dest = IWL_FW_INI_LOCATION_INVALID;
- for (i = 0; i < IWL_FW_INI_ALLOCATION_NUM; i++) {
- struct iwl_fw_ini_allocation_tlv *fw_mon_cfg =
- &fwrt->trans->dbg.fw_mon_cfg[i];
- u32 dest = le32_to_cpu(fw_mon_cfg->buf_location);
- if (dest == IWL_FW_INI_LOCATION_INVALID) {
- failed_alloc |= BIT(i);
- continue;
- }
- if (*ini_dest == IWL_FW_INI_LOCATION_INVALID)
- *ini_dest = dest;
- if (dest != *ini_dest)
- continue;
- ret = iwl_dbg_tlv_alloc_fragments(fwrt, i);
- if (ret) {
- IWL_WARN(fwrt,
- "WRT: Failed to allocate DRAM buffer for allocation id %d, ret=%d\n",
- i, ret);
- failed_alloc |= BIT(i);
- }
- }
- if (!failed_alloc)
- return;
- for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions) && failed_alloc; i++) {
- struct iwl_fw_ini_region_tlv *reg;
- struct iwl_ucode_tlv **active_reg =
- &fwrt->trans->dbg.active_regions[i];
- u32 reg_type;
- if (!*active_reg) {
- fwrt->trans->dbg.unsupported_region_msk |= BIT(i);
- continue;
- }
- reg = (void *)(*active_reg)->data;
- reg_type = reg->type;
- if (reg_type != IWL_FW_INI_REGION_DRAM_BUFFER ||
- !(BIT(le32_to_cpu(reg->dram_alloc_id)) & failed_alloc))
- continue;
- IWL_DEBUG_FW(fwrt,
- "WRT: removing allocation id %d from region id %d\n",
- le32_to_cpu(reg->dram_alloc_id), i);
- failed_alloc &= ~le32_to_cpu(reg->dram_alloc_id);
- fwrt->trans->dbg.unsupported_region_msk |= BIT(i);
- kfree(*active_reg);
- *active_reg = NULL;
- }
- }
- void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt,
- enum iwl_fw_ini_time_point tp_id,
- union iwl_dbg_tlv_tp_data *tp_data,
- bool sync)
- {
- struct list_head *hcmd_list, *trig_list, *conf_list;
- if (!iwl_trans_dbg_ini_valid(fwrt->trans) ||
- tp_id == IWL_FW_INI_TIME_POINT_INVALID ||
- tp_id >= IWL_FW_INI_TIME_POINT_NUM)
- return;
- hcmd_list = &fwrt->trans->dbg.time_point[tp_id].hcmd_list;
- trig_list = &fwrt->trans->dbg.time_point[tp_id].active_trig_list;
- conf_list = &fwrt->trans->dbg.time_point[tp_id].config_list;
- switch (tp_id) {
- case IWL_FW_INI_TIME_POINT_EARLY:
- iwl_dbg_tlv_init_cfg(fwrt);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
- iwl_dbg_tlv_update_drams(fwrt);
- iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
- break;
- case IWL_FW_INI_TIME_POINT_AFTER_ALIVE:
- iwl_dbg_tlv_apply_buffers(fwrt);
- iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
- iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
- break;
- case IWL_FW_INI_TIME_POINT_PERIODIC:
- iwl_dbg_tlv_set_periodic_trigs(fwrt);
- iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
- break;
- case IWL_FW_INI_TIME_POINT_FW_RSP_OR_NOTIF:
- case IWL_FW_INI_TIME_POINT_MISSED_BEACONS:
- case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION:
- iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
- iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data,
- iwl_dbg_tlv_check_fw_pkt);
- break;
- default:
- iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list);
- iwl_dbg_tlv_apply_config(fwrt, conf_list);
- iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL);
- break;
- }
- }
- IWL_EXPORT_SYMBOL(_iwl_dbg_tlv_time_point);
|