Skip to content

Commit 5a565ba

Browse files
crojewsk-intelbroonie
authored andcommitted
ASoC: Intel: avs: Probing and firmware tracing over debugfs
Define debugfs subdirectory delegated for IPC communication with DSP. Input format: uint,uint,(...) which are later translated into DWORDS sequence and further into instances of struct of interest given the IPC type. For Extractor probes, following have been enabled: - PROBE_POINT_ADD (echo <..> probe_points) - PROBE_POINT_REMOVE (echo <..> probe_points_remove) - PROBE_POINT_INFO (cat probe_points) Signed-off-by: Cezary Rojewski <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent e17527e commit 5a565ba

File tree

3 files changed

+335
-0
lines changed

3 files changed

+335
-0
lines changed

sound/soc/intel/avs/avs.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ struct avs_dev {
144144
u32 aging_timer_period;
145145
u32 fifo_full_timer_period;
146146
u32 logged_resources; /* context dependent: core or library */
147+
struct dentry *debugfs_root;
147148
/* probes */
148149
struct hdac_ext_stream *extractor;
149150
unsigned int num_probe_streams;
@@ -378,6 +379,9 @@ void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int
378379
void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len);
379380

380381
int avs_probe_platform_register(struct avs_dev *adev, const char *name);
382+
383+
void avs_debugfs_init(struct avs_dev *adev);
384+
void avs_debugfs_exit(struct avs_dev *adev);
381385
#else
382386
#define AVS_SET_ENABLE_LOGS_OP(name)
383387

