1 // SPDX-License-Identifier: GPL-2.0-only
3 // Copyright(c) 2022 Intel Corporation
5 #include <linux/debugfs.h>
6 #include <linux/sched/signal.h>
7 #include <linux/sched/clock.h>
8 #include <sound/sof/ipc4/header.h>
10 #include "ipc4-priv.h"
13 * debug info window is organized in 16 (equal sized) pages:
15 * ------------------------
16 * | Page0 - descriptors |
17 * ------------------------
19 * ------------------------
21 * ------------------------
23 * ------------------------
25 * ------------------------
27 * ------------------------
29 * The slot size == page size
31 * The first page contains descriptors for the remaining 15 cores
32 * The slot descriptor is:
37 * Log buffer slots have the following layout:
42 * The two pointers are offsets within the buffer.
45 #define FW_EPOCH_DELTA 11644473600LL
47 #define MAX_ALLOWED_LIBRARIES 16
49 #define SOF_IPC4_INVALID_SLOT_OFFSET 0xffffffff
51 /* for debug and critical types */
52 #define SOF_MTRACE_SLOT_CORE_MASK GENMASK(7, 0)
53 #define SOF_MTRACE_SLOT_TYPE_MASK GENMASK(31, 8)
55 #define DEFAULT_AGING_TIMER_PERIOD_MS 0x100
56 #define DEFAULT_FIFO_FULL_TIMER_PERIOD_MS 0x1000
58 /* ipc4 log level and source definitions for logs_priorities_mask */
59 #define SOF_MTRACE_LOG_LEVEL_CRITICAL BIT(0)
60 #define SOF_MTRACE_LOG_LEVEL_ERROR BIT(1)
61 #define SOF_MTRACE_LOG_LEVEL_WARNING BIT(2)
62 #define SOF_MTRACE_LOG_LEVEL_INFO BIT(3)
63 #define SOF_MTRACE_LOG_LEVEL_VERBOSE BIT(4)
64 #define SOF_MTRACE_LOG_SOURCE_INFRA BIT(5) /* log source 0 */
65 #define SOF_MTRACE_LOG_SOURCE_HAL BIT(6)
66 #define SOF_MTRACE_LOG_SOURCE_MODULE BIT(7)
67 #define SOF_MTRACE_LOG_SOURCE_AUDIO BIT(8)
68 #define SOF_MTRACE_LOG_SOURCE_SCHEDULER BIT(9)
69 #define SOF_MTRACE_LOG_SOURCE_ULP_INFRA BIT(10)
70 #define SOF_MTRACE_LOG_SOURCE_ULP_MODULE BIT(11)
71 #define SOF_MTRACE_LOG_SOURCE_VISION BIT(12) /* log source 7 */
72 #define DEFAULT_LOGS_PRIORITIES_MASK (SOF_MTRACE_LOG_LEVEL_CRITICAL | \
73 SOF_MTRACE_LOG_LEVEL_ERROR | \
74 SOF_MTRACE_LOG_LEVEL_WARNING | \
75 SOF_MTRACE_LOG_LEVEL_INFO | \
76 SOF_MTRACE_LOG_SOURCE_INFRA | \
77 SOF_MTRACE_LOG_SOURCE_HAL | \
78 SOF_MTRACE_LOG_SOURCE_MODULE | \
79 SOF_MTRACE_LOG_SOURCE_AUDIO)
81 struct sof_log_state_info
{
82 u32 aging_timer_period
;
83 u32 fifo_full_timer_period
;
85 u32 logs_priorities_mask
[MAX_ALLOWED_LIBRARIES
];
88 enum sof_mtrace_state
{
90 SOF_MTRACE_INITIALIZING
,
94 struct sof_mtrace_core_data
{
95 struct snd_sof_dev
*sdev
;
100 struct mutex buffer_lock
; /* for log_buffer alloc/free */
103 /* pos update IPC arrived before the slot offset is known, queried */
104 bool delayed_pos_update
;
105 wait_queue_head_t trace_sleep
;
108 struct sof_mtrace_priv
{
109 struct snd_sof_dev
*sdev
;
110 enum sof_mtrace_state mtrace_state
;
111 struct sof_log_state_info state_info
;
113 struct sof_mtrace_core_data cores
[];
116 static int sof_ipc4_mtrace_dfs_open(struct inode
*inode
, struct file
*file
)
118 struct sof_mtrace_core_data
*core_data
= inode
->i_private
;
121 mutex_lock(&core_data
->buffer_lock
);
123 if (core_data
->log_buffer
) {
128 ret
= debugfs_file_get(file
->f_path
.dentry
);
132 core_data
->log_buffer
= kmalloc(SOF_IPC4_DEBUG_SLOT_SIZE
, GFP_KERNEL
);
133 if (!core_data
->log_buffer
) {
134 debugfs_file_put(file
->f_path
.dentry
);
139 ret
= simple_open(inode
, file
);
141 kfree(core_data
->log_buffer
);
142 debugfs_file_put(file
->f_path
.dentry
);
146 mutex_unlock(&core_data
->buffer_lock
);
151 static bool sof_wait_mtrace_avail(struct sof_mtrace_core_data
*core_data
)
153 wait_queue_entry_t wait
;
155 /* data immediately available */
156 if (core_data
->host_read_ptr
!= core_data
->dsp_write_ptr
)
159 /* wait for available trace data from FW */
160 init_waitqueue_entry(&wait
, current
);
161 set_current_state(TASK_INTERRUPTIBLE
);
162 add_wait_queue(&core_data
->trace_sleep
, &wait
);
164 if (!signal_pending(current
)) {
165 /* set timeout to max value, no error code */
166 schedule_timeout(MAX_SCHEDULE_TIMEOUT
);
168 remove_wait_queue(&core_data
->trace_sleep
, &wait
);
170 if (core_data
->host_read_ptr
!= core_data
->dsp_write_ptr
)
176 static ssize_t
sof_ipc4_mtrace_dfs_read(struct file
*file
, char __user
*buffer
,
177 size_t count
, loff_t
*ppos
)
179 struct sof_mtrace_core_data
*core_data
= file
->private_data
;
180 u32 log_buffer_offset
, log_buffer_size
, read_ptr
, write_ptr
;
181 struct snd_sof_dev
*sdev
= core_data
->sdev
;
182 struct sof_mtrace_priv
*priv
= sdev
->fw_trace_data
;
183 void *log_buffer
= core_data
->log_buffer
;
188 /* check pos and count */
191 if (!count
|| count
< sizeof(avail
))
194 /* get available count based on current host offset */
195 if (!sof_wait_mtrace_avail(core_data
)) {
196 /* No data available */
198 if (copy_to_user(buffer
, &avail
, sizeof(avail
)))
204 if (core_data
->slot_offset
== SOF_IPC4_INVALID_SLOT_OFFSET
)
207 /* The log data buffer starts after the two pointer in the slot */
208 log_buffer_offset
= core_data
->slot_offset
+ (sizeof(u32
) * 2);
209 /* The log data size excludes the pointers */
210 log_buffer_size
= SOF_IPC4_DEBUG_SLOT_SIZE
- (sizeof(u32
) * 2);
212 read_ptr
= core_data
->host_read_ptr
;
213 write_ptr
= core_data
->dsp_write_ptr
;
215 if (read_ptr
< write_ptr
)
216 avail
= write_ptr
- read_ptr
;
218 avail
= log_buffer_size
- read_ptr
+ write_ptr
;
223 if (avail
> log_buffer_size
)
224 avail
= log_buffer_size
;
226 /* Need space for the initial u32 of the avail */
227 if (avail
> count
- sizeof(avail
))
228 avail
= count
- sizeof(avail
);
230 if (sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS
))
232 "core%d, host read: %#x, dsp write: %#x, avail: %#x\n",
233 core_data
->id
, read_ptr
, write_ptr
, avail
);
235 if (read_ptr
< write_ptr
) {
236 /* Read data between read pointer and write pointer */
237 sof_mailbox_read(sdev
, log_buffer_offset
+ read_ptr
, log_buffer
, avail
);
239 /* read from read pointer to end of the slot */
240 sof_mailbox_read(sdev
, log_buffer_offset
+ read_ptr
, log_buffer
,
242 /* read from slot start to write pointer */
244 sof_mailbox_read(sdev
, log_buffer_offset
,
245 (u8
*)(log_buffer
) + avail
- write_ptr
,
249 /* first write the number of bytes we have gathered */
250 ret
= copy_to_user(buffer
, &avail
, sizeof(avail
));
254 /* Followed by the data itself */
255 ret
= copy_to_user(buffer
+ sizeof(avail
), log_buffer
, avail
);
259 /* Update the host_read_ptr in the slot for this core */
261 if (read_ptr
>= log_buffer_size
)
262 read_ptr
-= log_buffer_size
;
263 sof_mailbox_write(sdev
, core_data
->slot_offset
, &read_ptr
, sizeof(read_ptr
));
265 /* Only update the host_read_ptr if mtrace is enabled */
266 if (priv
->mtrace_state
!= SOF_MTRACE_DISABLED
)
267 core_data
->host_read_ptr
= read_ptr
;
270 * Ask for a new buffer from user space for the next chunk, not
271 * streaming due to the heading number of bytes value.
278 static int sof_ipc4_mtrace_dfs_release(struct inode
*inode
, struct file
*file
)
280 struct sof_mtrace_core_data
*core_data
= inode
->i_private
;
282 debugfs_file_put(file
->f_path
.dentry
);
284 mutex_lock(&core_data
->buffer_lock
);
285 kfree(core_data
->log_buffer
);
286 core_data
->log_buffer
= NULL
;
287 mutex_unlock(&core_data
->buffer_lock
);
292 static const struct file_operations sof_dfs_mtrace_fops
= {
293 .open
= sof_ipc4_mtrace_dfs_open
,
294 .read
= sof_ipc4_mtrace_dfs_read
,
295 .llseek
= default_llseek
,
296 .release
= sof_ipc4_mtrace_dfs_release
,
298 .owner
= THIS_MODULE
,
301 static ssize_t
sof_ipc4_priority_mask_dfs_read(struct file
*file
, char __user
*to
,
302 size_t count
, loff_t
*ppos
)
304 struct sof_mtrace_priv
*priv
= file
->private_data
;
305 int i
, ret
, offset
, remaining
;
309 * one entry (14 char + new line = 15):
314 buf
= kzalloc(241, GFP_KERNEL
);
318 for (i
= 0; i
< MAX_ALLOWED_LIBRARIES
; i
++) {
319 offset
= strlen(buf
);
320 remaining
= 241 - offset
;
321 snprintf(buf
+ offset
, remaining
, "%2d: 0x%08x\n", i
,
322 priv
->state_info
.logs_priorities_mask
[i
]);
325 ret
= simple_read_from_buffer(to
, count
, ppos
, buf
, strlen(buf
));
331 static ssize_t
sof_ipc4_priority_mask_dfs_write(struct file
*file
,
332 const char __user
*from
,
333 size_t count
, loff_t
*ppos
)
335 struct sof_mtrace_priv
*priv
= file
->private_data
;
342 * To update Nth mask entry, write:
343 * "N,0x1234" or "N,1234" to the debugfs file
344 * The mask will be interpreted as hexadecimal number
346 buf
= memdup_user_nul(from
, count
);
350 ret
= sscanf(buf
, "%u,0x%x", &id
, &mask
);
352 ret
= sscanf(buf
, "%u,%x", &id
, &mask
);
359 if (id
>= MAX_ALLOWED_LIBRARIES
) {
364 priv
->state_info
.logs_priorities_mask
[id
] = mask
;
372 static const struct file_operations sof_dfs_priority_mask_fops
= {
374 .read
= sof_ipc4_priority_mask_dfs_read
,
375 .write
= sof_ipc4_priority_mask_dfs_write
,
376 .llseek
= default_llseek
,
378 .owner
= THIS_MODULE
,
381 static int mtrace_debugfs_create(struct snd_sof_dev
*sdev
)
383 struct sof_mtrace_priv
*priv
= sdev
->fw_trace_data
;
384 struct dentry
*dfs_root
;
388 dfs_root
= debugfs_create_dir("mtrace", sdev
->debugfs_root
);
389 if (IS_ERR_OR_NULL(dfs_root
))
392 /* Create files for the logging parameters */
393 debugfs_create_u32("aging_timer_period", 0644, dfs_root
,
394 &priv
->state_info
.aging_timer_period
);
395 debugfs_create_u32("fifo_full_timer_period", 0644, dfs_root
,
396 &priv
->state_info
.fifo_full_timer_period
);
397 debugfs_create_file("logs_priorities_mask", 0644, dfs_root
, priv
,
398 &sof_dfs_priority_mask_fops
);
400 /* Separate log files per core */
401 for (i
= 0; i
< sdev
->num_cores
; i
++) {
402 snprintf(dfs_name
, sizeof(dfs_name
), "core%d", i
);
403 debugfs_create_file(dfs_name
, 0444, dfs_root
, &priv
->cores
[i
],
404 &sof_dfs_mtrace_fops
);
410 static int ipc4_mtrace_enable(struct snd_sof_dev
*sdev
)
412 struct sof_mtrace_priv
*priv
= sdev
->fw_trace_data
;
413 const struct sof_ipc_ops
*iops
= sdev
->ipc
->ops
;
414 struct sof_ipc4_msg msg
;
418 if (priv
->mtrace_state
!= SOF_MTRACE_DISABLED
)
421 msg
.primary
= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG
);
422 msg
.primary
|= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST
);
423 msg
.primary
|= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID
);
424 msg
.primary
|= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID
);
425 msg
.extension
= SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_SYSTEM_TIME
);
428 * local_clock() is used to align with dmesg, so both kernel and firmware logs have
429 * the same base and a minor delta due to the IPC. system time is in us format but
430 * local_clock() returns the time in ns, so convert to ns.
432 system_time
= div64_u64(local_clock(), NSEC_PER_USEC
);
433 msg
.data_size
= sizeof(system_time
);
434 msg
.data_ptr
= &system_time
;
435 ret
= iops
->set_get_data(sdev
, &msg
, msg
.data_size
, true);
439 msg
.extension
= SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_ENABLE_LOGS
);
441 priv
->state_info
.enable
= 1;
443 msg
.data_size
= sizeof(priv
->state_info
);
444 msg
.data_ptr
= &priv
->state_info
;
446 priv
->mtrace_state
= SOF_MTRACE_INITIALIZING
;
447 ret
= iops
->set_get_data(sdev
, &msg
, msg
.data_size
, true);
449 priv
->mtrace_state
= SOF_MTRACE_DISABLED
;
453 priv
->mtrace_state
= SOF_MTRACE_ENABLED
;
458 static void ipc4_mtrace_disable(struct snd_sof_dev
*sdev
)
460 struct sof_mtrace_priv
*priv
= sdev
->fw_trace_data
;
461 const struct sof_ipc_ops
*iops
= sdev
->ipc
->ops
;
462 struct sof_ipc4_msg msg
;
465 if (priv
->mtrace_state
== SOF_MTRACE_DISABLED
)
468 msg
.primary
= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG
);
469 msg
.primary
|= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST
);
470 msg
.primary
|= SOF_IPC4_MOD_ID(SOF_IPC4_MOD_INIT_BASEFW_MOD_ID
);
471 msg
.primary
|= SOF_IPC4_MOD_INSTANCE(SOF_IPC4_MOD_INIT_BASEFW_INSTANCE_ID
);
472 msg
.extension
= SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_FW_PARAM_ENABLE_LOGS
);
474 priv
->state_info
.enable
= 0;
476 msg
.data_size
= sizeof(priv
->state_info
);
477 msg
.data_ptr
= &priv
->state_info
;
478 iops
->set_get_data(sdev
, &msg
, msg
.data_size
, true);
480 priv
->mtrace_state
= SOF_MTRACE_DISABLED
;
482 for (i
= 0; i
< sdev
->num_cores
; i
++) {
483 struct sof_mtrace_core_data
*core_data
= &priv
->cores
[i
];
485 core_data
->host_read_ptr
= 0;
486 core_data
->dsp_write_ptr
= 0;
487 wake_up(&core_data
->trace_sleep
);
492 * Each DSP core logs to a dedicated slot.
493 * Parse the slot descriptors at debug_box offset to find the debug log slots
494 * and map them to cores.
495 * There are 15 slots and therefore 15 descriptors to check (MAX_MTRACE_SLOTS)
497 static void sof_mtrace_find_core_slots(struct snd_sof_dev
*sdev
)
499 struct sof_mtrace_priv
*priv
= sdev
->fw_trace_data
;
500 struct sof_mtrace_core_data
*core_data
;
501 u32 slot_desc_type_offset
, type
, core
;
504 for (i
= 0; i
< SOF_IPC4_MAX_DEBUG_SLOTS
; i
++) {
505 /* The type is the second u32 in the slot descriptor */
506 slot_desc_type_offset
= sdev
->debug_box
.offset
;
507 slot_desc_type_offset
+= SOF_IPC4_DEBUG_DESCRIPTOR_SIZE
* i
+ sizeof(u32
);
508 sof_mailbox_read(sdev
, slot_desc_type_offset
, &type
, sizeof(type
));
510 if ((type
& SOF_MTRACE_SLOT_TYPE_MASK
) == SOF_IPC4_DEBUG_SLOT_DEBUG_LOG
) {
511 core
= type
& SOF_MTRACE_SLOT_CORE_MASK
;
513 if (core
>= sdev
->num_cores
) {
514 dev_dbg(sdev
->dev
, "core%u is invalid for slot%d\n",
519 core_data
= &priv
->cores
[core
];
521 * The area reserved for descriptors have the same size
523 * In other words: slot0 starts at
524 * debug_box + SOF_MTRACE_SLOT_SIZE offset
526 core_data
->slot_offset
= sdev
->debug_box
.offset
;
527 core_data
->slot_offset
+= SOF_IPC4_DEBUG_SLOT_SIZE
* (i
+ 1);
528 dev_dbg(sdev
->dev
, "slot%d is used for core%u\n", i
, core
);
529 if (core_data
->delayed_pos_update
) {
530 sof_ipc4_mtrace_update_pos(sdev
, core
);
531 core_data
->delayed_pos_update
= false;
534 dev_dbg(sdev
->dev
, "slot%d is not a log slot (%#x)\n", i
, type
);
539 static int ipc4_mtrace_init(struct snd_sof_dev
*sdev
)
541 struct sof_ipc4_fw_data
*ipc4_data
= sdev
->private;
542 struct sof_mtrace_priv
*priv
;
545 if (sdev
->fw_trace_data
) {
546 dev_err(sdev
->dev
, "fw_trace_data has been already allocated\n");
550 if (!ipc4_data
->mtrace_log_bytes
||
551 ipc4_data
->mtrace_type
!= SOF_IPC4_MTRACE_INTEL_CAVS_2
) {
552 sdev
->fw_trace_is_supported
= false;
556 priv
= devm_kzalloc(sdev
->dev
, struct_size(priv
, cores
, sdev
->num_cores
),
561 sdev
->fw_trace_data
= priv
;
563 /* Set initial values for mtrace parameters */
564 priv
->state_info
.aging_timer_period
= DEFAULT_AGING_TIMER_PERIOD_MS
;
565 priv
->state_info
.fifo_full_timer_period
= DEFAULT_FIFO_FULL_TIMER_PERIOD_MS
;
566 /* Only enable basefw logs initially (index 0 is always basefw) */
567 priv
->state_info
.logs_priorities_mask
[0] = DEFAULT_LOGS_PRIORITIES_MASK
;
569 for (i
= 0; i
< sdev
->num_cores
; i
++) {
570 struct sof_mtrace_core_data
*core_data
= &priv
->cores
[i
];
572 init_waitqueue_head(&core_data
->trace_sleep
);
573 mutex_init(&core_data
->buffer_lock
);
574 core_data
->sdev
= sdev
;
578 ret
= ipc4_mtrace_enable(sdev
);
581 * Mark firmware tracing as not supported and return 0 to not
582 * block the whole audio stack
584 sdev
->fw_trace_is_supported
= false;
585 dev_dbg(sdev
->dev
, "initialization failed, fw tracing is disabled\n");
589 sof_mtrace_find_core_slots(sdev
);
591 ret
= mtrace_debugfs_create(sdev
);
593 ipc4_mtrace_disable(sdev
);
598 static void ipc4_mtrace_free(struct snd_sof_dev
*sdev
)
600 ipc4_mtrace_disable(sdev
);
603 static int sof_ipc4_mtrace_update_pos_all_cores(struct snd_sof_dev
*sdev
)
607 for (i
= 0; i
< sdev
->num_cores
; i
++)
608 sof_ipc4_mtrace_update_pos(sdev
, i
);
613 int sof_ipc4_mtrace_update_pos(struct snd_sof_dev
*sdev
, int core
)
615 struct sof_mtrace_priv
*priv
= sdev
->fw_trace_data
;
616 struct sof_mtrace_core_data
*core_data
;
618 if (!sdev
->fw_trace_is_supported
||
619 priv
->mtrace_state
== SOF_MTRACE_DISABLED
)
622 if (core
>= sdev
->num_cores
)
625 core_data
= &priv
->cores
[core
];
627 if (core_data
->slot_offset
== SOF_IPC4_INVALID_SLOT_OFFSET
) {
628 core_data
->delayed_pos_update
= true;
632 /* Read out the dsp_write_ptr from the slot for this core */
633 sof_mailbox_read(sdev
, core_data
->slot_offset
+ sizeof(u32
),
634 &core_data
->dsp_write_ptr
, 4);
635 core_data
->dsp_write_ptr
-= core_data
->dsp_write_ptr
% 4;
637 if (sof_debug_check_flag(SOF_DBG_PRINT_DMA_POSITION_UPDATE_LOGS
))
638 dev_dbg(sdev
->dev
, "core%d, host read: %#x, dsp write: %#x",
639 core
, core_data
->host_read_ptr
, core_data
->dsp_write_ptr
);
641 wake_up(&core_data
->trace_sleep
);
646 static void ipc4_mtrace_fw_crashed(struct snd_sof_dev
*sdev
)
649 * The DSP might not be able to send SOF_IPC4_NOTIFY_LOG_BUFFER_STATUS
650 * messages anymore, so check the log buffer status on all
651 * cores and process any pending messages.
653 sof_ipc4_mtrace_update_pos_all_cores(sdev
);
656 static int ipc4_mtrace_resume(struct snd_sof_dev
*sdev
)
658 return ipc4_mtrace_enable(sdev
);
661 static void ipc4_mtrace_suspend(struct snd_sof_dev
*sdev
, pm_message_t pm_state
)
663 ipc4_mtrace_disable(sdev
);
666 const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops
= {
667 .init
= ipc4_mtrace_init
,
668 .free
= ipc4_mtrace_free
,
669 .fw_crashed
= ipc4_mtrace_fw_crashed
,
670 .suspend
= ipc4_mtrace_suspend
,
671 .resume
= ipc4_mtrace_resume
,