xref: /openbmc/linux/sound/soc/sof/debug.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
286b02f71SLiam Girdwood //
386b02f71SLiam Girdwood // This file is provided under a dual BSD/GPLv2 license.  When using or
486b02f71SLiam Girdwood // redistributing this file, you may do so under either license.
586b02f71SLiam Girdwood //
686b02f71SLiam Girdwood // Copyright(c) 2018 Intel Corporation. All rights reserved.
786b02f71SLiam Girdwood //
886b02f71SLiam Girdwood // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
986b02f71SLiam Girdwood //
1086b02f71SLiam Girdwood // Generic debug routines used to export DSP MMIO and memories to userspace
1186b02f71SLiam Girdwood // for firmware debugging.
1286b02f71SLiam Girdwood //
1386b02f71SLiam Girdwood 
1486b02f71SLiam Girdwood #include <linux/debugfs.h>
1586b02f71SLiam Girdwood #include <linux/io.h>
1686b02f71SLiam Girdwood #include <linux/pm_runtime.h>
175b10b629SKarol Trzcinski #include <sound/sof/ext_manifest.h>
185b10b629SKarol Trzcinski #include <sound/sof/debug.h>
1986b02f71SLiam Girdwood #include "sof-priv.h"
2086b02f71SLiam Girdwood #include "ops.h"
2186b02f71SLiam Girdwood 
sof_dfsentry_write(struct file * file,const char __user * buffer,size_t count,loff_t * ppos)22091c12e1SRanjani Sridharan static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer,
23091c12e1SRanjani Sridharan 				  size_t count, loff_t *ppos)
24091c12e1SRanjani Sridharan {
25091c12e1SRanjani Sridharan 	size_t size;
26091c12e1SRanjani Sridharan 	char *string;
27091c12e1SRanjani Sridharan 	int ret;
28091c12e1SRanjani Sridharan 
299037c3bdSHui Wang 	string = kzalloc(count+1, GFP_KERNEL);
30091c12e1SRanjani Sridharan 	if (!string)
31091c12e1SRanjani Sridharan 		return -ENOMEM;
32091c12e1SRanjani Sridharan 
33091c12e1SRanjani Sridharan 	size = simple_write_to_buffer(string, count, ppos, buffer, count);
34091c12e1SRanjani Sridharan 	ret = size;
35091c12e1SRanjani Sridharan 
36091c12e1SRanjani Sridharan 	kfree(string);
37091c12e1SRanjani Sridharan 	return ret;
38091c12e1SRanjani Sridharan }
39091c12e1SRanjani Sridharan 
sof_dfsentry_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)4086b02f71SLiam Girdwood static ssize_t sof_dfsentry_read(struct file *file, char __user *buffer,
4186b02f71SLiam Girdwood 				 size_t count, loff_t *ppos)
4286b02f71SLiam Girdwood {
4386b02f71SLiam Girdwood 	struct snd_sof_dfsentry *dfse = file->private_data;
4486b02f71SLiam Girdwood 	struct snd_sof_dev *sdev = dfse->sdev;
4586b02f71SLiam Girdwood 	loff_t pos = *ppos;
4686b02f71SLiam Girdwood 	size_t size_ret;
4786b02f71SLiam Girdwood 	int skip = 0;
4886b02f71SLiam Girdwood 	int size;
4986b02f71SLiam Girdwood 	u8 *buf;
5086b02f71SLiam Girdwood 
5186b02f71SLiam Girdwood 	size = dfse->size;
5286b02f71SLiam Girdwood 
5386b02f71SLiam Girdwood 	/* validate position & count */
5486b02f71SLiam Girdwood 	if (pos < 0)
5586b02f71SLiam Girdwood 		return -EINVAL;
5686b02f71SLiam Girdwood 	if (pos >= size || !count)
5786b02f71SLiam Girdwood 		return 0;
5886b02f71SLiam Girdwood 	/* find the minimum. min() is not used since it adds sparse warnings */
5986b02f71SLiam Girdwood 	if (count > size - pos)
6086b02f71SLiam Girdwood 		count = size - pos;
6186b02f71SLiam Girdwood 
6286b02f71SLiam Girdwood 	/* align io read start to u32 multiple */
6386b02f71SLiam Girdwood 	pos = ALIGN_DOWN(pos, 4);
6486b02f71SLiam Girdwood 
6586b02f71SLiam Girdwood 	/* intermediate buffer size must be u32 multiple */
6686b02f71SLiam Girdwood 	size = ALIGN(count, 4);
6786b02f71SLiam Girdwood 
6886b02f71SLiam Girdwood 	/* if start position is unaligned, read extra u32 */
6986b02f71SLiam Girdwood 	if (unlikely(pos != *ppos)) {
7086b02f71SLiam Girdwood 		skip = *ppos - pos;
7186b02f71SLiam Girdwood 		if (pos + size + 4 < dfse->size)
7286b02f71SLiam Girdwood 			size += 4;
7386b02f71SLiam Girdwood 	}
7486b02f71SLiam Girdwood 
7586b02f71SLiam Girdwood 	buf = kzalloc(size, GFP_KERNEL);
7686b02f71SLiam Girdwood 	if (!buf)
7786b02f71SLiam Girdwood 		return -ENOMEM;
7886b02f71SLiam Girdwood 
7986b02f71SLiam Girdwood 	if (dfse->type == SOF_DFSENTRY_TYPE_IOMEM) {
8086b02f71SLiam Girdwood #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
8186b02f71SLiam Girdwood 		/*
8286b02f71SLiam Girdwood 		 * If the DSP is active: copy from IO.
8386b02f71SLiam Girdwood 		 * If the DSP is suspended:
8486b02f71SLiam Girdwood 		 *	- Copy from IO if the memory is always accessible.
8586b02f71SLiam Girdwood 		 *	- Otherwise, copy from cached buffer.
8686b02f71SLiam Girdwood 		 */
8786b02f71SLiam Girdwood 		if (pm_runtime_active(sdev->dev) ||
8886b02f71SLiam Girdwood 		    dfse->access_type == SOF_DEBUGFS_ACCESS_ALWAYS) {
8986b02f71SLiam Girdwood 			memcpy_fromio(buf, dfse->io_mem + pos, size);
9086b02f71SLiam Girdwood 		} else {
9186b02f71SLiam Girdwood 			dev_info(sdev->dev,
9286b02f71SLiam Girdwood 				 "Copying cached debugfs data\n");
9386b02f71SLiam Girdwood 			memcpy(buf, dfse->cache_buf + pos, size);
9486b02f71SLiam Girdwood 		}
9586b02f71SLiam Girdwood #else
9686b02f71SLiam Girdwood 		/* if the DSP is in D3 */
9786b02f71SLiam Girdwood 		if (!pm_runtime_active(sdev->dev) &&
9886b02f71SLiam Girdwood 		    dfse->access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) {
9986b02f71SLiam Girdwood 			dev_err(sdev->dev,
1003ff3a4f6SGreg Kroah-Hartman 				"error: debugfs entry cannot be read in DSP D3\n");
10186b02f71SLiam Girdwood 			kfree(buf);
10286b02f71SLiam Girdwood 			return -EINVAL;
10386b02f71SLiam Girdwood 		}
10486b02f71SLiam Girdwood 
10586b02f71SLiam Girdwood 		memcpy_fromio(buf, dfse->io_mem + pos, size);
10686b02f71SLiam Girdwood #endif
10786b02f71SLiam Girdwood 	} else {
10886b02f71SLiam Girdwood 		memcpy(buf, ((u8 *)(dfse->buf) + pos), size);
10986b02f71SLiam Girdwood 	}
11086b02f71SLiam Girdwood 
11186b02f71SLiam Girdwood 	/* copy to userspace */
11286b02f71SLiam Girdwood 	size_ret = copy_to_user(buffer, buf + skip, count);
11386b02f71SLiam Girdwood 
11486b02f71SLiam Girdwood 	kfree(buf);
11586b02f71SLiam Girdwood 
11686b02f71SLiam Girdwood 	/* update count & position if copy succeeded */
11786b02f71SLiam Girdwood 	if (size_ret)
11886b02f71SLiam Girdwood 		return -EFAULT;
11986b02f71SLiam Girdwood 
12086b02f71SLiam Girdwood 	*ppos = pos + count;
12186b02f71SLiam Girdwood 
12286b02f71SLiam Girdwood 	return count;
12386b02f71SLiam Girdwood }
12486b02f71SLiam Girdwood 
12586b02f71SLiam Girdwood static const struct file_operations sof_dfs_fops = {
12686b02f71SLiam Girdwood 	.open = simple_open,
12786b02f71SLiam Girdwood 	.read = sof_dfsentry_read,
12886b02f71SLiam Girdwood 	.llseek = default_llseek,
129091c12e1SRanjani Sridharan 	.write = sof_dfsentry_write,
13086b02f71SLiam Girdwood };
13186b02f71SLiam Girdwood 
13286b02f71SLiam Girdwood /* create FS entry for debug files that can expose DSP memories, registers */
snd_sof_debugfs_io_item(struct snd_sof_dev * sdev,void __iomem * base,size_t size,const char * name,enum sof_debugfs_access_type access_type)133bde4f08cSPeter Ujfalusi static int snd_sof_debugfs_io_item(struct snd_sof_dev *sdev,
13486b02f71SLiam Girdwood 				   void __iomem *base, size_t size,
13586b02f71SLiam Girdwood 				   const char *name,
13686b02f71SLiam Girdwood 				   enum sof_debugfs_access_type access_type)
13786b02f71SLiam Girdwood {
13886b02f71SLiam Girdwood 	struct snd_sof_dfsentry *dfse;
13986b02f71SLiam Girdwood 
14086b02f71SLiam Girdwood 	if (!sdev)
14186b02f71SLiam Girdwood 		return -EINVAL;
14286b02f71SLiam Girdwood 
14386b02f71SLiam Girdwood 	dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
14486b02f71SLiam Girdwood 	if (!dfse)
14586b02f71SLiam Girdwood 		return -ENOMEM;
14686b02f71SLiam Girdwood 
14786b02f71SLiam Girdwood 	dfse->type = SOF_DFSENTRY_TYPE_IOMEM;
14886b02f71SLiam Girdwood 	dfse->io_mem = base;
14986b02f71SLiam Girdwood 	dfse->size = size;
15086b02f71SLiam Girdwood 	dfse->sdev = sdev;
15186b02f71SLiam Girdwood 	dfse->access_type = access_type;
15286b02f71SLiam Girdwood 
15386b02f71SLiam Girdwood #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE)
15486b02f71SLiam Girdwood 	/*
15586b02f71SLiam Girdwood 	 * allocate cache buffer that will be used to save the mem window
15686b02f71SLiam Girdwood 	 * contents prior to suspend
15786b02f71SLiam Girdwood 	 */
15886b02f71SLiam Girdwood 	if (access_type == SOF_DEBUGFS_ACCESS_D0_ONLY) {
15986b02f71SLiam Girdwood 		dfse->cache_buf = devm_kzalloc(sdev->dev, size, GFP_KERNEL);
16086b02f71SLiam Girdwood 		if (!dfse->cache_buf)
16186b02f71SLiam Girdwood 			return -ENOMEM;
16286b02f71SLiam Girdwood 	}
16386b02f71SLiam Girdwood #endif
16486b02f71SLiam Girdwood 
1653ff3a4f6SGreg Kroah-Hartman 	debugfs_create_file(name, 0444, sdev->debugfs_root, dfse,
1663ff3a4f6SGreg Kroah-Hartman 			    &sof_dfs_fops);
1673ff3a4f6SGreg Kroah-Hartman 
16886b02f71SLiam Girdwood 	/* add to dfsentry list */
16986b02f71SLiam Girdwood 	list_add(&dfse->list, &sdev->dfsentry_list);
17086b02f71SLiam Girdwood 
17186b02f71SLiam Girdwood 	return 0;
17286b02f71SLiam Girdwood }
17386b02f71SLiam Girdwood 
snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev * sdev,enum snd_sof_fw_blk_type blk_type,u32 offset,size_t size,const char * name,enum sof_debugfs_access_type access_type)17407e833b4SPeter Ujfalusi int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev,
17507e833b4SPeter Ujfalusi 					  enum snd_sof_fw_blk_type blk_type, u32 offset,
17607e833b4SPeter Ujfalusi 					  size_t size, const char *name,
17707e833b4SPeter Ujfalusi 					  enum sof_debugfs_access_type access_type)
17807e833b4SPeter Ujfalusi {
17907e833b4SPeter Ujfalusi 	int bar = snd_sof_dsp_get_bar_index(sdev, blk_type);
18007e833b4SPeter Ujfalusi 
18107e833b4SPeter Ujfalusi 	if (bar < 0)
18207e833b4SPeter Ujfalusi 		return bar;
18307e833b4SPeter Ujfalusi 
18407e833b4SPeter Ujfalusi 	return snd_sof_debugfs_io_item(sdev, sdev->bar[bar] + offset, size, name,
18507e833b4SPeter Ujfalusi 				       access_type);
18607e833b4SPeter Ujfalusi }
18707e833b4SPeter Ujfalusi EXPORT_SYMBOL_GPL(snd_sof_debugfs_add_region_item_iomem);
18807e833b4SPeter Ujfalusi 
18986b02f71SLiam Girdwood /* create FS entry for debug files to expose kernel memory */
snd_sof_debugfs_buf_item(struct snd_sof_dev * sdev,void * base,size_t size,const char * name,mode_t mode)19086b02f71SLiam Girdwood int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
19186b02f71SLiam Girdwood 			     void *base, size_t size,
1925c9714f6SRanjani Sridharan 			     const char *name, mode_t mode)
19386b02f71SLiam Girdwood {
19486b02f71SLiam Girdwood 	struct snd_sof_dfsentry *dfse;
19586b02f71SLiam Girdwood 
19686b02f71SLiam Girdwood 	if (!sdev)
19786b02f71SLiam Girdwood 		return -EINVAL;
19886b02f71SLiam Girdwood 
19986b02f71SLiam Girdwood 	dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
20086b02f71SLiam Girdwood 	if (!dfse)
20186b02f71SLiam Girdwood 		return -ENOMEM;
20286b02f71SLiam Girdwood 
20386b02f71SLiam Girdwood 	dfse->type = SOF_DFSENTRY_TYPE_BUF;
20486b02f71SLiam Girdwood 	dfse->buf = base;
20586b02f71SLiam Girdwood 	dfse->size = size;
20686b02f71SLiam Girdwood 	dfse->sdev = sdev;
20786b02f71SLiam Girdwood 
2083ff3a4f6SGreg Kroah-Hartman 	debugfs_create_file(name, mode, sdev->debugfs_root, dfse,
2093ff3a4f6SGreg Kroah-Hartman 			    &sof_dfs_fops);
21086b02f71SLiam Girdwood 	/* add to dfsentry list */
21186b02f71SLiam Girdwood 	list_add(&dfse->list, &sdev->dfsentry_list);
21286b02f71SLiam Girdwood 
21386b02f71SLiam Girdwood 	return 0;
21486b02f71SLiam Girdwood }
21586b02f71SLiam Girdwood EXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item);
21686b02f71SLiam Girdwood 
memory_info_update(struct snd_sof_dev * sdev,char * buf,size_t buff_size)2175b10b629SKarol Trzcinski static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_size)
2185b10b629SKarol Trzcinski {
2195b10b629SKarol Trzcinski 	struct sof_ipc_cmd_hdr msg = {
2205b10b629SKarol Trzcinski 		.size = sizeof(struct sof_ipc_cmd_hdr),
2215b10b629SKarol Trzcinski 		.cmd = SOF_IPC_GLB_DEBUG | SOF_IPC_DEBUG_MEM_USAGE,
2225b10b629SKarol Trzcinski 	};
2235b10b629SKarol Trzcinski 	struct sof_ipc_dbg_mem_usage *reply;
2245b10b629SKarol Trzcinski 	int len;
2255b10b629SKarol Trzcinski 	int ret;
2265b10b629SKarol Trzcinski 	int i;
2275b10b629SKarol Trzcinski 
2285b10b629SKarol Trzcinski 	reply = kmalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
2295b10b629SKarol Trzcinski 	if (!reply)
2305b10b629SKarol Trzcinski 		return -ENOMEM;
2315b10b629SKarol Trzcinski 
232c106f46eSPierre-Louis Bossart 	ret = pm_runtime_resume_and_get(sdev->dev);
2335b10b629SKarol Trzcinski 	if (ret < 0 && ret != -EACCES) {
2345b10b629SKarol Trzcinski 		dev_err(sdev->dev, "error: enabling device failed: %d\n", ret);
2355b10b629SKarol Trzcinski 		goto error;
2365b10b629SKarol Trzcinski 	}
2375b10b629SKarol Trzcinski 
2382a51c0f8SPeter Ujfalusi 	ret = sof_ipc_tx_message(sdev->ipc, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
2395b10b629SKarol Trzcinski 	pm_runtime_mark_last_busy(sdev->dev);
2405b10b629SKarol Trzcinski 	pm_runtime_put_autosuspend(sdev->dev);
2415b10b629SKarol Trzcinski 	if (ret < 0 || reply->rhdr.error < 0) {
2425b10b629SKarol Trzcinski 		ret = min(ret, reply->rhdr.error);
2435b10b629SKarol Trzcinski 		dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret);
2445b10b629SKarol Trzcinski 		goto error;
2455b10b629SKarol Trzcinski 	}
2465b10b629SKarol Trzcinski 
2475b10b629SKarol Trzcinski 	if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) {
2485b10b629SKarol Trzcinski 		dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n",
2495b10b629SKarol Trzcinski 			reply->rhdr.hdr.size);
2505b10b629SKarol Trzcinski 		ret = -EINVAL;
2515b10b629SKarol Trzcinski 		goto error;
2525b10b629SKarol Trzcinski 	}
2535b10b629SKarol Trzcinski 
2545b10b629SKarol Trzcinski 	for (i = 0, len = 0; i < reply->num_elems; i++) {
2551eb123ceSTakashi Iwai 		ret = scnprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n",
2565b10b629SKarol Trzcinski 				reply->elems[i].zone, reply->elems[i].id,
2575b10b629SKarol Trzcinski 				reply->elems[i].used, reply->elems[i].free);
2585b10b629SKarol Trzcinski 		if (ret < 0)
2595b10b629SKarol Trzcinski 			goto error;
2605b10b629SKarol Trzcinski 		len += ret;
2615b10b629SKarol Trzcinski 	}
2625b10b629SKarol Trzcinski 
2635b10b629SKarol Trzcinski 	ret = len;
2645b10b629SKarol Trzcinski error:
2655b10b629SKarol Trzcinski 	kfree(reply);
2665b10b629SKarol Trzcinski 	return ret;
2675b10b629SKarol Trzcinski }
2685b10b629SKarol Trzcinski 
memory_info_read(struct file * file,char __user * to,size_t count,loff_t * ppos)2695b10b629SKarol Trzcinski static ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
2705b10b629SKarol Trzcinski {
2715b10b629SKarol Trzcinski 	struct snd_sof_dfsentry *dfse = file->private_data;
2725b10b629SKarol Trzcinski 	struct snd_sof_dev *sdev = dfse->sdev;
2735b10b629SKarol Trzcinski 	int data_length;
2745b10b629SKarol Trzcinski 
2755b10b629SKarol Trzcinski 	/* read memory info from FW only once for each file read */
2765b10b629SKarol Trzcinski 	if (!*ppos) {
2775b10b629SKarol Trzcinski 		dfse->buf_data_size = 0;
2785b10b629SKarol Trzcinski 		data_length = memory_info_update(sdev, dfse->buf, dfse->size);
2795b10b629SKarol Trzcinski 		if (data_length < 0)
2805b10b629SKarol Trzcinski 			return data_length;
2815b10b629SKarol Trzcinski 		dfse->buf_data_size = data_length;
2825b10b629SKarol Trzcinski 	}
2835b10b629SKarol Trzcinski 
2845b10b629SKarol Trzcinski 	return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size);
2855b10b629SKarol Trzcinski }
2865b10b629SKarol Trzcinski 
memory_info_open(struct inode * inode,struct file * file)2875b10b629SKarol Trzcinski static int memory_info_open(struct inode *inode, struct file *file)
2885b10b629SKarol Trzcinski {
2895b10b629SKarol Trzcinski 	struct snd_sof_dfsentry *dfse = inode->i_private;
2905b10b629SKarol Trzcinski 	struct snd_sof_dev *sdev = dfse->sdev;
2915b10b629SKarol Trzcinski 
2925b10b629SKarol Trzcinski 	file->private_data = dfse;
2935b10b629SKarol Trzcinski 
2945b10b629SKarol Trzcinski 	/* allocate buffer memory only in first open run, to save memory when unused */
2955b10b629SKarol Trzcinski 	if (!dfse->buf) {
2965b10b629SKarol Trzcinski 		dfse->buf = devm_kmalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL);
2975b10b629SKarol Trzcinski 		if (!dfse->buf)
2985b10b629SKarol Trzcinski 			return -ENOMEM;
2995b10b629SKarol Trzcinski 		dfse->size = PAGE_SIZE;
3005b10b629SKarol Trzcinski 	}
3015b10b629SKarol Trzcinski 
3025b10b629SKarol Trzcinski 	return 0;
3035b10b629SKarol Trzcinski }
3045b10b629SKarol Trzcinski 
3055b10b629SKarol Trzcinski static const struct file_operations memory_info_fops = {
3065b10b629SKarol Trzcinski 	.open = memory_info_open,
3075b10b629SKarol Trzcinski 	.read = memory_info_read,
3085b10b629SKarol Trzcinski 	.llseek = default_llseek,
3095b10b629SKarol Trzcinski };
3105b10b629SKarol Trzcinski 
snd_sof_dbg_memory_info_init(struct snd_sof_dev * sdev)3115b10b629SKarol Trzcinski int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev)
3125b10b629SKarol Trzcinski {
3135b10b629SKarol Trzcinski 	struct snd_sof_dfsentry *dfse;
3145b10b629SKarol Trzcinski 
3155b10b629SKarol Trzcinski 	dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
3165b10b629SKarol Trzcinski 	if (!dfse)
3175b10b629SKarol Trzcinski 		return -ENOMEM;
3185b10b629SKarol Trzcinski 
3195b10b629SKarol Trzcinski 	/* don't allocate buffer before first usage, to save memory when unused */
3205b10b629SKarol Trzcinski 	dfse->type = SOF_DFSENTRY_TYPE_BUF;
3215b10b629SKarol Trzcinski 	dfse->sdev = sdev;
3225b10b629SKarol Trzcinski 
3235b10b629SKarol Trzcinski 	debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops);
3245b10b629SKarol Trzcinski 
3255b10b629SKarol Trzcinski 	/* add to dfsentry list */
3265b10b629SKarol Trzcinski 	list_add(&dfse->list, &sdev->dfsentry_list);
3275b10b629SKarol Trzcinski 	return 0;
3285b10b629SKarol Trzcinski }
3295b10b629SKarol Trzcinski EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init);
3305b10b629SKarol Trzcinski 
snd_sof_dbg_init(struct snd_sof_dev * sdev)33186b02f71SLiam Girdwood int snd_sof_dbg_init(struct snd_sof_dev *sdev)
33286b02f71SLiam Girdwood {
333856601e5SPierre-Louis Bossart 	struct snd_sof_dsp_ops *ops = sof_ops(sdev);
33486b02f71SLiam Girdwood 	const struct snd_sof_debugfs_map *map;
33586b02f71SLiam Girdwood 	int i;
33686b02f71SLiam Girdwood 	int err;
33786b02f71SLiam Girdwood 
33886b02f71SLiam Girdwood 	/* use "sof" as top level debugFS dir */
33986b02f71SLiam Girdwood 	sdev->debugfs_root = debugfs_create_dir("sof", NULL);
34086b02f71SLiam Girdwood 
34186b02f71SLiam Girdwood 	/* init dfsentry list */
34286b02f71SLiam Girdwood 	INIT_LIST_HEAD(&sdev->dfsentry_list);
34386b02f71SLiam Girdwood 
34486b02f71SLiam Girdwood 	/* create debugFS files for platform specific MMIO/DSP memories */
34586b02f71SLiam Girdwood 	for (i = 0; i < ops->debug_map_count; i++) {
34686b02f71SLiam Girdwood 		map = &ops->debug_map[i];
34786b02f71SLiam Girdwood 
34886b02f71SLiam Girdwood 		err = snd_sof_debugfs_io_item(sdev, sdev->bar[map->bar] +
34986b02f71SLiam Girdwood 					      map->offset, map->size,
35086b02f71SLiam Girdwood 					      map->name, map->access_type);
35186b02f71SLiam Girdwood 		/* errors are only due to memory allocation, not debugfs */
35286b02f71SLiam Girdwood 		if (err < 0)
35386b02f71SLiam Girdwood 			return err;
35486b02f71SLiam Girdwood 	}
35586b02f71SLiam Girdwood 
3569a9134fdSCurtis Malainey 	return snd_sof_debugfs_buf_item(sdev, &sdev->fw_state,
3579a9134fdSCurtis Malainey 					sizeof(sdev->fw_state),
3589a9134fdSCurtis Malainey 					"fw_state", 0444);
35986b02f71SLiam Girdwood }
36086b02f71SLiam Girdwood EXPORT_SYMBOL_GPL(snd_sof_dbg_init);
36186b02f71SLiam Girdwood 
snd_sof_free_debug(struct snd_sof_dev * sdev)36286b02f71SLiam Girdwood void snd_sof_free_debug(struct snd_sof_dev *sdev)
36386b02f71SLiam Girdwood {
36486b02f71SLiam Girdwood 	debugfs_remove_recursive(sdev->debugfs_root);
36586b02f71SLiam Girdwood }
36686b02f71SLiam Girdwood EXPORT_SYMBOL_GPL(snd_sof_free_debug);
3679a06508bSLiam Girdwood 
368c05ec071SPeter Ujfalusi static const struct soc_fw_state_info {
369d41607d3SPeter Ujfalusi 	enum sof_fw_state state;
370c05ec071SPeter Ujfalusi 	const char *name;
371c05ec071SPeter Ujfalusi } fw_state_dbg[] = {
372c05ec071SPeter Ujfalusi 	{SOF_FW_BOOT_NOT_STARTED, "SOF_FW_BOOT_NOT_STARTED"},
37328d40e7aSPeter Ujfalusi 	{SOF_DSPLESS_MODE, "SOF_DSPLESS_MODE"},
374c05ec071SPeter Ujfalusi 	{SOF_FW_BOOT_PREPARE, "SOF_FW_BOOT_PREPARE"},
375c05ec071SPeter Ujfalusi 	{SOF_FW_BOOT_IN_PROGRESS, "SOF_FW_BOOT_IN_PROGRESS"},
376c05ec071SPeter Ujfalusi 	{SOF_FW_BOOT_FAILED, "SOF_FW_BOOT_FAILED"},
377c05ec071SPeter Ujfalusi 	{SOF_FW_BOOT_READY_FAILED, "SOF_FW_BOOT_READY_FAILED"},
378b2e9eb3aSPeter Ujfalusi 	{SOF_FW_BOOT_READY_OK, "SOF_FW_BOOT_READY_OK"},
379c05ec071SPeter Ujfalusi 	{SOF_FW_BOOT_COMPLETE, "SOF_FW_BOOT_COMPLETE"},
3804e1f8648SPeter Ujfalusi 	{SOF_FW_CRASHED, "SOF_FW_CRASHED"},
381c05ec071SPeter Ujfalusi };
382c05ec071SPeter Ujfalusi 
snd_sof_dbg_print_fw_state(struct snd_sof_dev * sdev,const char * level)3830152b8a2SPeter Ujfalusi static void snd_sof_dbg_print_fw_state(struct snd_sof_dev *sdev, const char *level)
384c05ec071SPeter Ujfalusi {
385c05ec071SPeter Ujfalusi 	int i;
386c05ec071SPeter Ujfalusi 
387c05ec071SPeter Ujfalusi 	for (i = 0; i < ARRAY_SIZE(fw_state_dbg); i++) {
388c05ec071SPeter Ujfalusi 		if (sdev->fw_state == fw_state_dbg[i].state) {
3890152b8a2SPeter Ujfalusi 			dev_printk(level, sdev->dev, "fw_state: %s (%d)\n",
3900152b8a2SPeter Ujfalusi 				   fw_state_dbg[i].name, i);
391c05ec071SPeter Ujfalusi 			return;
392c05ec071SPeter Ujfalusi 		}
393c05ec071SPeter Ujfalusi 	}
394c05ec071SPeter Ujfalusi 
3950152b8a2SPeter Ujfalusi 	dev_printk(level, sdev->dev, "fw_state: UNKNOWN (%d)\n", sdev->fw_state);
396c05ec071SPeter Ujfalusi }
397c05ec071SPeter Ujfalusi 
snd_sof_dsp_dbg_dump(struct snd_sof_dev * sdev,const char * msg,u32 flags)3982f148430SPeter Ujfalusi void snd_sof_dsp_dbg_dump(struct snd_sof_dev *sdev, const char *msg, u32 flags)
399360fa323SPeter Ujfalusi {
40091888125SPierre-Louis Bossart 	char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR;
40112b401f4SPeter Ujfalusi 	bool print_all = sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS);
40234346a38SPeter Ujfalusi 
40334346a38SPeter Ujfalusi 	if (flags & SOF_DBG_DUMP_OPTIONAL && !print_all)
40434346a38SPeter Ujfalusi 		return;
40534346a38SPeter Ujfalusi 
406360fa323SPeter Ujfalusi 	if (sof_ops(sdev)->dbg_dump && !sdev->dbg_dump_printed) {
4070152b8a2SPeter Ujfalusi 		dev_printk(level, sdev->dev,
4080152b8a2SPeter Ujfalusi 			   "------------[ DSP dump start ]------------\n");
4092f148430SPeter Ujfalusi 		if (msg)
4100152b8a2SPeter Ujfalusi 			dev_printk(level, sdev->dev, "%s\n", msg);
4110152b8a2SPeter Ujfalusi 		snd_sof_dbg_print_fw_state(sdev, level);
412360fa323SPeter Ujfalusi 		sof_ops(sdev)->dbg_dump(sdev, flags);
4130152b8a2SPeter Ujfalusi 		dev_printk(level, sdev->dev,
4140152b8a2SPeter Ujfalusi 			   "------------[ DSP dump end ]------------\n");
41534346a38SPeter Ujfalusi 		if (!print_all)
416360fa323SPeter Ujfalusi 			sdev->dbg_dump_printed = true;
4172f148430SPeter Ujfalusi 	} else if (msg) {
4180152b8a2SPeter Ujfalusi 		dev_printk(level, sdev->dev, "%s\n", msg);
419360fa323SPeter Ujfalusi 	}
420360fa323SPeter Ujfalusi }
421360fa323SPeter Ujfalusi EXPORT_SYMBOL(snd_sof_dsp_dbg_dump);
422360fa323SPeter Ujfalusi 
snd_sof_ipc_dump(struct snd_sof_dev * sdev)423360fa323SPeter Ujfalusi static void snd_sof_ipc_dump(struct snd_sof_dev *sdev)
424360fa323SPeter Ujfalusi {
425360fa323SPeter Ujfalusi 	if (sof_ops(sdev)->ipc_dump  && !sdev->ipc_dump_printed) {
426360fa323SPeter Ujfalusi 		dev_err(sdev->dev, "------------[ IPC dump start ]------------\n");
427360fa323SPeter Ujfalusi 		sof_ops(sdev)->ipc_dump(sdev);
428360fa323SPeter Ujfalusi 		dev_err(sdev->dev, "------------[ IPC dump end ]------------\n");
42912b401f4SPeter Ujfalusi 		if (!sof_debug_check_flag(SOF_DBG_PRINT_ALL_DUMPS))
430360fa323SPeter Ujfalusi 			sdev->ipc_dump_printed = true;
431360fa323SPeter Ujfalusi 	}
432360fa323SPeter Ujfalusi }
433360fa323SPeter Ujfalusi 
snd_sof_handle_fw_exception(struct snd_sof_dev * sdev,const char * msg)434145cb4e7SPeter Ujfalusi void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev, const char *msg)
4359a06508bSLiam Girdwood {
4369a06508bSLiam Girdwood 	if (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_RETAIN_DSP_CONTEXT) ||
43712b401f4SPeter Ujfalusi 	    sof_debug_check_flag(SOF_DBG_RETAIN_CTX)) {
4389a06508bSLiam Girdwood 		/* should we prevent DSP entering D3 ? */
4399ff90859SPeter Ujfalusi 		if (!sdev->ipc_dump_printed)
4409ff90859SPeter Ujfalusi 			dev_info(sdev->dev,
441*3de97586SPierre-Louis Bossart 				 "Attempting to prevent DSP from entering D3 state to preserve context\n");
442*3de97586SPierre-Louis Bossart 		pm_runtime_get_if_in_use(sdev->dev);
4439a06508bSLiam Girdwood 	}
4449a06508bSLiam Girdwood 
4459a06508bSLiam Girdwood 	/* dump vital information to the logs */
4469a06508bSLiam Girdwood 	snd_sof_ipc_dump(sdev);
447145cb4e7SPeter Ujfalusi 	snd_sof_dsp_dbg_dump(sdev, msg, SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX);
4481dedbe4fSPeter Ujfalusi 	sof_fw_trace_fw_crashed(sdev);
4499a06508bSLiam Girdwood }
4509a06508bSLiam Girdwood EXPORT_SYMBOL(snd_sof_handle_fw_exception);
451