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