18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license. When using or 48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license. 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved. 78c2ecf20Sopenharmony_ci// 88c2ecf20Sopenharmony_ci// Author: Liam Girdwood <liam.r.girdwood@linux.intel.com> 98c2ecf20Sopenharmony_ci// 108c2ecf20Sopenharmony_ci// Generic debug routines used to export DSP MMIO and memories to userspace 118c2ecf20Sopenharmony_ci// for firmware debugging. 128c2ecf20Sopenharmony_ci// 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci#include "sof-priv.h" 188c2ecf20Sopenharmony_ci#include "ops.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) 218c2ecf20Sopenharmony_ci#include "probe.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/** 248c2ecf20Sopenharmony_ci * strsplit_u32 - Split string into sequence of u32 tokens 258c2ecf20Sopenharmony_ci * @buf: String to split into tokens. 268c2ecf20Sopenharmony_ci * @delim: String containing delimiter characters. 278c2ecf20Sopenharmony_ci * @tkns: Returned u32 sequence pointer. 288c2ecf20Sopenharmony_ci * @num_tkns: Returned number of tokens obtained. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic int 318c2ecf20Sopenharmony_cistrsplit_u32(char **buf, const char *delim, u32 **tkns, size_t *num_tkns) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci char *s; 348c2ecf20Sopenharmony_ci u32 *data, *tmp; 358c2ecf20Sopenharmony_ci size_t count = 0; 368c2ecf20Sopenharmony_ci size_t cap = 32; 378c2ecf20Sopenharmony_ci int ret = 0; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci *tkns = NULL; 408c2ecf20Sopenharmony_ci *num_tkns = 0; 418c2ecf20Sopenharmony_ci data = kcalloc(cap, sizeof(*data), GFP_KERNEL); 428c2ecf20Sopenharmony_ci if (!data) 438c2ecf20Sopenharmony_ci return -ENOMEM; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci while ((s = strsep(buf, delim)) != NULL) { 468c2ecf20Sopenharmony_ci ret = kstrtouint(s, 0, data + count); 478c2ecf20Sopenharmony_ci if (ret) 488c2ecf20Sopenharmony_ci goto exit; 498c2ecf20Sopenharmony_ci if (++count >= cap) { 508c2ecf20Sopenharmony_ci cap *= 2; 518c2ecf20Sopenharmony_ci tmp = krealloc(data, cap * sizeof(*data), GFP_KERNEL); 528c2ecf20Sopenharmony_ci if (!tmp) { 538c2ecf20Sopenharmony_ci ret = -ENOMEM; 548c2ecf20Sopenharmony_ci goto exit; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci data = tmp; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci if (!count) 618c2ecf20Sopenharmony_ci goto exit; 628c2ecf20Sopenharmony_ci *tkns = kmemdup(data, count * sizeof(*data), GFP_KERNEL); 638c2ecf20Sopenharmony_ci if (*tkns == NULL) { 648c2ecf20Sopenharmony_ci ret = -ENOMEM; 658c2ecf20Sopenharmony_ci goto exit; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci *num_tkns = count; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ciexit: 708c2ecf20Sopenharmony_ci kfree(data); 718c2ecf20Sopenharmony_ci return ret; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int tokenize_input(const char __user *from, size_t count, 758c2ecf20Sopenharmony_ci loff_t *ppos, u32 **tkns, size_t *num_tkns) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci char *buf; 788c2ecf20Sopenharmony_ci int ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci buf = kmalloc(count + 1, GFP_KERNEL); 818c2ecf20Sopenharmony_ci if (!buf) 828c2ecf20Sopenharmony_ci return -ENOMEM; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci ret = simple_write_to_buffer(buf, count, ppos, from, count); 858c2ecf20Sopenharmony_ci if (ret != count) { 868c2ecf20Sopenharmony_ci ret = ret >= 0 ? -EIO : ret; 878c2ecf20Sopenharmony_ci goto exit; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci buf[count] = '\0'; 918c2ecf20Sopenharmony_ci ret = strsplit_u32((char **)&buf, ",", tkns, num_tkns); 928c2ecf20Sopenharmony_ciexit: 938c2ecf20Sopenharmony_ci kfree(buf); 948c2ecf20Sopenharmony_ci return ret; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic ssize_t probe_points_read(struct file *file, 988c2ecf20Sopenharmony_ci char __user *to, size_t count, loff_t *ppos) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse = file->private_data; 1018c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 1028c2ecf20Sopenharmony_ci struct sof_probe_point_desc *desc; 1038c2ecf20Sopenharmony_ci size_t num_desc, len = 0; 1048c2ecf20Sopenharmony_ci char *buf; 1058c2ecf20Sopenharmony_ci int i, ret; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { 1088c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "no extractor stream running\n"); 1098c2ecf20Sopenharmony_ci return -ENOENT; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 1138c2ecf20Sopenharmony_ci if (!buf) 1148c2ecf20Sopenharmony_ci return -ENOMEM; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = sof_ipc_probe_points_info(sdev, &desc, &num_desc); 1178c2ecf20Sopenharmony_ci if (ret < 0) 1188c2ecf20Sopenharmony_ci goto exit; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (i = 0; i < num_desc; i++) { 1218c2ecf20Sopenharmony_ci ret = snprintf(buf + len, PAGE_SIZE - len, 1228c2ecf20Sopenharmony_ci "Id: %#010x Purpose: %d Node id: %#x\n", 1238c2ecf20Sopenharmony_ci desc[i].buffer_id, desc[i].purpose, desc[i].stream_tag); 1248c2ecf20Sopenharmony_ci if (ret < 0) 1258c2ecf20Sopenharmony_ci goto free_desc; 1268c2ecf20Sopenharmony_ci len += ret; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(to, count, ppos, buf, len); 1308c2ecf20Sopenharmony_cifree_desc: 1318c2ecf20Sopenharmony_ci kfree(desc); 1328c2ecf20Sopenharmony_ciexit: 1338c2ecf20Sopenharmony_ci kfree(buf); 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic ssize_t probe_points_write(struct file *file, 1388c2ecf20Sopenharmony_ci const char __user *from, size_t count, loff_t *ppos) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse = file->private_data; 1418c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 1428c2ecf20Sopenharmony_ci struct sof_probe_point_desc *desc; 1438c2ecf20Sopenharmony_ci size_t num_tkns, bytes; 1448c2ecf20Sopenharmony_ci u32 *tkns; 1458c2ecf20Sopenharmony_ci int ret; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { 1488c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "no extractor stream running\n"); 1498c2ecf20Sopenharmony_ci return -ENOENT; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); 1538c2ecf20Sopenharmony_ci if (ret < 0) 1548c2ecf20Sopenharmony_ci return ret; 1558c2ecf20Sopenharmony_ci bytes = sizeof(*tkns) * num_tkns; 1568c2ecf20Sopenharmony_ci if (!num_tkns || (bytes % sizeof(*desc))) { 1578c2ecf20Sopenharmony_ci ret = -EINVAL; 1588c2ecf20Sopenharmony_ci goto exit; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci desc = (struct sof_probe_point_desc *)tkns; 1628c2ecf20Sopenharmony_ci ret = sof_ipc_probe_points_add(sdev, 1638c2ecf20Sopenharmony_ci desc, bytes / sizeof(*desc)); 1648c2ecf20Sopenharmony_ci if (!ret) 1658c2ecf20Sopenharmony_ci ret = count; 1668c2ecf20Sopenharmony_ciexit: 1678c2ecf20Sopenharmony_ci kfree(tkns); 1688c2ecf20Sopenharmony_ci return ret; 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic const struct file_operations probe_points_fops = { 1728c2ecf20Sopenharmony_ci .open = simple_open, 1738c2ecf20Sopenharmony_ci .read = probe_points_read, 1748c2ecf20Sopenharmony_ci .write = probe_points_write, 1758c2ecf20Sopenharmony_ci .llseek = default_llseek, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic ssize_t probe_points_remove_write(struct file *file, 1798c2ecf20Sopenharmony_ci const char __user *from, size_t count, loff_t *ppos) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse = file->private_data; 1828c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 1838c2ecf20Sopenharmony_ci size_t num_tkns; 1848c2ecf20Sopenharmony_ci u32 *tkns; 1858c2ecf20Sopenharmony_ci int ret; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (sdev->extractor_stream_tag == SOF_PROBE_INVALID_NODE_ID) { 1888c2ecf20Sopenharmony_ci dev_warn(sdev->dev, "no extractor stream running\n"); 1898c2ecf20Sopenharmony_ci return -ENOENT; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ret = tokenize_input(from, count, ppos, &tkns, &num_tkns); 1938c2ecf20Sopenharmony_ci if (ret < 0) 1948c2ecf20Sopenharmony_ci return ret; 1958c2ecf20Sopenharmony_ci if (!num_tkns) { 1968c2ecf20Sopenharmony_ci ret = -EINVAL; 1978c2ecf20Sopenharmony_ci goto exit; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = sof_ipc_probe_points_remove(sdev, tkns, num_tkns); 2018c2ecf20Sopenharmony_ci if (!ret) 2028c2ecf20Sopenharmony_ci ret = count; 2038c2ecf20Sopenharmony_ciexit: 2048c2ecf20Sopenharmony_ci kfree(tkns); 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic const struct file_operations probe_points_remove_fops = { 2098c2ecf20Sopenharmony_ci .open = simple_open, 2108c2ecf20Sopenharmony_ci .write = probe_points_remove_write, 2118c2ecf20Sopenharmony_ci .llseek = default_llseek, 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int snd_sof_debugfs_probe_item(struct snd_sof_dev *sdev, 2158c2ecf20Sopenharmony_ci const char *name, mode_t mode, 2168c2ecf20Sopenharmony_ci const struct file_operations *fops) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 2218c2ecf20Sopenharmony_ci if (!dfse) 2228c2ecf20Sopenharmony_ci return -ENOMEM; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci dfse->type = SOF_DFSENTRY_TYPE_BUF; 2258c2ecf20Sopenharmony_ci dfse->sdev = sdev; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci debugfs_create_file(name, mode, sdev->debugfs_root, dfse, fops); 2288c2ecf20Sopenharmony_ci /* add to dfsentry list */ 2298c2ecf20Sopenharmony_ci list_add(&dfse->list, &sdev->dfsentry_list); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci#endif 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) 2368c2ecf20Sopenharmony_ci#define MAX_IPC_FLOOD_DURATION_MS 1000 2378c2ecf20Sopenharmony_ci#define MAX_IPC_FLOOD_COUNT 10000 2388c2ecf20Sopenharmony_ci#define IPC_FLOOD_TEST_RESULT_LEN 512 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int sof_debug_ipc_flood_test(struct snd_sof_dev *sdev, 2418c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse, 2428c2ecf20Sopenharmony_ci bool flood_duration_test, 2438c2ecf20Sopenharmony_ci unsigned long ipc_duration_ms, 2448c2ecf20Sopenharmony_ci unsigned long ipc_count) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct sof_ipc_cmd_hdr hdr; 2478c2ecf20Sopenharmony_ci struct sof_ipc_reply reply; 2488c2ecf20Sopenharmony_ci u64 min_response_time = U64_MAX; 2498c2ecf20Sopenharmony_ci ktime_t start, end, test_end; 2508c2ecf20Sopenharmony_ci u64 avg_response_time = 0; 2518c2ecf20Sopenharmony_ci u64 max_response_time = 0; 2528c2ecf20Sopenharmony_ci u64 ipc_response_time; 2538c2ecf20Sopenharmony_ci int i = 0; 2548c2ecf20Sopenharmony_ci int ret; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* configure test IPC */ 2578c2ecf20Sopenharmony_ci hdr.cmd = SOF_IPC_GLB_TEST_MSG | SOF_IPC_TEST_IPC_FLOOD; 2588c2ecf20Sopenharmony_ci hdr.size = sizeof(hdr); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* set test end time for duration flood test */ 2618c2ecf20Sopenharmony_ci if (flood_duration_test) 2628c2ecf20Sopenharmony_ci test_end = ktime_get_ns() + ipc_duration_ms * NSEC_PER_MSEC; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* send test IPC's */ 2658c2ecf20Sopenharmony_ci while (1) { 2668c2ecf20Sopenharmony_ci start = ktime_get(); 2678c2ecf20Sopenharmony_ci ret = sof_ipc_tx_message(sdev->ipc, hdr.cmd, &hdr, hdr.size, 2688c2ecf20Sopenharmony_ci &reply, sizeof(reply)); 2698c2ecf20Sopenharmony_ci end = ktime_get(); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (ret < 0) 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* compute min and max response times */ 2758c2ecf20Sopenharmony_ci ipc_response_time = ktime_to_ns(ktime_sub(end, start)); 2768c2ecf20Sopenharmony_ci min_response_time = min(min_response_time, ipc_response_time); 2778c2ecf20Sopenharmony_ci max_response_time = max(max_response_time, ipc_response_time); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* sum up response times */ 2808c2ecf20Sopenharmony_ci avg_response_time += ipc_response_time; 2818c2ecf20Sopenharmony_ci i++; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* test complete? */ 2848c2ecf20Sopenharmony_ci if (flood_duration_test) { 2858c2ecf20Sopenharmony_ci if (ktime_to_ns(end) >= test_end) 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci } else { 2888c2ecf20Sopenharmony_ci if (i == ipc_count) 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (ret < 0) 2948c2ecf20Sopenharmony_ci dev_err(sdev->dev, 2958c2ecf20Sopenharmony_ci "error: ipc flood test failed at %d iterations\n", i); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* return if the first IPC fails */ 2988c2ecf20Sopenharmony_ci if (!i) 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci /* compute average response time */ 3028c2ecf20Sopenharmony_ci do_div(avg_response_time, i); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci /* clear previous test output */ 3058c2ecf20Sopenharmony_ci memset(dfse->cache_buf, 0, IPC_FLOOD_TEST_RESULT_LEN); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (flood_duration_test) { 3088c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "IPC Flood test duration: %lums\n", 3098c2ecf20Sopenharmony_ci ipc_duration_ms); 3108c2ecf20Sopenharmony_ci snprintf(dfse->cache_buf, IPC_FLOOD_TEST_RESULT_LEN, 3118c2ecf20Sopenharmony_ci "IPC Flood test duration: %lums\n", ipc_duration_ms); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, 3158c2ecf20Sopenharmony_ci "IPC Flood count: %d, Avg response time: %lluns\n", 3168c2ecf20Sopenharmony_ci i, avg_response_time); 3178c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "Max response time: %lluns\n", 3188c2ecf20Sopenharmony_ci max_response_time); 3198c2ecf20Sopenharmony_ci dev_dbg(sdev->dev, "Min response time: %lluns\n", 3208c2ecf20Sopenharmony_ci min_response_time); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* format output string */ 3238c2ecf20Sopenharmony_ci snprintf(dfse->cache_buf + strlen(dfse->cache_buf), 3248c2ecf20Sopenharmony_ci IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf), 3258c2ecf20Sopenharmony_ci "IPC Flood count: %d\nAvg response time: %lluns\n", 3268c2ecf20Sopenharmony_ci i, avg_response_time); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci snprintf(dfse->cache_buf + strlen(dfse->cache_buf), 3298c2ecf20Sopenharmony_ci IPC_FLOOD_TEST_RESULT_LEN - strlen(dfse->cache_buf), 3308c2ecf20Sopenharmony_ci "Max response time: %lluns\nMin response time: %lluns\n", 3318c2ecf20Sopenharmony_ci max_response_time, min_response_time); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return ret; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci#endif 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, 3388c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) 3418c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse = file->private_data; 3428c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 3438c2ecf20Sopenharmony_ci unsigned long ipc_duration_ms = 0; 3448c2ecf20Sopenharmony_ci bool flood_duration_test = false; 3458c2ecf20Sopenharmony_ci unsigned long ipc_count = 0; 3468c2ecf20Sopenharmony_ci struct dentry *dentry; 3478c2ecf20Sopenharmony_ci int err; 3488c2ecf20Sopenharmony_ci#endif 3498c2ecf20Sopenharmony_ci size_t size; 3508c2ecf20Sopenharmony_ci char *string; 3518c2ecf20Sopenharmony_ci int ret; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci string = kzalloc(count+1, GFP_KERNEL); 3548c2ecf20Sopenharmony_ci if (!string) 3558c2ecf20Sopenharmony_ci return -ENOMEM; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci size = simple_write_to_buffer(string, count, ppos, buffer, count); 3588c2ecf20Sopenharmony_ci ret = size; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * write op is only supported for ipc_flood_count or 3638c2ecf20Sopenharmony_ci * ipc_flood_duration_ms debugfs entries atm. 3648c2ecf20Sopenharmony_ci * ipc_flood_count floods the DSP with the number of IPC's specified. 3658c2ecf20Sopenharmony_ci * ipc_duration_ms test floods the DSP for the time specified 3668c2ecf20Sopenharmony_ci * in the debugfs entry. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci dentry = file->f_path.dentry; 3698c2ecf20Sopenharmony_ci if (strcmp(dentry->d_name.name, "ipc_flood_count") && 3708c2ecf20Sopenharmony_ci strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) { 3718c2ecf20Sopenharmony_ci ret = -EINVAL; 3728c2ecf20Sopenharmony_ci goto out; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (!strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) 3768c2ecf20Sopenharmony_ci flood_duration_test = true; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* test completion criterion */ 3798c2ecf20Sopenharmony_ci if (flood_duration_test) 3808c2ecf20Sopenharmony_ci ret = kstrtoul(string, 0, &ipc_duration_ms); 3818c2ecf20Sopenharmony_ci else 3828c2ecf20Sopenharmony_ci ret = kstrtoul(string, 0, &ipc_count); 3838c2ecf20Sopenharmony_ci if (ret < 0) 3848c2ecf20Sopenharmony_ci goto out; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* limit max duration/ipc count for flood test */ 3878c2ecf20Sopenharmony_ci if (flood_duration_test) { 3888c2ecf20Sopenharmony_ci if (!ipc_duration_ms) { 3898c2ecf20Sopenharmony_ci ret = size; 3908c2ecf20Sopenharmony_ci goto out; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* find the minimum. min() is not used to avoid warnings */ 3948c2ecf20Sopenharmony_ci if (ipc_duration_ms > MAX_IPC_FLOOD_DURATION_MS) 3958c2ecf20Sopenharmony_ci ipc_duration_ms = MAX_IPC_FLOOD_DURATION_MS; 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci if (!ipc_count) { 3988c2ecf20Sopenharmony_ci ret = size; 3998c2ecf20Sopenharmony_ci goto out; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* find the minimum. min() is not used to avoid warnings */ 4038c2ecf20Sopenharmony_ci if (ipc_count > MAX_IPC_FLOOD_COUNT) 4048c2ecf20Sopenharmony_ci ipc_count = MAX_IPC_FLOOD_COUNT; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(sdev->dev); 4088c2ecf20Sopenharmony_ci if (ret < 0 && ret != -EACCES) { 4098c2ecf20Sopenharmony_ci dev_err_ratelimited(sdev->dev, 4108c2ecf20Sopenharmony_ci "error: debugfs write failed to resume %d\n", 4118c2ecf20Sopenharmony_ci ret); 4128c2ecf20Sopenharmony_ci pm_runtime_put_noidle(sdev->dev); 4138c2ecf20Sopenharmony_ci goto out; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* flood test */ 4178c2ecf20Sopenharmony_ci ret = sof_debug_ipc_flood_test(sdev, dfse, flood_duration_test, 4188c2ecf20Sopenharmony_ci ipc_duration_ms, ipc_count); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(sdev->dev); 4218c2ecf20Sopenharmony_ci err = pm_runtime_put_autosuspend(sdev->dev); 4228c2ecf20Sopenharmony_ci if (err < 0) 4238c2ecf20Sopenharmony_ci dev_err_ratelimited(sdev->dev, 4248c2ecf20Sopenharmony_ci "error: debugfs write failed to idle %d\n", 4258c2ecf20Sopenharmony_ci err); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* return size if test is successful */ 4288c2ecf20Sopenharmony_ci if (ret >= 0) 4298c2ecf20Sopenharmony_ci ret = size; 4308c2ecf20Sopenharmony_ciout: 4318c2ecf20Sopenharmony_ci#endif 4328c2ecf20Sopenharmony_ci kfree(string); 4338c2ecf20Sopenharmony_ci return ret; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic ssize_t sof_dfsentry_read(struct file *file, char __user *buffer, 4378c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse = file->private_data; 4408c2ecf20Sopenharmony_ci struct snd_sof_dev *sdev = dfse->sdev; 4418c2ecf20Sopenharmony_ci loff_t pos = *ppos; 4428c2ecf20Sopenharmony_ci size_t size_ret; 4438c2ecf20Sopenharmony_ci int skip = 0; 4448c2ecf20Sopenharmony_ci int size; 4458c2ecf20Sopenharmony_ci u8 *buf; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) 4488c2ecf20Sopenharmony_ci struct dentry *dentry; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dentry = file->f_path.dentry; 4518c2ecf20Sopenharmony_ci if ((!strcmp(dentry->d_name.name, "ipc_flood_count") || 4528c2ecf20Sopenharmony_ci !strcmp(dentry->d_name.name, "ipc_flood_duration_ms")) && 4538c2ecf20Sopenharmony_ci dfse->cache_buf) { 4548c2ecf20Sopenharmony_ci if (*ppos) 4558c2ecf20Sopenharmony_ci return 0; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci count = strlen(dfse->cache_buf); 4588c2ecf20Sopenharmony_ci size_ret = copy_to_user(buffer, dfse->cache_buf, count); 4598c2ecf20Sopenharmony_ci if (size_ret) 4608c2ecf20Sopenharmony_ci return -EFAULT; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci *ppos += count; 4638c2ecf20Sopenharmony_ci return count; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci#endif 4668c2ecf20Sopenharmony_ci size = dfse->size; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* validate position & count */ 4698c2ecf20Sopenharmony_ci if (pos < 0) 4708c2ecf20Sopenharmony_ci return -EINVAL; 4718c2ecf20Sopenharmony_ci if (pos >= size || !count) 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci /* find the minimum. min() is not used since it adds sparse warnings */ 4748c2ecf20Sopenharmony_ci if (count > size - pos) 4758c2ecf20Sopenharmony_ci count = size - pos; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* align io read start to u32 multiple */ 4788c2ecf20Sopenharmony_ci pos = ALIGN_DOWN(pos, 4); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* intermediate buffer size must be u32 multiple */ 4818c2ecf20Sopenharmony_ci size = ALIGN(count, 4); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* if start position is unaligned, read extra u32 */ 4848c2ecf20Sopenharmony_ci if (unlikely(pos != *ppos)) { 4858c2ecf20Sopenharmony_ci skip = *ppos - pos; 4868c2ecf20Sopenharmony_ci if (pos + size + 4 < dfse->size) 4878c2ecf20Sopenharmony_ci size += 4; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci buf = kzalloc(size, GFP_KERNEL); 4918c2ecf20Sopenharmony_ci if (!buf) 4928c2ecf20Sopenharmony_ci return -ENOMEM; 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (dfse->type == SOF_DFSENTRY_TYPE_IOMEM) { 4958c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * If the DSP is active: copy from IO. 4988c2ecf20Sopenharmony_ci * If the DSP is suspended: 4998c2ecf20Sopenharmony_ci * - Copy from IO if the memory is always accessible. 5008c2ecf20Sopenharmony_ci * - Otherwise, copy from cached buffer. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci if (pm_runtime_active(sdev->dev) || 5038c2ecf20Sopenharmony_ci dfse->access_type == SOF_DEBUGFS_ACCESS_ALWAYS) { 5048c2ecf20Sopenharmony_ci memcpy_fromio(buf, dfse->io_mem + pos, size); 5058c2ecf20Sopenharmony_ci } else { 5068c2ecf20Sopenharmony_ci dev_info(sdev->dev, 5078c2ecf20Sopenharmony_ci "Copying cached debugfs data\n"); 5088c2ecf20Sopenharmony_ci memcpy(buf, dfse->cache_buf + pos, size); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci#else 5118c2ecf20Sopenharmony_ci /* if the DSP is in D3 */ 5128c2ecf20Sopenharmony_ci if (!pm_runtime_active(sdev->dev) && 5138c2ecf20Sopenharmony_ci dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 5148c2ecf20Sopenharmony_ci dev_err(sdev->dev, 5158c2ecf20Sopenharmony_ci "error: debugfs entry cannot be read in DSP D3\n"); 5168c2ecf20Sopenharmony_ci kfree(buf); 5178c2ecf20Sopenharmony_ci return -EINVAL; 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci memcpy_fromio(buf, dfse->io_mem + pos, size); 5218c2ecf20Sopenharmony_ci#endif 5228c2ecf20Sopenharmony_ci } else { 5238c2ecf20Sopenharmony_ci memcpy(buf, ((u8 *)(dfse->buf) + pos), size); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* copy to userspace */ 5278c2ecf20Sopenharmony_ci size_ret = copy_to_user(buffer, buf + skip, count); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci kfree(buf); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* update count & position if copy succeeded */ 5328c2ecf20Sopenharmony_ci if (size_ret) 5338c2ecf20Sopenharmony_ci return -EFAULT; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci *ppos = pos + count; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return count; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic const struct file_operations sof_dfs_fops = { 5418c2ecf20Sopenharmony_ci .open = simple_open, 5428c2ecf20Sopenharmony_ci .read = sof_dfsentry_read, 5438c2ecf20Sopenharmony_ci .llseek = default_llseek, 5448c2ecf20Sopenharmony_ci .write = sof_dfsentry_write, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci/* create FS entry for debug files that can expose DSP memories, registers */ 5488c2ecf20Sopenharmony_ciint snd_sof_debugfs_io_item(struct snd_sof_dev *sdev, 5498c2ecf20Sopenharmony_ci void __iomem *base, size_t size, 5508c2ecf20Sopenharmony_ci const char *name, 5518c2ecf20Sopenharmony_ci enum sof_debugfs_access_type access_type) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (!sdev) 5568c2ecf20Sopenharmony_ci return -EINVAL; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 5598c2ecf20Sopenharmony_ci if (!dfse) 5608c2ecf20Sopenharmony_ci return -ENOMEM; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci dfse->type = SOF_DFSENTRY_TYPE_IOMEM; 5638c2ecf20Sopenharmony_ci dfse->io_mem = base; 5648c2ecf20Sopenharmony_ci dfse->size = size; 5658c2ecf20Sopenharmony_ci dfse->sdev = sdev; 5668c2ecf20Sopenharmony_ci dfse->access_type = access_type; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) 5698c2ecf20Sopenharmony_ci /* 5708c2ecf20Sopenharmony_ci * allocate cache buffer that will be used to save the mem window 5718c2ecf20Sopenharmony_ci * contents prior to suspend 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_ci if (access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) { 5748c2ecf20Sopenharmony_ci dfse->cache_buf = devm_kzalloc(sdev->dev, size, GFP_KERNEL); 5758c2ecf20Sopenharmony_ci if (!dfse->cache_buf) 5768c2ecf20Sopenharmony_ci return -ENOMEM; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci#endif 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci debugfs_create_file(name, 0444, sdev->debugfs_root, dfse, 5818c2ecf20Sopenharmony_ci &sof_dfs_fops); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* add to dfsentry list */ 5848c2ecf20Sopenharmony_ci list_add(&dfse->list, &sdev->dfsentry_list); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return 0; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_debugfs_io_item); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci/* create FS entry for debug files to expose kernel memory */ 5918c2ecf20Sopenharmony_ciint snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, 5928c2ecf20Sopenharmony_ci void *base, size_t size, 5938c2ecf20Sopenharmony_ci const char *name, mode_t mode) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct snd_sof_dfsentry *dfse; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (!sdev) 5988c2ecf20Sopenharmony_ci return -EINVAL; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 6018c2ecf20Sopenharmony_ci if (!dfse) 6028c2ecf20Sopenharmony_ci return -ENOMEM; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci dfse->type = SOF_DFSENTRY_TYPE_BUF; 6058c2ecf20Sopenharmony_ci dfse->buf = base; 6068c2ecf20Sopenharmony_ci dfse->size = size; 6078c2ecf20Sopenharmony_ci dfse->sdev = sdev; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) 6108c2ecf20Sopenharmony_ci /* 6118c2ecf20Sopenharmony_ci * cache_buf is unused for SOF_DFSENTRY_TYPE_BUF debugfs entries. 6128c2ecf20Sopenharmony_ci * So, use it to save the results of the last IPC flood test. 6138c2ecf20Sopenharmony_ci */ 6148c2ecf20Sopenharmony_ci dfse->cache_buf = devm_kzalloc(sdev->dev, IPC_FLOOD_TEST_RESULT_LEN, 6158c2ecf20Sopenharmony_ci GFP_KERNEL); 6168c2ecf20Sopenharmony_ci if (!dfse->cache_buf) 6178c2ecf20Sopenharmony_ci return -ENOMEM; 6188c2ecf20Sopenharmony_ci#endif 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci debugfs_create_file(name, mode, sdev->debugfs_root, dfse, 6218c2ecf20Sopenharmony_ci &sof_dfs_fops); 6228c2ecf20Sopenharmony_ci /* add to dfsentry list */ 6238c2ecf20Sopenharmony_ci list_add(&dfse->list, &sdev->dfsentry_list); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return 0; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ciint snd_sof_dbg_init(struct snd_sof_dev *sdev) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci const struct snd_sof_dsp_ops *ops = sof_ops(sdev); 6328c2ecf20Sopenharmony_ci const struct snd_sof_debugfs_map *map; 6338c2ecf20Sopenharmony_ci int i; 6348c2ecf20Sopenharmony_ci int err; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* use "sof" as top level debugFS dir */ 6378c2ecf20Sopenharmony_ci sdev->debugfs_root = debugfs_create_dir("sof", NULL); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* init dfsentry list */ 6408c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdev->dfsentry_list); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* create debugFS files for platform specific MMIO/DSP memories */ 6438c2ecf20Sopenharmony_ci for (i = 0; i < ops->debug_map_count; i++) { 6448c2ecf20Sopenharmony_ci map = &ops->debug_map[i]; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci err = snd_sof_debugfs_io_item(sdev, sdev->bar[map->bar] + 6478c2ecf20Sopenharmony_ci map->offset, map->size, 6488c2ecf20Sopenharmony_ci map->name, map->access_type); 6498c2ecf20Sopenharmony_ci /* errors are only due to memory allocation, not debugfs */ 6508c2ecf20Sopenharmony_ci if (err < 0) 6518c2ecf20Sopenharmony_ci return err; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES) 6558c2ecf20Sopenharmony_ci err = snd_sof_debugfs_probe_item(sdev, "probe_points", 6568c2ecf20Sopenharmony_ci 0644, &probe_points_fops); 6578c2ecf20Sopenharmony_ci if (err < 0) 6588c2ecf20Sopenharmony_ci return err; 6598c2ecf20Sopenharmony_ci err = snd_sof_debugfs_probe_item(sdev, "probe_points_remove", 6608c2ecf20Sopenharmony_ci 0200, &probe_points_remove_fops); 6618c2ecf20Sopenharmony_ci if (err < 0) 6628c2ecf20Sopenharmony_ci return err; 6638c2ecf20Sopenharmony_ci#endif 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST) 6668c2ecf20Sopenharmony_ci /* create read-write ipc_flood_count debugfs entry */ 6678c2ecf20Sopenharmony_ci err = snd_sof_debugfs_buf_item(sdev, NULL, 0, 6688c2ecf20Sopenharmony_ci "ipc_flood_count", 0666); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* errors are only due to memory allocation, not debugfs */ 6718c2ecf20Sopenharmony_ci if (err < 0) 6728c2ecf20Sopenharmony_ci return err; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* create read-write ipc_flood_duration_ms debugfs entry */ 6758c2ecf20Sopenharmony_ci err = snd_sof_debugfs_buf_item(sdev, NULL, 0, 6768c2ecf20Sopenharmony_ci "ipc_flood_duration_ms", 0666); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* errors are only due to memory allocation, not debugfs */ 6798c2ecf20Sopenharmony_ci if (err < 0) 6808c2ecf20Sopenharmony_ci return err; 6818c2ecf20Sopenharmony_ci#endif 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci return 0; 6848c2ecf20Sopenharmony_ci} 6858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_dbg_init); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_civoid snd_sof_free_debug(struct snd_sof_dev *sdev) 6888c2ecf20Sopenharmony_ci{ 6898c2ecf20Sopenharmony_ci debugfs_remove_recursive(sdev->debugfs_root); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_sof_free_debug); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_civoid snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) || 6968c2ecf20Sopenharmony_ci (sof_core_debug & SOF_DBG_RETAIN_CTX)) { 6978c2ecf20Sopenharmony_ci /* should we prevent DSP entering D3 ? */ 6988c2ecf20Sopenharmony_ci dev_info(sdev->dev, "info: preventing DSP entering D3 state to preserve context\n"); 6998c2ecf20Sopenharmony_ci pm_runtime_get_noresume(sdev->dev); 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* dump vital information to the logs */ 7038c2ecf20Sopenharmony_ci snd_sof_dsp_dbg_dump(sdev, SOF_DBG_REGS | SOF_DBG_MBOX); 7048c2ecf20Sopenharmony_ci snd_sof_ipc_dump(sdev); 7058c2ecf20Sopenharmony_ci snd_sof_trace_notify_for_error(sdev); 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(snd_sof_handle_fw_exception); 708