From e88a7b293d0b473d2d47a411b307f245e4b3c8f2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Tue, 15 Feb 2022 17:38:54 +0800 Subject: [PATCH] [WIP]:ASoC: SOF: add trace support for IPC4 Signed-off-by: Bard Liao --- sound/soc/sof/Makefile | 3 +- sound/soc/sof/ipc4-priv.h | 1 + sound/soc/sof/ipc4-trace.c | 366 +++++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc4-trace.h | 48 +++++ sound/soc/sof/ipc4.c | 8 + sound/soc/sof/sof-priv.h | 9 + 6 files changed, 434 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/ipc4-trace.c create mode 100644 sound/soc/sof/ipc4-trace.h diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 9a74ed116ed92a..9325f0788593cb 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -9,7 +9,8 @@ snd-sof-objs += ipc3.o ipc3-loader.o ipc3-topology.o ipc3-control.o ipc3-pcm.o\ ipc3-dtrace.o endif ifneq ($(CONFIG_SND_SOC_SOF_INTEL_IPC4),) -snd-sof-objs += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o +snd-sof-objs += ipc4.o ipc4-loader.o ipc4-topology.o ipc4-control.o ipc4-pcm.o\ + ipc4-trace.o endif # SOF client support diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h index 9492fe1796c2f1..3c4f5709e3d583 100644 --- a/sound/soc/sof/ipc4-priv.h +++ b/sound/soc/sof/ipc4-priv.h @@ -45,6 +45,7 @@ extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; extern const struct sof_ipc_tplg_ops ipc4_tplg_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops; extern const struct sof_ipc_pcm_ops ipc4_pcm_ops; +extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops; int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state); diff --git a/sound/soc/sof/ipc4-trace.c b/sound/soc/sof/ipc4-trace.c new file mode 100644 index 00000000000000..c4f1f54e543032 --- /dev/null +++ b/sound/soc/sof/ipc4-trace.c @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// Author: Rander Wang +// + +#include +#include +#include +#include "sof-priv.h" +#include "ipc4-trace.h" + +struct sof_mtrace_priv { + wait_queue_head_t trace_sleep; +}; + +static bool sof_wait_mtrace_avail(struct snd_sof_dev *sdev) +{ + struct sof_mtrace_priv *priv = sdev->fw_trace_data; + wait_queue_entry_t wait; + + /* data immediately available */ + if (sdev->host_read_ptr != sdev->dsp_write_ptr) + return TRUE; + + /* wait for available trace data from FW */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&priv->trace_sleep, &wait); + + if (!signal_pending(current)) { + /* set timeout to max value, no error code */ + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + } + remove_wait_queue(&priv->trace_sleep, &wait); + + if (sdev->host_read_ptr != sdev->dsp_write_ptr) + return TRUE; + + return false; +} + +static ssize_t sof_ipc4_mtrace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + u32 read_ptr, write_ptr; + loff_t lpos = *ppos; + u32 avail; + int ret; + + /* check pos and count */ + if (lpos < 0) + return -EINVAL; + if (!count) + return 0; + + /* get available count based on current host offset */ + if (!sof_wait_mtrace_avail(sdev)) { + dev_dbg(sdev->dev, "got unexpected error"); + return 0; + } + + read_ptr = sdev->host_read_ptr; + write_ptr = sdev->dsp_write_ptr; + // ring buffer + if (read_ptr < write_ptr) + { + // check if output buffer is sufficient in size + if ((write_ptr - read_ptr) > MEMORY_WINDOW_SLOT_SIZE) + { + dev_err(sdev->dev, "Output log buffer is insufficient"); + return -ENOMEM; + } + else + { + avail = write_ptr -read_ptr; + + ret = copy_to_user(buffer, &avail, sizeof(avail)); + if (ret) + return -EFAULT; + + ret = copy_to_user(buffer + sizeof(avail), ((u8 *)(dfse->buf) + read_ptr), avail); + if (ret) + return -EFAULT; + + // update read_ptr to write ptr for fw to update log + memcpy_toio(dfse->io_mem - 8, &write_ptr, sizeof(write_ptr)); + } + } + else + { + u32 slotBufferSize = MEMORY_WINDOW_SLOT_SIZE - sizeof(u32) - sizeof(u32); + // check if output buffer is sufficient in size + if ((slotBufferSize + write_ptr - read_ptr) > MEMORY_WINDOW_SLOT_SIZE) + { + dev_err(sdev->dev, "Output log buffer is insufficient"); + return -ENOMEM; + } + else + { + /* skip the 8 bytes of read&write pointer in debug memory box */ + avail = slotBufferSize - read_ptr + write_ptr -8; + ret = copy_to_user(buffer, &avail, sizeof(avail)); + if (ret) + return -EFAULT; + + ret = copy_to_user(buffer + sizeof(avail), (u8 *)(dfse->buf) + read_ptr, (slotBufferSize - read_ptr)); + if (ret) + return -EFAULT; + + ret = copy_to_user(buffer + sizeof(avail) + (slotBufferSize - read_ptr), (u8 *)(dfse->buf), write_ptr); + if (ret) + return -EFAULT; + + memcpy_toio(dfse->io_mem - 8, &write_ptr, sizeof(write_ptr)); + } + } + + sdev->host_read_ptr = write_ptr; + *ppos += MEMORY_WINDOW_SLOT_SIZE; + + /* move debugfs reading position */ + return MEMORY_WINDOW_SLOT_SIZE; +} + +static ssize_t sof_ipc4_mtrace_write(struct file *file, + const char __user *from, size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + char *buf; + int ret; + + buf = kmalloc(count + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = simple_write_to_buffer(buf, count, ppos, from, count); + if (ret != count) { + ret = ret >= 0 ? -EIO : ret; + goto exit; + } + + buf[count] = '\0'; + ret = kstrtouint(buf, 0, &sdev->mtrace_setting); + dev_dbg(sdev->dev, "set mtrace config %x", sdev->mtrace_setting); + + ret = ret ? 0 : sizeof(int); +exit: + kfree(buf); + return ret; +} + +static int sof_dfsentry_mtrace_release(struct inode *inode, struct file *file) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + + sdev->mtrace_is_enabled = false; + return 0; +} + +static const struct file_operations sof_dfs_mtrace_fops = { + .open = simple_open, + .read = sof_ipc4_mtrace_read, + .write = sof_ipc4_mtrace_write, + .llseek = default_llseek, + .release = sof_dfsentry_mtrace_release, +}; + +/* + * debug memory windows layout at debug_box offset: + * u32 resouce0 id | u32 slot0 id | u32 vma0 + * u32 resouce1 id | u32 slot1 id | u32 vma1 + * ... + * u32 resouce14 id | u32 slot14 id | u32 vma14 + * ... after 0x1000 + * read_ptr0 | write_ptr0 | log of 0x1000 - 8 + * read_ptr1 | write_ptr1 | log of 0x1000 - 8 + * ... + * read_ptr14 | write_ptr14 | log of 0x1000 - 8 + * + * the first slot is for base fw and others are for loadable + * modules. Now only fw log of base fw is supported +*/ +static int mtrace_debugfs_create(struct snd_sof_dev *sdev) +{ + struct snd_sof_dfsentry *dfse; + + if (!sdev) + return -EINVAL; + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->type = SOF_DFSENTRY_TYPE_IOMEM; + dfse->io_mem = sdev->bar[sdev->mailbox_bar] + sdev->debug_box.offset + + MEMORY_WINDOW_SLOT_SIZE + 8; + dfse->size = MEMORY_WINDOW_SLOT_SIZE - 8; + dfse->sdev = sdev; + + debugfs_create_file("mtrace", 0444, sdev->debugfs_root, dfse, + &sof_dfs_mtrace_fops); + + return 0; +} + +int sof_ipc4_init_mtrace(struct snd_sof_dev *sdev) +{ + struct sof_mtrace_priv *priv; + int ret; + + sdev->mtrace_is_supported = true; + + priv = devm_kzalloc(sdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sdev->fw_trace_data = priv; + + /* default enable default trace setting */ + sdev->mtrace_setting = L_DEFAULT | S_DEFAULT; + + if (sdev->first_boot) { + ret = mtrace_debugfs_create(sdev); + if (ret < 0) + return -EBUSY; + } + + init_waitqueue_head(&priv->trace_sleep); + + return 0; +} + +int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev) +{ + struct sof_mtrace_priv *priv = sdev->fw_trace_data; + int debug_address; + int res, slot; + + if (!sdev->mtrace_is_supported) + return 0; + + if (!sdev->mtrace_is_enabled) + return 0; + + debug_address = sdev->debug_box.offset; + + sof_mailbox_read(sdev, debug_address, &res, 4); + sof_mailbox_read(sdev, debug_address + 4, &slot, 4); + + dev_dbg(sdev->dev, "resource id %x, slot id %x", res, slot); + + if ((slot & SLOT_DEBUG_LOG_MASK) != SLOT_DEBUG_LOG) { + dev_dbg(sdev->dev, "invalid log msg"); + return 0; + } + + if (res == INVALID_SLOT_RESOURCE_ID) { + dev_dbg(sdev->dev, "invalid cpu id"); + return 0; + } + + debug_address += MEMORY_WINDOW_SLOT_SIZE; + sof_mailbox_read(sdev, debug_address + 4, &sdev->dsp_write_ptr, 4); + sdev->dsp_write_ptr -= sdev->dsp_write_ptr % 4; + + dev_vdbg(sdev->dev, "host read %x, dsp write %x", sdev->host_read_ptr, + sdev->dsp_write_ptr); + + wake_up(&priv->trace_sleep); + + return 0; +} + +void sof_ipc4_enable_mtrace(struct snd_sof_dev *sdev, u32 module_id) +{ + const struct sof_ipc_ops *iops = sdev->ipc->ops; + struct sof_log_setting setting; + ktime_t current_time; + struct sof_ipc4_msg msg; + + if (!sdev->mtrace_is_supported) + return; + + atomic_inc(&sdev->use_count); + + if (sdev->mtrace_is_enabled) + return; + + sdev->mtrace_is_enabled = true; + current_time = ktime_get_real(); + + msg.primary = 0x44000000; + msg.extension = 0x21400000; + msg.data_size = sizeof(current_time); + msg.data_ptr = ¤t_time; + iops->set_get_data(sdev, &msg, msg.data_size, true); + + setting.enable = 1; + setting.aging_timer_period = 0x400; + setting.fifo_full_timer_period = 0x1000; + memzero_explicit(&setting.logs_priorities_mask, 16*sizeof(uint32_t)); + setting.logs_priorities_mask[module_id] = sdev->mtrace_setting; + + msg.extension = 0x20600000; + msg.data_size = sizeof(setting); + msg.data_ptr = &setting; + iops->set_get_data(sdev, &msg, msg.data_size, true); +} + +void sof_ipc4_disable_mtrace(struct snd_sof_dev *sdev, u32 module_id) +{ + const struct sof_ipc_ops *iops = sdev->ipc->ops; + struct sof_mtrace_priv *priv = sdev->fw_trace_data; + struct sof_log_setting setting; + struct sof_ipc4_msg msg; + + if (!sdev->mtrace_is_supported || !sdev->mtrace_is_enabled) + return; + + if (!atomic_dec_and_test(&sdev->use_count)) + return; + + sdev->host_read_ptr = sdev->dsp_write_ptr = 0; + + setting.enable = 0; + setting.aging_timer_period = 0; + setting.fifo_full_timer_period = 0; + memzero_explicit(&setting.logs_priorities_mask, 16*sizeof(uint32_t)); + + msg.primary = 0x44000000; + msg.extension = 0x20600000; + msg.data_size = sizeof(setting); + msg.data_ptr = &setting; + iops->set_get_data(sdev, &msg, msg.data_size, true); + + sdev->mtrace_is_enabled = false; + wake_up(&priv->trace_sleep); +} + +static void ipc4_mtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state) +{ + sof_ipc4_disable_mtrace(sdev, SOF_IPC4_BASE_FW); +} + +static int ipc4_mtrace_resume(struct snd_sof_dev *sdev) +{ + sof_ipc4_enable_mtrace(sdev, SOF_IPC4_BASE_FW); + return 0; +} + +const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops = { + .init = sof_ipc4_init_mtrace, +// .free = ipc4_mtrace_free, +// .fw_crashed = ipc4_mtrace_fw_crashed, + .suspend = ipc4_mtrace_suspend, + .resume = ipc4_mtrace_resume, +}; diff --git a/sound/soc/sof/ipc4-trace.h b/sound/soc/sof/ipc4-trace.h new file mode 100644 index 00000000000000..a948fdc9e9018d --- /dev/null +++ b/sound/soc/sof/ipc4-trace.h @@ -0,0 +1,48 @@ + +#ifndef __INCLUDE_SOUND_SOF_IPC4_TRACE_H__ +#define __INCLUDE_SOUND_SOF_IPC4_TRACE_H__ + +#define SOF_IPC4_BASE_FW 0 + +#define IPC4_DBOX_CAVS_25_SIZE 0x10000 + +#define INVALID_SLOT_RESOURCE_ID 0xffffffff +#define MEMORY_WINDOW_SLOTS_COUNT 15 +#define MEMORY_WINDOW_SLOT_SIZE 0x1000 +#define SLOT_DEBUG_LOG 0x474f4c00 +#define SLOT_DEBUG_LOG_MASK GENMASK(31, 8) +#define MAX_ALLOWED_LIBRARIES 16 + +/* ipc4 mtrace */ +enum sof_mtrace_level +{ + L_CRITICAL = BIT(0), + L_ERROR = BIT(1), + L_WARNING = BIT(2), + L_INFO = BIT(3), + L_VERBOSE = BIT(4), + L_DEFAULT = L_CRITICAL | L_ERROR |L_INFO +}; + +enum sof_mtrace_source +{ + S_INFRA = BIT(5), + S_HAL = BIT(6), + S_MODULE = BIT(7), + S_AUDIO = BIT(8), + S_DEFAULT = S_INFRA | S_HAL | S_MODULE |S_AUDIO +}; + +struct sof_log_setting +{ + uint32_t aging_timer_period; + uint32_t fifo_full_timer_period; + uint32_t enable; + uint32_t logs_priorities_mask[MAX_ALLOWED_LIBRARIES]; +}; + +int sof_ipc4_init_mtrace(struct snd_sof_dev *sdev); +void sof_ipc4_enable_mtrace(struct snd_sof_dev *sdev, u32 module_id); +void sof_ipc4_disable_mtrace(struct snd_sof_dev *sdev, u32 module_id); +int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev); +#endif diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c index a8dea5abf2655f..f87620b8649e0e 100644 --- a/sound/soc/sof/ipc4.c +++ b/sound/soc/sof/ipc4.c @@ -13,6 +13,7 @@ #include "sof-priv.h" #include "sof-audio.h" #include "ipc4-priv.h" +#include "ipc4-trace.h" #include "ops.h" #ifdef DEBUG_VERBOSE @@ -533,6 +534,9 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg sdev->host_box.offset = outbox_offset; sdev->host_box.size = outbox_size; + sdev->debug_box.offset = snd_sof_dsp_get_window_offset(sdev, 2); + sdev->debug_box.size = IPC4_DBOX_CAVS_25_SIZE; + dev_dbg(sdev->dev, "mailbox upstream 0x%x - size 0x%x\n", inbox_offset, inbox_size); dev_dbg(sdev->dev, "mailbox downstream 0x%x - size 0x%x\n", @@ -573,6 +577,9 @@ static void sof_ipc4_rx_msg(struct snd_sof_dev *sdev) case SOF_IPC4_NOTIFY_RESOURCE_EVENT: data_size = sizeof(struct sof_ipc4_notify_resource_data); break; + case SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS: + sof_ipc4_mtrace_update_pos(sdev); + break; default: dev_dbg(sdev->dev, "%s: Unhandled DSP message: %#x|%#x\n", __func__, ipc4_msg->primary, ipc4_msg->extension); @@ -646,4 +653,5 @@ const struct sof_ipc_ops ipc4_ops = { .fw_loader = &ipc4_loader_ops, .tplg = &ipc4_tplg_ops, .pcm = &ipc4_pcm_ops, + .fw_tracing = &ipc4_mtrace_ops, }; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 52396f38dcec29..6e390113deaf99 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -51,6 +51,7 @@ #define SOF_DBG_DUMP_PCI BIT(3) /* Output this dump (at the DEBUG level) only when SOF_DBG_PRINT_ALL_DUMPS is set */ #define SOF_DBG_DUMP_OPTIONAL BIT(4) +#define SOF_DBG_ENABLE_MTRACE BIT(8) /* global debug state set by SOF_DBG_ flags */ bool sof_debug_check_flag(int mask); @@ -555,6 +556,14 @@ struct snd_sof_dev { bool msi_enabled; + /* ipc4 trace */ + bool mtrace_is_supported; + bool mtrace_is_enabled; + u32 mtrace_setting; + atomic_t use_count; + u32 host_read_ptr; + u32 dsp_write_ptr; + /* DSP core context */ u32 num_cores;