162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 462306a36Sopenharmony_ci// redistributing this file, you may do so under either license. 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 962306a36Sopenharmony_ci// 1062306a36Sopenharmony_ci// Generic debug routines used to export DSP MMIO and memories to userspace 1162306a36Sopenharmony_ci// for firmware debugging. 1262306a36Sopenharmony_ci// 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/debugfs.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <sound/sof/ext_manifest.h> 1862306a36Sopenharmony_ci#include <sound/sof/debug.h> 1962306a36Sopenharmony_ci#include "sof-priv.h" 2062306a36Sopenharmony_ci#include "ops.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, 2362306a36Sopenharmony_ci size_t count, loff_t *ppos) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci size_t size; 2662306a36Sopenharmony_ci char *string; 2762306a36Sopenharmony_ci int ret; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci string = kzalloc(count+1, GFP_KERNEL); 3062306a36Sopenharmony_ci if (!string) 3162306a36Sopenharmony_ci return -ENOMEM; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci size = simple_write_to_buffer(string, count, ppos, buffer, count); 3462306a36Sopenharmony_ci ret = size; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci kfree(string); 3762306a36Sopenharmony_ci return ret; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, 4162306a36Sopenharmony_ci size_t count, loff_t *ppos) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct snd_sof_dfsentry *dfse = file->private_data; 4462306a36Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 4562306a36Sopenharmony_ci loff_t pos = *ppos; 4662306a36Sopenharmony_ci size_t size_ret; 4762306a36Sopenharmony_ci int skip = 0; 4862306a36Sopenharmony_ci int size; 4962306a36Sopenharmony_ci u8 *buf; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci size = dfse->size; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci /* validate position & count */ 5462306a36Sopenharmony_ci if (pos < 0) 5562306a36Sopenharmony_ci return -EINVAL; 5662306a36Sopenharmony_ci if (pos >= size || !count) 5762306a36Sopenharmony_ci return 0; 5862306a36Sopenharmony_ci /* find the minimum. min() is not used since it adds sparse warnings */ 5962306a36Sopenharmony_ci if (count > size - pos) 6062306a36Sopenharmony_ci count = size - pos; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* align io read start to u32 multiple */ 6362306a36Sopenharmony_ci pos = ALIGN_DOWN(pos, 4); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* intermediate buffer size must be u32 multiple */ 6662306a36Sopenharmony_ci size = ALIGN(count, 4); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* if start position is unaligned, read extra u32 */ 6962306a36Sopenharmony_ci if (unlikely(pos != *ppos)) { 7062306a36Sopenharmony_ci skip = *ppos - pos; 7162306a36Sopenharmony_ci if (pos + size + 4 < dfse->size) 7262306a36Sopenharmony_ci size += 4; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci buf = kzalloc(size, GFP_KERNEL); 7662306a36Sopenharmony_ci if (!buf) 7762306a36Sopenharmony_ci return -ENOMEM; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (dfse->type == SOF_DFSENTRY_TYPE_IOMEM) { 8062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * If the DSP is active: copy from IO. 8362306a36Sopenharmony_ci * If the DSP is suspended: 8462306a36Sopenharmony_ci * - Copy from IO if the memory is always accessible. 8562306a36Sopenharmony_ci * - Otherwise, copy from cached buffer. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci if (pm_runtime_active(sdev->dev) || 8862306a36Sopenharmony_ci dfse->access_type == SOF_DEBUGFS_ACCESS_ALWAYS) { 8962306a36Sopenharmony_ci memcpy_fromio(buf, dfse->io_mem + pos, size); 9062306a36Sopenharmony_ci } else { 9162306a36Sopenharmony_ci dev_info(sdev->dev, 9262306a36Sopenharmony_ci "Copying cached debugfs data\n"); 9362306a36Sopenharmony_ci memcpy(buf, dfse->cache_buf + pos, size); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci#else 9662306a36Sopenharmony_ci /* if the DSP is in D3 */ 9762306a36Sopenharmony_ci if (!pm_runtime_active(sdev->dev) && 9862306a36Sopenharmony_ci dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 9962306a36Sopenharmony_ci dev_err(sdev->dev, 10062306a36Sopenharmony_ci "error: debugfs entry cannot be read in DSP D3\n"); 10162306a36Sopenharmony_ci kfree(buf); 10262306a36Sopenharmony_ci return -EINVAL; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci memcpy_fromio(buf, dfse->io_mem + pos, size); 10662306a36Sopenharmony_ci#endif 10762306a36Sopenharmony_ci } else { 10862306a36Sopenharmony_ci memcpy(buf, ((u8 *)(dfse->buf) + pos), size); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* copy to userspace */ 11262306a36Sopenharmony_ci size_ret = copy_to_user(buffer, buf + skip, count); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci kfree(buf); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* update count & position if copy succeeded */ 11762306a36Sopenharmony_ci if (size_ret) 11862306a36Sopenharmony_ci return -EFAULT; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci *ppos = pos + count; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return count; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic const struct file_operations sof_dfs_fops = { 12662306a36Sopenharmony_ci .open = simple_open, 12762306a36Sopenharmony_ci .read = sof_dfsentry_read, 12862306a36Sopenharmony_ci .llseek = default_llseek, 12962306a36Sopenharmony_ci .write = sof_dfsentry_write, 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* create FS entry for debug files that can expose DSP memories, registers */ 13362306a36Sopenharmony_cistatic int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, 13462306a36Sopenharmony_ci void __iomem *base, size_t size, 13562306a36Sopenharmony_ci const char *name, 13662306a36Sopenharmony_ci enum sof_debugfs_access_type access_type) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct snd_sof_dfsentry *dfse; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!sdev) 14162306a36Sopenharmony_ci return -EINVAL; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 14462306a36Sopenharmony_ci if (!dfse) 14562306a36Sopenharmony_ci return -ENOMEM; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci dfse->type = SOF_DFSENTRY_TYPE_IOMEM; 14862306a36Sopenharmony_ci dfse->io_mem = base; 14962306a36Sopenharmony_ci dfse->size = size; 15062306a36Sopenharmony_ci dfse->sdev = sdev; 15162306a36Sopenharmony_ci dfse->access_type = access_type; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 15462306a36Sopenharmony_ci /* 15562306a36Sopenharmony_ci * allocate cache buffer that will be used to save the mem window 15662306a36Sopenharmony_ci * contents prior to suspend 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_ci if (access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 15962306a36Sopenharmony_ci dfse->cache_buf = devm_kzalloc(sdev->dev, size, GFP_KERNEL); 16062306a36Sopenharmony_ci if (!dfse->cache_buf) 16162306a36Sopenharmony_ci return -ENOMEM; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci#endif 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci debugfs_create_file(name, 0444, sdev->debugfs_root, dfse, 16662306a36Sopenharmony_ci &sof_dfs_fops); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* add to dfsentry list */ 16962306a36Sopenharmony_ci list_add(&dfse->list, &sdev->dfsentry_list); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciint snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, 17562306a36Sopenharmony_ci enum snd_sof_fw_blk_type blk_type, u32 offset, 17662306a36Sopenharmony_ci size_t size, const char *name, 17762306a36Sopenharmony_ci enum sof_debugfs_access_type access_type) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int bar = snd_sof_dsp_get_bar_index(sdev, blk_type); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (bar < 0) 18262306a36Sopenharmony_ci return bar; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return snd_sof_debugfs_io_item(sdev, sdev->bar[bar] + offset, size, name, 18562306a36Sopenharmony_ci access_type); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_debugfs_add_region_item_iomem); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* create FS entry for debug files to expose kernel memory */ 19062306a36Sopenharmony_ciint snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, 19162306a36Sopenharmony_ci void *base, size_t size, 19262306a36Sopenharmony_ci const char *name, mode_t mode) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct snd_sof_dfsentry *dfse; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!sdev) 19762306a36Sopenharmony_ci return -EINVAL; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 20062306a36Sopenharmony_ci if (!dfse) 20162306a36Sopenharmony_ci return -ENOMEM; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci dfse->type = SOF_DFSENTRY_TYPE_BUF; 20462306a36Sopenharmony_ci dfse->buf = base; 20562306a36Sopenharmony_ci dfse->size = size; 20662306a36Sopenharmony_ci dfse->sdev = sdev; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci debugfs_create_file(name, mode, sdev->debugfs_root, dfse, 20962306a36Sopenharmony_ci &sof_dfs_fops); 21062306a36Sopenharmony_ci /* add to dfsentry list */ 21162306a36Sopenharmony_ci list_add(&dfse->list, &sdev->dfsentry_list); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_size) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct sof_ipc_cmd_hdr msg = { 22062306a36Sopenharmony_ci .size = sizeof(struct sof_ipc_cmd_hdr), 22162306a36Sopenharmony_ci .cmd = SOF_IPC_GLB_DEBUG | SOF_IPC_DEBUG_MEM_USAGE, 22262306a36Sopenharmony_ci }; 22362306a36Sopenharmony_ci struct sof_ipc_dbg_mem_usage *reply; 22462306a36Sopenharmony_ci int len; 22562306a36Sopenharmony_ci int ret; 22662306a36Sopenharmony_ci int i; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci reply = kmalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 22962306a36Sopenharmony_ci if (!reply) 23062306a36Sopenharmony_ci return -ENOMEM; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(sdev->dev); 23362306a36Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 23462306a36Sopenharmony_ci dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); 23562306a36Sopenharmony_ci goto error; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); 23962306a36Sopenharmony_ci pm_runtime_mark_last_busy(sdev->dev); 24062306a36Sopenharmony_ci pm_runtime_put_autosuspend(sdev->dev); 24162306a36Sopenharmony_ci if (ret < 0 || reply->rhdr.error < 0) { 24262306a36Sopenharmony_ci ret = min(ret, reply->rhdr.error); 24362306a36Sopenharmony_ci dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret); 24462306a36Sopenharmony_ci goto error; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) { 24862306a36Sopenharmony_ci dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n", 24962306a36Sopenharmony_ci reply->rhdr.hdr.size); 25062306a36Sopenharmony_ci ret = -EINVAL; 25162306a36Sopenharmony_ci goto error; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci for (i = 0, len = 0; i < reply->num_elems; i++) { 25562306a36Sopenharmony_ci ret = scnprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n", 25662306a36Sopenharmony_ci reply->elems[i].zone, reply->elems[i].id, 25762306a36Sopenharmony_ci reply->elems[i].used, reply->elems[i].free); 25862306a36Sopenharmony_ci if (ret < 0) 25962306a36Sopenharmony_ci goto error; 26062306a36Sopenharmony_ci len += ret; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = len; 26462306a36Sopenharmony_cierror: 26562306a36Sopenharmony_ci kfree(reply); 26662306a36Sopenharmony_ci return ret; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct snd_sof_dfsentry *dfse = file->private_data; 27262306a36Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 27362306a36Sopenharmony_ci int data_length; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* read memory info from FW only once for each file read */ 27662306a36Sopenharmony_ci if (!*ppos) { 27762306a36Sopenharmony_ci dfse->buf_data_size = 0; 27862306a36Sopenharmony_ci data_length = memory_info_update(sdev, dfse->buf, dfse->size); 27962306a36Sopenharmony_ci if (data_length < 0) 28062306a36Sopenharmony_ci return data_length; 28162306a36Sopenharmony_ci dfse->buf_data_size = data_length; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int memory_info_open(struct inode *inode, struct file *file) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct snd_sof_dfsentry *dfse = inode->i_private; 29062306a36Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci file->private_data = dfse; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* allocate buffer memory only in first open run, to save memory when unused */ 29562306a36Sopenharmony_ci if (!dfse->buf) { 29662306a36Sopenharmony_ci dfse->buf = devm_kmalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); 29762306a36Sopenharmony_ci if (!dfse->buf) 29862306a36Sopenharmony_ci return -ENOMEM; 29962306a36Sopenharmony_ci dfse->size = PAGE_SIZE; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci return 0; 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic const struct file_operations memory_info_fops = { 30662306a36Sopenharmony_ci .open = memory_info_open, 30762306a36Sopenharmony_ci .read = memory_info_read, 30862306a36Sopenharmony_ci .llseek = default_llseek, 30962306a36Sopenharmony_ci}; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciint snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev) 31262306a36Sopenharmony_ci{ 31362306a36Sopenharmony_ci struct snd_sof_dfsentry *dfse; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 31662306a36Sopenharmony_ci if (!dfse) 31762306a36Sopenharmony_ci return -ENOMEM; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* don't allocate buffer before first usage, to save memory when unused */ 32062306a36Sopenharmony_ci dfse->type = SOF_DFSENTRY_TYPE_BUF; 32162306a36Sopenharmony_ci dfse->sdev = sdev; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* add to dfsentry list */ 32662306a36Sopenharmony_ci list_add(&dfse->list, &sdev->dfsentry_list); 32762306a36Sopenharmony_ci return 0; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ciint snd_sof_dbg_init(struct snd_sof_dev *sdev) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct snd_sof_dsp_ops *ops = sof_ops(sdev); 33462306a36Sopenharmony_ci const struct snd_sof_debugfs_map *map; 33562306a36Sopenharmony_ci int i; 33662306a36Sopenharmony_ci int err; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* use "sof" as top level debugFS dir */ 33962306a36Sopenharmony_ci sdev->debugfs_root = debugfs_create_dir("sof", NULL); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* init dfsentry list */ 34262306a36Sopenharmony_ci INIT_LIST_HEAD(&sdev->dfsentry_list); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* create debugFS files for platform specific MMIO/DSP memories */ 34562306a36Sopenharmony_ci for (i = 0; i < ops->debug_map_count; i++) { 34662306a36Sopenharmony_ci map = &ops->debug_map[i]; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci err = snd_sof_debugfs_io_item(sdev, sdev->bar[map->bar] + 34962306a36Sopenharmony_ci map->offset, map->size, 35062306a36Sopenharmony_ci map->name, map->access_type); 35162306a36Sopenharmony_ci /* errors are only due to memory allocation, not debugfs */ 35262306a36Sopenharmony_ci if (err < 0) 35362306a36Sopenharmony_ci return err; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return snd_sof_debugfs_buf_item(sdev, &sdev->fw_state, 35762306a36Sopenharmony_ci sizeof(sdev->fw_state), 35862306a36Sopenharmony_ci "fw_state", 0444); 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_dbg_init); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_civoid snd_sof_free_debug(struct snd_sof_dev *sdev) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci debugfs_remove_recursive(sdev->debugfs_root); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_free_debug); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic const struct soc_fw_state_info { 36962306a36Sopenharmony_ci enum sof_fw_state state; 37062306a36Sopenharmony_ci const char *name; 37162306a36Sopenharmony_ci} fw_state_dbg[] = { 37262306a36Sopenharmony_ci {SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"}, 37362306a36Sopenharmony_ci {SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"}, 37462306a36Sopenharmony_ci {SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"}, 37562306a36Sopenharmony_ci {SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"}, 37662306a36Sopenharmony_ci {SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"}, 37762306a36Sopenharmony_ci {SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"}, 37862306a36Sopenharmony_ci {SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"}, 37962306a36Sopenharmony_ci {SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"}, 38062306a36Sopenharmony_ci {SOF_FW_CRASHED, "SOF_FW_CRASHED"}, 38162306a36Sopenharmony_ci}; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci int i; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) { 38862306a36Sopenharmony_ci if (sdev->fw_state == fw_state_dbg[i].state) { 38962306a36Sopenharmony_ci dev_printk(level, sdev->dev, "fw_state: %s (%d)\n", 39062306a36Sopenharmony_ci fw_state_dbg[i].name, i); 39162306a36Sopenharmony_ci return; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_civoid snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; 40162306a36Sopenharmony_ci bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all) 40462306a36Sopenharmony_ci return; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) { 40762306a36Sopenharmony_ci dev_printk(level, sdev->dev, 40862306a36Sopenharmony_ci "------------[ DSP dump start ]------------\n"); 40962306a36Sopenharmony_ci if (msg) 41062306a36Sopenharmony_ci dev_printk(level, sdev->dev, "%s\n", msg); 41162306a36Sopenharmony_ci snd_sof_dbg_print_fw_state(sdev, level); 41262306a36Sopenharmony_ci sof_ops(sdev)->dbg_dump(sdev, flags); 41362306a36Sopenharmony_ci dev_printk(level, sdev->dev, 41462306a36Sopenharmony_ci "------------[ DSP dump end ]------------\n"); 41562306a36Sopenharmony_ci if (!print_all) 41662306a36Sopenharmony_ci sdev->dbg_dump_printed = true; 41762306a36Sopenharmony_ci } else if (msg) { 41862306a36Sopenharmony_ci dev_printk(level, sdev->dev, "%s\n", msg); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_dsp_dbg_dump); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic void snd_sof_ipc_dump(struct snd_sof_dev *sdev) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci if (sof_ops(sdev)->ipc_dump && !sdev->ipc_dump_printed) { 42662306a36Sopenharmony_ci dev_err(sdev->dev, "------------[ IPC dump start ]------------\n"); 42762306a36Sopenharmony_ci sof_ops(sdev)->ipc_dump(sdev); 42862306a36Sopenharmony_ci dev_err(sdev->dev, "------------[ IPC dump end ]------------\n"); 42962306a36Sopenharmony_ci if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS)) 43062306a36Sopenharmony_ci sdev->ipc_dump_printed = true; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_civoid snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || 43762306a36Sopenharmony_ci sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) { 43862306a36Sopenharmony_ci /* should we prevent DSP entering D3 ? */ 43962306a36Sopenharmony_ci if (!sdev->ipc_dump_printed) 44062306a36Sopenharmony_ci dev_info(sdev->dev, 44162306a36Sopenharmony_ci "Attempting to prevent DSP from entering D3 state to preserve context\n"); 44262306a36Sopenharmony_ci pm_runtime_get_if_in_use(sdev->dev); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* dump vital information to the logs */ 44662306a36Sopenharmony_ci snd_sof_ipc_dump(sdev); 44762306a36Sopenharmony_ci snd_sof_dsp_dbg_dump(sdev, msg, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); 44862306a36Sopenharmony_ci sof_fw_trace_fw_crashed(sdev); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ciEXPORT_SYMBOL(snd_sof_handle_fw_exception); 451