@@ -399,6 +403,9 @@ static inline int avs_probe_platform_register(struct avs_dev *adev, const char *
399403
{
400404
return 0;
401405
}
406+
407+
static inline void avs_debugfs_init(struct avs_dev *adev) { }
408+
static inline void avs_debugfs_exit(struct avs_dev *adev) { }
402409
#endif
403410

404411
#endif /* __SOUND_SOC_INTEL_AVS_H */

sound/soc/intel/avs/core.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ static void avs_hda_probe_work(struct work_struct *work)
214214
adev->nhlt = intel_nhlt_init(adev->dev);
215215
if (!adev->nhlt)
216216
dev_info(bus->dev, "platform has no NHLT\n");
217+
avs_debugfs_init(adev);
217218

218219
avs_register_all_boards(adev);
219220

@@ -491,6 +492,7 @@ static void avs_pci_remove(struct pci_dev *pci)
491492

492493
avs_unregister_all_boards(adev);
493494

495+
avs_debugfs_exit(adev);
494496
if (adev->nhlt)
495497
intel_nhlt_free(adev->nhlt);
496498

sound/soc/intel/avs/debugfs.c

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
#include <linux/debugfs.h>
1010
#include <linux/kfifo.h>
1111
#include <linux/wait.h>
12+
#include <linux/sched/signal.h>
13+
#include <sound/soc.h>
1214
#include "avs.h"
15+
#include "messages.h"
1316

1417
static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len)
1518
{
@@ -44,3 +47,326 @@ void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsig
4447
avs_dump_fw_log(adev, src, len);
4548
wake_up(&adev->trace_waitq);
4649
}
50+
51+
static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
52+
{
53+
struct avs_dev *adev = file->private_data;
54+
struct avs_probe_point_desc *desc;
55+
size_t num_desc, len = 0;
56+
char *buf;
57+
int i, ret;
58+
59+
/* Prevent chaining, send and dump IPC value just once. */
60+
if (*ppos)
61+
return 0;
62+
63+
buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
64+
if (!buf)
65+
return -ENOMEM;
66+
67+
ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
68+
if (ret) {
69+
ret = AVS_IPC_RET(ret);
70+
goto exit;
71+
}
72+
73+
for (i = 0; i < num_desc; i++) {
74+
ret = snprintf(buf + len, PAGE_SIZE - len,
75+
"Id: %#010x Purpose: %d Node id: %#x\n",
76+
desc[i].id.value, desc[i].purpose, desc[i].node_id.val);
77+
if (ret < 0)
78+
goto free_desc;
79+
len += ret;
80+
}
81+
82+
ret = simple_read_from_buffer(to, count, ppos, buf, len);
83+
free_desc:
84+
kfree(desc);
85+
exit:
86+
kfree(buf);
87+
return ret;
88+
}
89+
90+
static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count,
91+
loff_t *ppos)
92+
{
93+
struct avs_dev *adev = file->private_data;
94+
struct avs_probe_point_desc *desc;
95+
u32 *array, num_elems;
96+
size_t bytes;
97+
int ret;
98+
99+
ret = parse_int_array_user(from, count, (int **)&array);
100+
if (ret < 0)
101+
return ret;
102+
103+
num_elems = *array;
104+
bytes = sizeof(*array) * num_elems;
105+
if (bytes % sizeof(*desc)) {
106+
ret = -EINVAL;
107+
goto exit;
108+
}
109+
110+
desc = (struct avs_probe_point_desc *)&array[1];
111+
ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc));
112+
if (ret)
113+
ret = AVS_IPC_RET(ret);
114+
else
115+
ret = count;
116+
exit:
117+
kfree(array);
118+
return ret;
119+
}
120+
121+
static const struct file_operations probe_points_fops = {
122+
.open = simple_open,
123+
.read = probe_points_read,
124+
.write = probe_points_write,
125+
.llseek = no_llseek,
126+
};
127+
128+
static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from,
129+
size_t count, loff_t *ppos)
130+
{
131+
struct avs_dev *adev = file->private_data;
132+
union avs_probe_point_id *id;
133+
u32 *array, num_elems;
134+
size_t bytes;
135+
int ret;
136+
137+
ret = parse_int_array_user(from, count, (int **)&array);
138+
if (ret < 0)
139+
return ret;
140+
141+
num_elems = *array;
142+
bytes = sizeof(*array) * num_elems;
143+
if (bytes % sizeof(*id)) {
144+
ret = -EINVAL;
145+
goto exit;
146+
}
147+
148+
id = (union avs_probe_point_id *)&array[1];
149+
ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id));
150+
if (ret)
151+
ret = AVS_IPC_RET(ret);
152+
else
153+
ret = count;
154+
exit:
155+
kfree(array);
156+
return ret;
157+
}
158+
159+
static const struct file_operations probe_points_disconnect_fops = {
160+
.open = simple_open,
161+
.write = probe_points_disconnect_write,
162+
.llseek = default_llseek,
163+
};
164+
165+
static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
166+
{
167+
struct avs_dev *adev = file->private_data;
168+
struct kfifo *fifo = &adev->trace_fifo;
169+
unsigned int copied;
170+
171+
if (kfifo_is_empty(fifo)) {
172+
DEFINE_WAIT(wait);
173+
174+
prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE);
175+
if (!signal_pending(current))
176+
schedule();
177+
finish_wait(&adev->trace_waitq, &wait);
178+
}
179+
180+
if (kfifo_to_user(fifo, to, count, &copied))
181+
return -EFAULT;
182+
*ppos += copied;
183+
return copied;
184+
}
185+
186+
static int strace_open(struct inode *inode, struct file *file)
187+
{
188+
struct avs_dev *adev = inode->i_private;
189+
int ret;
190+
191+
if (kfifo_initialized(&adev->trace_fifo))
192+
return -EBUSY;
193+
194+
ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL);
195+
if (ret < 0)
196+
return ret;
197+
198+
file->private_data = adev;
199+
return 0;
200+
}
201+
202+
static int strace_release(struct inode *inode, struct file *file)
203+
{
204+
struct avs_dev *adev = file->private_data;
205+
unsigned long flags;
206+
207+
spin_lock_irqsave(&adev->trace_lock, flags);
208+
kfifo_free(&adev->trace_fifo);
209+
spin_unlock_irqrestore(&adev->trace_lock, flags);
210+
211+
return 0;
212+
}
213+
214+
static const struct file_operations strace_fops = {
215+
.llseek = default_llseek,
216+
.read = strace_read,
217+
.open = strace_open,
218+
.release = strace_release,
219+
};
220+
221+
#define DISABLE_TIMERS UINT_MAX
222+
223+
static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities)
224+
{
225+
int ret;
226+
227+
/* Logging demands D0i0 state from DSP. */
228+
if (!adev->logged_resources) {
229+
pm_runtime_get_sync(adev->dev);
230+
231+
ret = avs_dsp_disable_d0ix(adev);
232+
if (ret)
233+
goto err_d0ix;
234+
}
235+
236+
ret = avs_ipc_set_system_time(adev);
237+
if (ret && ret != AVS_IPC_NOT_SUPPORTED) {
238+
ret = AVS_IPC_RET(ret);
239+
goto err_ipc;
240+
}
241+
242+
ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period,
243+
adev->fifo_full_timer_period, resource_mask, priorities);
244+
if (ret)
245+
goto err_ipc;
246+
247+
adev->logged_resources |= resource_mask;
248+
return 0;
249+
250+
err_ipc:
251+
if (!adev->logged_resources) {
252+
avs_dsp_enable_d0ix(adev);
253+
err_d0ix:
254+
pm_runtime_mark_last_busy(adev->dev);
255+
pm_runtime_put_autosuspend(adev->dev);
256+
}
257+
258+
return ret;
259+
}
260+
261+
static int disable_logs(struct avs_dev *adev, u32 resource_mask)
262+
{
263+
int ret;
264+
265+
/* Check if there's anything to do. */
266+
if (!adev->logged_resources)
267+
return 0;
268+
269+
ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS,
270+
resource_mask, NULL);
271+
272+
/*
273+
* If IPC fails causing recovery, logged_resources is already zero
274+
* so unsetting bits is still safe.
275+
*/
276+
adev->logged_resources &= ~resource_mask;
277+
278+
/* If that's the last resource, allow for D3. */
279+
if (!adev->logged_resources) {
280+
avs_dsp_enable_d0ix(adev);
281+
pm_runtime_mark_last_busy(adev->dev);
282+
pm_runtime_put_autosuspend(adev->dev);
283+
}
284+
285+
return ret;
286+
}
287+
288+
static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
289+
{
290+
struct avs_dev *adev = file->private_data;
291+
char buf[64];
292+
int len;
293+
294+
len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources);
295+
296+
return simple_read_from_buffer(to, count, ppos, buf, len);
297+
}
298+
299+
static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count,
300+
loff_t *ppos)
301+
{
302+
struct avs_dev *adev = file->private_data;
303+
u32 *array, num_elems;
304+
u32 resource_mask;
305+
int ret;
306+
307+
ret = parse_int_array_user(from, count, (int **)&array);
308+
if (ret < 0)
309+
return ret;
310+
311+
num_elems = *array;
312+
resource_mask = array[1];
313+
314+
/*
315+
* Disable if just resource mask is provided - no log priority flags.
316+
*
317+
* Enable input format: mask, prio1, .., prioN
318+
* Where 'N' equals number of bits set in the 'mask'.
319+
*/
320+
if (num_elems == 1) {
321+
ret = disable_logs(adev, resource_mask);
322+
} else {
323+
if (num_elems != (hweight_long(resource_mask) + 1)) {
324+
ret = -EINVAL;
325+
goto free_array;
326+
}
327+
328+
ret = enable_logs(adev, resource_mask, &array[2]);
329+
}
330+
331+
if (!ret)
332+
ret = count;
333+
free_array:
334+
kfree(array);
335+
return ret;
336+
}
337+
338+
static const struct file_operations trace_control_fops = {
339+
.llseek = default_llseek,
340+
.read = trace_control_read,
341+
.write = trace_control_write,
342+
.open = simple_open,
343+
};
344+
345+
void avs_debugfs_init(struct avs_dev *adev)
346+
{
347+
init_waitqueue_head(&adev->trace_waitq);
348+
spin_lock_init(&adev->trace_lock);
349+
350+
adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root);
351+
352+
/* Initialize timer periods with recommended defaults. */
353+
adev->aging_timer_period = 10;
354+
adev->fifo_full_timer_period = 10;
355+
356+
debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops);
357+
debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops);
358+
359+
debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root,
360+
&adev->aging_timer_period);
361+
debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root,
362+
&adev->fifo_full_timer_period);
363+
364+
debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops);
365+
debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev,
366+
&probe_points_disconnect_fops);
367+
}
368+
369+
void avs_debugfs_exit(struct avs_dev *adev)
370+
{
371+
debugfs_remove_recursive(adev->debugfs_root);
372+
}

0 commit comments

Comments
 (0)