162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012-2014, 2018-2020 Intel Corporation 462306a36Sopenharmony_ci * Copyright (C) 2013-2015 Intel Mobile Communications GmbH 562306a36Sopenharmony_ci * Copyright (C) 2016-2017 Intel Deutschland GmbH 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include "api/commands.h" 862306a36Sopenharmony_ci#include "debugfs.h" 962306a36Sopenharmony_ci#include "dbg.h" 1062306a36Sopenharmony_ci#include <linux/seq_file.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#define FWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 1362306a36Sopenharmony_cistruct dbgfs_##name##_data { \ 1462306a36Sopenharmony_ci argtype *arg; \ 1562306a36Sopenharmony_ci bool read_done; \ 1662306a36Sopenharmony_ci ssize_t rlen; \ 1762306a36Sopenharmony_ci char rbuf[buflen]; \ 1862306a36Sopenharmony_ci}; \ 1962306a36Sopenharmony_cistatic int _iwl_dbgfs_##name##_open(struct inode *inode, \ 2062306a36Sopenharmony_ci struct file *file) \ 2162306a36Sopenharmony_ci{ \ 2262306a36Sopenharmony_ci struct dbgfs_##name##_data *data; \ 2362306a36Sopenharmony_ci \ 2462306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); \ 2562306a36Sopenharmony_ci if (!data) \ 2662306a36Sopenharmony_ci return -ENOMEM; \ 2762306a36Sopenharmony_ci \ 2862306a36Sopenharmony_ci data->read_done = false; \ 2962306a36Sopenharmony_ci data->arg = inode->i_private; \ 3062306a36Sopenharmony_ci file->private_data = data; \ 3162306a36Sopenharmony_ci \ 3262306a36Sopenharmony_ci return 0; \ 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define FWRT_DEBUGFS_READ_WRAPPER(name) \ 3662306a36Sopenharmony_cistatic ssize_t _iwl_dbgfs_##name##_read(struct file *file, \ 3762306a36Sopenharmony_ci char __user *user_buf, \ 3862306a36Sopenharmony_ci size_t count, loff_t *ppos) \ 3962306a36Sopenharmony_ci{ \ 4062306a36Sopenharmony_ci struct dbgfs_##name##_data *data = file->private_data; \ 4162306a36Sopenharmony_ci \ 4262306a36Sopenharmony_ci if (!data->read_done) { \ 4362306a36Sopenharmony_ci data->read_done = true; \ 4462306a36Sopenharmony_ci data->rlen = iwl_dbgfs_##name##_read(data->arg, \ 4562306a36Sopenharmony_ci sizeof(data->rbuf),\ 4662306a36Sopenharmony_ci data->rbuf); \ 4762306a36Sopenharmony_ci } \ 4862306a36Sopenharmony_ci \ 4962306a36Sopenharmony_ci if (data->rlen < 0) \ 5062306a36Sopenharmony_ci return data->rlen; \ 5162306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, \ 5262306a36Sopenharmony_ci data->rbuf, data->rlen); \ 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic int _iwl_dbgfs_release(struct inode *inode, struct file *file) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci kfree(file->private_data); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return 0; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define _FWRT_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \ 6362306a36Sopenharmony_ciFWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 6462306a36Sopenharmony_ciFWRT_DEBUGFS_READ_WRAPPER(name) \ 6562306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 6662306a36Sopenharmony_ci .read = _iwl_dbgfs_##name##_read, \ 6762306a36Sopenharmony_ci .open = _iwl_dbgfs_##name##_open, \ 6862306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 6962306a36Sopenharmony_ci .release = _iwl_dbgfs_release, \ 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define FWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ 7362306a36Sopenharmony_cistatic ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ 7462306a36Sopenharmony_ci const char __user *user_buf, \ 7562306a36Sopenharmony_ci size_t count, loff_t *ppos) \ 7662306a36Sopenharmony_ci{ \ 7762306a36Sopenharmony_ci argtype *arg = \ 7862306a36Sopenharmony_ci ((struct dbgfs_##name##_data *)file->private_data)->arg;\ 7962306a36Sopenharmony_ci char buf[buflen] = {}; \ 8062306a36Sopenharmony_ci size_t buf_size = min(count, sizeof(buf) - 1); \ 8162306a36Sopenharmony_ci \ 8262306a36Sopenharmony_ci if (copy_from_user(buf, user_buf, buf_size)) \ 8362306a36Sopenharmony_ci return -EFAULT; \ 8462306a36Sopenharmony_ci \ 8562306a36Sopenharmony_ci return iwl_dbgfs_##name##_write(arg, buf, buf_size); \ 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, buflen, argtype) \ 8962306a36Sopenharmony_ciFWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 9062306a36Sopenharmony_ciFWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ 9162306a36Sopenharmony_ciFWRT_DEBUGFS_READ_WRAPPER(name) \ 9262306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 9362306a36Sopenharmony_ci .write = _iwl_dbgfs_##name##_write, \ 9462306a36Sopenharmony_ci .read = _iwl_dbgfs_##name##_read, \ 9562306a36Sopenharmony_ci .open = _iwl_dbgfs_##name##_open, \ 9662306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 9762306a36Sopenharmony_ci .release = _iwl_dbgfs_release, \ 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define _FWRT_DEBUGFS_WRITE_FILE_OPS(name, buflen, argtype) \ 10162306a36Sopenharmony_ciFWRT_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ 10262306a36Sopenharmony_ciFWRT_DEBUGFS_WRITE_WRAPPER(name, buflen, argtype) \ 10362306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_##name##_ops = { \ 10462306a36Sopenharmony_ci .write = _iwl_dbgfs_##name##_write, \ 10562306a36Sopenharmony_ci .open = _iwl_dbgfs_##name##_open, \ 10662306a36Sopenharmony_ci .llseek = generic_file_llseek, \ 10762306a36Sopenharmony_ci .release = _iwl_dbgfs_release, \ 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz) \ 11162306a36Sopenharmony_ci _FWRT_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_fw_runtime) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ 11462306a36Sopenharmony_ci _FWRT_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime) 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ 11762306a36Sopenharmony_ci _FWRT_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_fw_runtime) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define FWRT_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) do { \ 12062306a36Sopenharmony_ci debugfs_create_file(alias, mode, parent, fwrt, \ 12162306a36Sopenharmony_ci &iwl_dbgfs_##name##_ops); \ 12262306a36Sopenharmony_ci } while (0) 12362306a36Sopenharmony_ci#define FWRT_DEBUGFS_ADD_FILE(name, parent, mode) \ 12462306a36Sopenharmony_ci FWRT_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int iwl_dbgfs_enabled_severities_write(struct iwl_fw_runtime *fwrt, 12762306a36Sopenharmony_ci char *buf, size_t count) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct iwl_dbg_host_event_cfg_cmd event_cfg; 13062306a36Sopenharmony_ci struct iwl_host_cmd hcmd = { 13162306a36Sopenharmony_ci .id = WIDE_ID(DEBUG_GROUP, HOST_EVENT_CFG), 13262306a36Sopenharmony_ci .flags = CMD_ASYNC, 13362306a36Sopenharmony_ci .data[0] = &event_cfg, 13462306a36Sopenharmony_ci .len[0] = sizeof(event_cfg), 13562306a36Sopenharmony_ci }; 13662306a36Sopenharmony_ci u32 enabled_severities; 13762306a36Sopenharmony_ci int ret = kstrtou32(buf, 10, &enabled_severities); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci if (ret < 0) 14062306a36Sopenharmony_ci return ret; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci event_cfg.enabled_severities = cpu_to_le32(enabled_severities); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ret = iwl_trans_send_cmd(fwrt->trans, &hcmd); 14562306a36Sopenharmony_ci IWL_INFO(fwrt, 14662306a36Sopenharmony_ci "sent host event cfg with enabled_severities: %u, ret: %d\n", 14762306a36Sopenharmony_ci enabled_severities, ret); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return ret ?: count; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciFWRT_DEBUGFS_WRITE_FILE_OPS(enabled_severities, 16); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic void iwl_fw_timestamp_marker_wk(struct work_struct *work) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci int ret; 15762306a36Sopenharmony_ci struct iwl_fw_runtime *fwrt = 15862306a36Sopenharmony_ci container_of(work, struct iwl_fw_runtime, timestamp.wk.work); 15962306a36Sopenharmony_ci unsigned long delay = fwrt->timestamp.delay; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci ret = iwl_fw_send_timestamp_marker_cmd(fwrt); 16262306a36Sopenharmony_ci if (!ret && delay) 16362306a36Sopenharmony_ci schedule_delayed_work(&fwrt->timestamp.wk, 16462306a36Sopenharmony_ci round_jiffies_relative(delay)); 16562306a36Sopenharmony_ci else 16662306a36Sopenharmony_ci IWL_INFO(fwrt, 16762306a36Sopenharmony_ci "stopping timestamp_marker, ret: %d, delay: %u\n", 16862306a36Sopenharmony_ci ret, jiffies_to_msecs(delay) / 1000); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_civoid iwl_fw_trigger_timestamp(struct iwl_fw_runtime *fwrt, u32 delay) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci IWL_INFO(fwrt, 17462306a36Sopenharmony_ci "starting timestamp_marker trigger with delay: %us\n", 17562306a36Sopenharmony_ci delay); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci iwl_fw_cancel_timestamp(fwrt); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci fwrt->timestamp.delay = msecs_to_jiffies(delay * 1000); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci schedule_delayed_work(&fwrt->timestamp.wk, 18262306a36Sopenharmony_ci round_jiffies_relative(fwrt->timestamp.delay)); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_timestamp_marker_write(struct iwl_fw_runtime *fwrt, 18662306a36Sopenharmony_ci char *buf, size_t count) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int ret; 18962306a36Sopenharmony_ci u32 delay; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ret = kstrtou32(buf, 10, &delay); 19262306a36Sopenharmony_ci if (ret < 0) 19362306a36Sopenharmony_ci return ret; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci iwl_fw_trigger_timestamp(fwrt, delay); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci return count; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_timestamp_marker_read(struct iwl_fw_runtime *fwrt, 20162306a36Sopenharmony_ci size_t size, char *buf) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci u32 delay_secs = jiffies_to_msecs(fwrt->timestamp.delay) / 1000; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return scnprintf(buf, size, "%d\n", delay_secs); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciFWRT_DEBUGFS_READ_WRITE_FILE_OPS(timestamp_marker, 16); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistruct hcmd_write_data { 21162306a36Sopenharmony_ci __be32 cmd_id; 21262306a36Sopenharmony_ci __be32 flags; 21362306a36Sopenharmony_ci __be16 length; 21462306a36Sopenharmony_ci u8 data[]; 21562306a36Sopenharmony_ci} __packed; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf, 21862306a36Sopenharmony_ci size_t count) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci size_t header_size = (sizeof(u32) * 2 + sizeof(u16)) * 2; 22162306a36Sopenharmony_ci size_t data_size = (count - 1) / 2; 22262306a36Sopenharmony_ci int ret; 22362306a36Sopenharmony_ci struct hcmd_write_data *data; 22462306a36Sopenharmony_ci struct iwl_host_cmd hcmd = { 22562306a36Sopenharmony_ci .len = { 0, }, 22662306a36Sopenharmony_ci .data = { NULL, }, 22762306a36Sopenharmony_ci }; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (fwrt->ops && fwrt->ops->fw_running && 23062306a36Sopenharmony_ci !fwrt->ops->fw_running(fwrt->ops_ctx)) 23162306a36Sopenharmony_ci return -EIO; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (count < header_size + 1 || count > 1024 * 4) 23462306a36Sopenharmony_ci return -EINVAL; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci data = kmalloc(data_size, GFP_KERNEL); 23762306a36Sopenharmony_ci if (!data) 23862306a36Sopenharmony_ci return -ENOMEM; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci ret = hex2bin((u8 *)data, buf, data_size); 24162306a36Sopenharmony_ci if (ret) 24262306a36Sopenharmony_ci goto out; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci hcmd.id = be32_to_cpu(data->cmd_id); 24562306a36Sopenharmony_ci hcmd.flags = be32_to_cpu(data->flags); 24662306a36Sopenharmony_ci hcmd.len[0] = be16_to_cpu(data->length); 24762306a36Sopenharmony_ci hcmd.data[0] = data->data; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (count != header_size + hcmd.len[0] * 2 + 1) { 25062306a36Sopenharmony_ci IWL_ERR(fwrt, 25162306a36Sopenharmony_ci "host command data size does not match header length\n"); 25262306a36Sopenharmony_ci ret = -EINVAL; 25362306a36Sopenharmony_ci goto out; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (fwrt->ops && fwrt->ops->send_hcmd) 25762306a36Sopenharmony_ci ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd); 25862306a36Sopenharmony_ci else 25962306a36Sopenharmony_ci ret = -EPERM; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (ret < 0) 26262306a36Sopenharmony_ci goto out; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (hcmd.flags & CMD_WANT_SKB) 26562306a36Sopenharmony_ci iwl_free_resp(&hcmd); 26662306a36Sopenharmony_ciout: 26762306a36Sopenharmony_ci kfree(data); 26862306a36Sopenharmony_ci return ret ?: count; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciFWRT_DEBUGFS_WRITE_FILE_OPS(send_hcmd, 512); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic ssize_t iwl_dbgfs_fw_dbg_domain_read(struct iwl_fw_runtime *fwrt, 27462306a36Sopenharmony_ci size_t size, char *buf) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci return scnprintf(buf, size, "0x%08x\n", 27762306a36Sopenharmony_ci fwrt->trans->dbg.domains_bitmap); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciFWRT_DEBUGFS_READ_FILE_OPS(fw_dbg_domain, 20); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistruct iwl_dbgfs_fw_info_priv { 28362306a36Sopenharmony_ci struct iwl_fw_runtime *fwrt; 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistruct iwl_dbgfs_fw_info_state { 28762306a36Sopenharmony_ci loff_t pos; 28862306a36Sopenharmony_ci}; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_cistatic void *iwl_dbgfs_fw_info_seq_next(struct seq_file *seq, 29162306a36Sopenharmony_ci void *v, loff_t *pos) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci struct iwl_dbgfs_fw_info_state *state = v; 29462306a36Sopenharmony_ci struct iwl_dbgfs_fw_info_priv *priv = seq->private; 29562306a36Sopenharmony_ci const struct iwl_fw *fw = priv->fwrt->fw; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci *pos = ++state->pos; 29862306a36Sopenharmony_ci if (*pos >= fw->ucode_capa.n_cmd_versions) { 29962306a36Sopenharmony_ci kfree(state); 30062306a36Sopenharmony_ci return NULL; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return state; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void iwl_dbgfs_fw_info_seq_stop(struct seq_file *seq, 30762306a36Sopenharmony_ci void *v) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci kfree(v); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void *iwl_dbgfs_fw_info_seq_start(struct seq_file *seq, loff_t *pos) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci struct iwl_dbgfs_fw_info_priv *priv = seq->private; 31562306a36Sopenharmony_ci const struct iwl_fw *fw = priv->fwrt->fw; 31662306a36Sopenharmony_ci struct iwl_dbgfs_fw_info_state *state; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (*pos >= fw->ucode_capa.n_cmd_versions) 31962306a36Sopenharmony_ci return NULL; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 32262306a36Sopenharmony_ci if (!state) 32362306a36Sopenharmony_ci return NULL; 32462306a36Sopenharmony_ci state->pos = *pos; 32562306a36Sopenharmony_ci return state; 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int iwl_dbgfs_fw_info_seq_show(struct seq_file *seq, void *v) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct iwl_dbgfs_fw_info_state *state = v; 33162306a36Sopenharmony_ci struct iwl_dbgfs_fw_info_priv *priv = seq->private; 33262306a36Sopenharmony_ci const struct iwl_fw *fw = priv->fwrt->fw; 33362306a36Sopenharmony_ci const struct iwl_fw_cmd_version *ver; 33462306a36Sopenharmony_ci u32 cmd_id; 33562306a36Sopenharmony_ci int has_capa; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!state->pos) { 33862306a36Sopenharmony_ci seq_puts(seq, "fw_capa:\n"); 33962306a36Sopenharmony_ci has_capa = fw_has_capa(&fw->ucode_capa, 34062306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT) ? 1 : 0; 34162306a36Sopenharmony_ci seq_printf(seq, 34262306a36Sopenharmony_ci " %d: %d\n", 34362306a36Sopenharmony_ci IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT, 34462306a36Sopenharmony_ci has_capa); 34562306a36Sopenharmony_ci seq_puts(seq, "fw_api_ver:\n"); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci ver = &fw->ucode_capa.cmd_versions[state->pos]; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci cmd_id = WIDE_ID(ver->group, ver->cmd); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci seq_printf(seq, " 0x%04x:\n", cmd_id); 35362306a36Sopenharmony_ci seq_printf(seq, " name: %s\n", 35462306a36Sopenharmony_ci iwl_get_cmd_string(priv->fwrt->trans, cmd_id)); 35562306a36Sopenharmony_ci seq_printf(seq, " cmd_ver: %d\n", ver->cmd_ver); 35662306a36Sopenharmony_ci seq_printf(seq, " notif_ver: %d\n", ver->notif_ver); 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic const struct seq_operations iwl_dbgfs_info_seq_ops = { 36162306a36Sopenharmony_ci .start = iwl_dbgfs_fw_info_seq_start, 36262306a36Sopenharmony_ci .next = iwl_dbgfs_fw_info_seq_next, 36362306a36Sopenharmony_ci .stop = iwl_dbgfs_fw_info_seq_stop, 36462306a36Sopenharmony_ci .show = iwl_dbgfs_fw_info_seq_show, 36562306a36Sopenharmony_ci}; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int iwl_dbgfs_fw_info_open(struct inode *inode, struct file *filp) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct iwl_dbgfs_fw_info_priv *priv; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci priv = __seq_open_private(filp, &iwl_dbgfs_info_seq_ops, 37262306a36Sopenharmony_ci sizeof(*priv)); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (!priv) 37562306a36Sopenharmony_ci return -ENOMEM; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci priv->fwrt = inode->i_private; 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic const struct file_operations iwl_dbgfs_fw_info_ops = { 38262306a36Sopenharmony_ci .owner = THIS_MODULE, 38362306a36Sopenharmony_ci .open = iwl_dbgfs_fw_info_open, 38462306a36Sopenharmony_ci .read = seq_read, 38562306a36Sopenharmony_ci .llseek = seq_lseek, 38662306a36Sopenharmony_ci .release = seq_release_private, 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_civoid iwl_fwrt_dbgfs_register(struct iwl_fw_runtime *fwrt, 39062306a36Sopenharmony_ci struct dentry *dbgfs_dir) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci INIT_DELAYED_WORK(&fwrt->timestamp.wk, iwl_fw_timestamp_marker_wk); 39362306a36Sopenharmony_ci FWRT_DEBUGFS_ADD_FILE(timestamp_marker, dbgfs_dir, 0200); 39462306a36Sopenharmony_ci FWRT_DEBUGFS_ADD_FILE(fw_info, dbgfs_dir, 0200); 39562306a36Sopenharmony_ci FWRT_DEBUGFS_ADD_FILE(send_hcmd, dbgfs_dir, 0200); 39662306a36Sopenharmony_ci FWRT_DEBUGFS_ADD_FILE(enabled_severities, dbgfs_dir, 0200); 39762306a36Sopenharmony_ci FWRT_DEBUGFS_ADD_FILE(fw_dbg_domain, dbgfs_dir, 0400); 39862306a36Sopenharmony_ci} 399