1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) |
2 | // |
3 | // This file is provided under a dual BSD/GPLv2 license. When using or |
4 | // redistributing this file, you may do so under either license. |
5 | // |
6 | // Copyright(c) 2018-2023 Intel Corporation. All rights reserved. |
7 | // |
8 | |
9 | #include <linux/debugfs.h> |
10 | #include <linux/io.h> |
11 | #include <linux/pm_runtime.h> |
12 | #include <sound/sof/debug.h> |
13 | #include <sound/sof/ipc4/header.h> |
14 | #include "sof-priv.h" |
15 | #include "ops.h" |
16 | #include "ipc4-telemetry.h" |
17 | #include "ipc4-priv.h" |
18 | |
19 | static void __iomem *sof_ipc4_query_exception_address(struct snd_sof_dev *sdev) |
20 | { |
21 | u32 type = SOF_IPC4_DEBUG_SLOT_TELEMETRY; |
22 | size_t telemetry_slot_offset; |
23 | u32 offset; |
24 | |
25 | telemetry_slot_offset = sof_ipc4_find_debug_slot_offset_by_type(sdev, slot_type: type); |
26 | if (!telemetry_slot_offset) |
27 | return NULL; |
28 | |
29 | /* skip the first separator magic number */ |
30 | offset = telemetry_slot_offset + sizeof(u32); |
31 | |
32 | return sdev->bar[sdev->mailbox_bar] + offset; |
33 | } |
34 | |
35 | static ssize_t sof_telemetry_entry_read(struct file *file, char __user *buffer, |
36 | size_t count, loff_t *ppos) |
37 | { |
38 | struct snd_sof_dfsentry *dfse = file->private_data; |
39 | struct snd_sof_dev *sdev = dfse->sdev; |
40 | void __iomem *io_addr; |
41 | loff_t pos = *ppos; |
42 | size_t size_ret; |
43 | u8 *buf; |
44 | |
45 | if (pos < 0) |
46 | return -EINVAL; |
47 | /* skip the first separator magic number */ |
48 | if (pos >= SOF_IPC4_DEBUG_SLOT_SIZE - 4 || !count) |
49 | return 0; |
50 | if (count > SOF_IPC4_DEBUG_SLOT_SIZE - 4 - pos) |
51 | count = SOF_IPC4_DEBUG_SLOT_SIZE - 4 - pos; |
52 | |
53 | io_addr = sof_ipc4_query_exception_address(sdev); |
54 | if (!io_addr) |
55 | return -EFAULT; |
56 | |
57 | buf = kzalloc(SOF_IPC4_DEBUG_SLOT_SIZE - 4, GFP_KERNEL); |
58 | if (!buf) |
59 | return -ENOMEM; |
60 | |
61 | memcpy_fromio(buf, io_addr, SOF_IPC4_DEBUG_SLOT_SIZE - 4); |
62 | size_ret = copy_to_user(to: buffer, from: buf + pos, n: count); |
63 | if (size_ret) { |
64 | kfree(objp: buf); |
65 | return -EFAULT; |
66 | } |
67 | |
68 | *ppos = pos + count; |
69 | kfree(objp: buf); |
70 | |
71 | return count; |
72 | } |
73 | |
74 | static const struct file_operations sof_telemetry_fops = { |
75 | .open = simple_open, |
76 | .read = sof_telemetry_entry_read, |
77 | }; |
78 | |
79 | void sof_ipc4_create_exception_debugfs_node(struct snd_sof_dev *sdev) |
80 | { |
81 | struct snd_sof_dfsentry *dfse; |
82 | |
83 | dfse = devm_kzalloc(dev: sdev->dev, size: sizeof(*dfse), GFP_KERNEL); |
84 | if (!dfse) |
85 | return; |
86 | |
87 | dfse->type = SOF_DFSENTRY_TYPE_IOMEM; |
88 | dfse->size = SOF_IPC4_DEBUG_SLOT_SIZE - 4; |
89 | dfse->access_type = SOF_DEBUGFS_ACCESS_ALWAYS; |
90 | dfse->sdev = sdev; |
91 | |
92 | list_add(new: &dfse->list, head: &sdev->dfsentry_list); |
93 | |
94 | debugfs_create_file(name: "exception" , mode: 0444, parent: sdev->debugfs_root, data: dfse, fops: &sof_telemetry_fops); |
95 | } |
96 | |