1// SPDX-License-Identifier: GPL-2.0-only 2// 3// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. 4// 5// Authors: Cezary Rojewski <cezary.rojewski@intel.com> 6// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> 7// 8 9#include <linux/debugfs.h> 10#include <linux/kfifo.h> 11#include <linux/wait.h> 12#include <linux/sched/signal.h> 13#include <sound/soc.h> 14#include "avs.h" 15#include "messages.h" 16 17static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len) 18{ 19 struct __kfifo *__fifo = &fifo->kfifo; 20 unsigned int l, off; 21 22 len = min(len, kfifo_avail(fifo)); 23 off = __fifo->in & __fifo->mask; 24 l = min(len, kfifo_size(fifo) - off); 25 26 memcpy_fromio(__fifo->data + off, src, l); 27 memcpy_fromio(__fifo->data, src + l, len - l); 28 /* Make sure data copied from SRAM is visible to all CPUs. */ 29 smp_mb(); 30 __fifo->in += len; 31 32 return len; 33} 34 35bool avs_logging_fw(struct avs_dev *adev) 36{ 37 return kfifo_initialized(&adev->trace_fifo); 38} 39 40void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len) 41{ 42 __kfifo_fromio(&adev->trace_fifo, src, len); 43} 44 45void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len) 46{ 47 avs_dump_fw_log(adev, src, len); 48 wake_up(&adev->trace_waitq); 49} 50 51static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 52{ 53 struct avs_dev *adev = file->private_data; 54 char *buf; 55 int ret; 56 57 buf = kzalloc(AVS_FW_REGS_SIZE, GFP_KERNEL); 58 if (!buf) 59 return -ENOMEM; 60 61 memcpy_fromio(buf, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE); 62 63 ret = simple_read_from_buffer(to, count, ppos, buf, AVS_FW_REGS_SIZE); 64 kfree(buf); 65 return ret; 66} 67 68static const struct file_operations fw_regs_fops = { 69 .open = simple_open, 70 .read = fw_regs_read, 71 .llseek = no_llseek, 72}; 73 74static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 75{ 76 struct avs_dev *adev = file->private_data; 77 size_t size; 78 char *buf; 79 int ret; 80 81 size = adev->hw_cfg.dsp_cores * AVS_WINDOW_CHUNK_SIZE; 82 buf = kzalloc(size, GFP_KERNEL); 83 if (!buf) 84 return -ENOMEM; 85 86 memcpy_fromio(buf, avs_sram_addr(adev, AVS_DEBUG_WINDOW), size); 87 88 ret = simple_read_from_buffer(to, count, ppos, buf, size); 89 kfree(buf); 90 return ret; 91} 92 93static const struct file_operations debug_window_fops = { 94 .open = simple_open, 95 .read = debug_window_read, 96 .llseek = no_llseek, 97}; 98 99static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 100{ 101 struct avs_dev *adev = file->private_data; 102 struct avs_probe_point_desc *desc; 103 size_t num_desc, len = 0; 104 char *buf; 105 int i, ret; 106 107 /* Prevent chaining, send and dump IPC value just once. */ 108 if (*ppos) 109 return 0; 110 111 buf = kzalloc(PAGE_SIZE, GFP_KERNEL); 112 if (!buf) 113 return -ENOMEM; 114 115 ret = avs_ipc_probe_get_points(adev, &desc, &num_desc); 116 if (ret) { 117 ret = AVS_IPC_RET(ret); 118 goto exit; 119 } 120 121 for (i = 0; i < num_desc; i++) { 122 ret = snprintf(buf + len, PAGE_SIZE - len, 123 "Id: %#010x Purpose: %d Node id: %#x\n", 124 desc[i].id.value, desc[i].purpose, desc[i].node_id.val); 125 if (ret < 0) 126 goto free_desc; 127 len += ret; 128 } 129 130 ret = simple_read_from_buffer(to, count, ppos, buf, len); 131free_desc: 132 kfree(desc); 133exit: 134 kfree(buf); 135 return ret; 136} 137 138static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count, 139 loff_t *ppos) 140{ 141 struct avs_dev *adev = file->private_data; 142 struct avs_probe_point_desc *desc; 143 u32 *array, num_elems; 144 size_t bytes; 145 int ret; 146 147 ret = parse_int_array_user(from, count, (int **)&array); 148 if (ret < 0) 149 return ret; 150 151 num_elems = *array; 152 bytes = sizeof(*array) * num_elems; 153 if (bytes % sizeof(*desc)) { 154 ret = -EINVAL; 155 goto exit; 156 } 157 158 desc = (struct avs_probe_point_desc *)&array[1]; 159 ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc)); 160 if (ret) 161 ret = AVS_IPC_RET(ret); 162 else 163 ret = count; 164exit: 165 kfree(array); 166 return ret; 167} 168 169static const struct file_operations probe_points_fops = { 170 .open = simple_open, 171 .read = probe_points_read, 172 .write = probe_points_write, 173 .llseek = no_llseek, 174}; 175 176static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from, 177 size_t count, loff_t *ppos) 178{ 179 struct avs_dev *adev = file->private_data; 180 union avs_probe_point_id *id; 181 u32 *array, num_elems; 182 size_t bytes; 183 int ret; 184 185 ret = parse_int_array_user(from, count, (int **)&array); 186 if (ret < 0) 187 return ret; 188 189 num_elems = *array; 190 bytes = sizeof(*array) * num_elems; 191 if (bytes % sizeof(*id)) { 192 ret = -EINVAL; 193 goto exit; 194 } 195 196 id = (union avs_probe_point_id *)&array[1]; 197 ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id)); 198 if (ret) 199 ret = AVS_IPC_RET(ret); 200 else 201 ret = count; 202exit: 203 kfree(array); 204 return ret; 205} 206 207static const struct file_operations probe_points_disconnect_fops = { 208 .open = simple_open, 209 .write = probe_points_disconnect_write, 210 .llseek = default_llseek, 211}; 212 213static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 214{ 215 struct avs_dev *adev = file->private_data; 216 struct kfifo *fifo = &adev->trace_fifo; 217 unsigned int copied; 218 219 if (kfifo_is_empty(fifo)) { 220 DEFINE_WAIT(wait); 221 222 prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE); 223 if (!signal_pending(current)) 224 schedule(); 225 finish_wait(&adev->trace_waitq, &wait); 226 } 227 228 if (kfifo_to_user(fifo, to, count, &copied)) 229 return -EFAULT; 230 *ppos += copied; 231 return copied; 232} 233 234static int strace_open(struct inode *inode, struct file *file) 235{ 236 struct avs_dev *adev = inode->i_private; 237 int ret; 238 239 if (!try_module_get(adev->dev->driver->owner)) 240 return -ENODEV; 241 242 if (kfifo_initialized(&adev->trace_fifo)) 243 return -EBUSY; 244 245 ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); 246 if (ret < 0) 247 return ret; 248 249 file->private_data = adev; 250 return 0; 251} 252 253static int strace_release(struct inode *inode, struct file *file) 254{ 255 union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); 256 struct avs_dev *adev = file->private_data; 257 unsigned long resource_mask; 258 unsigned long flags, i; 259 u32 num_cores; 260 261 resource_mask = adev->logged_resources; 262 num_cores = adev->hw_cfg.dsp_cores; 263 264 spin_lock_irqsave(&adev->trace_lock, flags); 265 266 /* Gather any remaining logs. */ 267 for_each_set_bit(i, &resource_mask, num_cores) { 268 msg.log.core = i; 269 avs_dsp_op(adev, log_buffer_status, &msg); 270 } 271 272 kfifo_free(&adev->trace_fifo); 273 274 spin_unlock_irqrestore(&adev->trace_lock, flags); 275 276 module_put(adev->dev->driver->owner); 277 return 0; 278} 279 280static const struct file_operations strace_fops = { 281 .llseek = default_llseek, 282 .read = strace_read, 283 .open = strace_open, 284 .release = strace_release, 285}; 286 287#define DISABLE_TIMERS UINT_MAX 288 289static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities) 290{ 291 int ret; 292 293 /* Logging demands D0i0 state from DSP. */ 294 if (!adev->logged_resources) { 295 pm_runtime_get_sync(adev->dev); 296 297 ret = avs_dsp_disable_d0ix(adev); 298 if (ret) 299 goto err_d0ix; 300 } 301 302 ret = avs_ipc_set_system_time(adev); 303 if (ret && ret != AVS_IPC_NOT_SUPPORTED) { 304 ret = AVS_IPC_RET(ret); 305 goto err_ipc; 306 } 307 308 ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period, 309 adev->fifo_full_timer_period, resource_mask, priorities); 310 if (ret) 311 goto err_ipc; 312 313 adev->logged_resources |= resource_mask; 314 return 0; 315 316err_ipc: 317 if (!adev->logged_resources) { 318 avs_dsp_enable_d0ix(adev); 319err_d0ix: 320 pm_runtime_mark_last_busy(adev->dev); 321 pm_runtime_put_autosuspend(adev->dev); 322 } 323 324 return ret; 325} 326 327static int disable_logs(struct avs_dev *adev, u32 resource_mask) 328{ 329 int ret; 330 331 /* Check if there's anything to do. */ 332 if (!adev->logged_resources) 333 return 0; 334 335 ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS, 336 resource_mask, NULL); 337 338 /* 339 * If IPC fails causing recovery, logged_resources is already zero 340 * so unsetting bits is still safe. 341 */ 342 adev->logged_resources &= ~resource_mask; 343 344 /* If that's the last resource, allow for D3. */ 345 if (!adev->logged_resources) { 346 avs_dsp_enable_d0ix(adev); 347 pm_runtime_mark_last_busy(adev->dev); 348 pm_runtime_put_autosuspend(adev->dev); 349 } 350 351 return ret; 352} 353 354static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 355{ 356 struct avs_dev *adev = file->private_data; 357 char buf[64]; 358 int len; 359 360 len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources); 361 362 return simple_read_from_buffer(to, count, ppos, buf, len); 363} 364 365static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count, 366 loff_t *ppos) 367{ 368 struct avs_dev *adev = file->private_data; 369 u32 *array, num_elems; 370 u32 resource_mask; 371 int ret; 372 373 ret = parse_int_array_user(from, count, (int **)&array); 374 if (ret < 0) 375 return ret; 376 377 num_elems = *array; 378 resource_mask = array[1]; 379 380 /* 381 * Disable if just resource mask is provided - no log priority flags. 382 * 383 * Enable input format: mask, prio1, .., prioN 384 * Where 'N' equals number of bits set in the 'mask'. 385 */ 386 if (num_elems == 1) { 387 ret = disable_logs(adev, resource_mask); 388 } else { 389 if (num_elems != (hweight_long(resource_mask) + 1)) { 390 ret = -EINVAL; 391 goto free_array; 392 } 393 394 ret = enable_logs(adev, resource_mask, &array[2]); 395 } 396 397 if (!ret) 398 ret = count; 399free_array: 400 kfree(array); 401 return ret; 402} 403 404static const struct file_operations trace_control_fops = { 405 .llseek = default_llseek, 406 .read = trace_control_read, 407 .write = trace_control_write, 408 .open = simple_open, 409}; 410 411void avs_debugfs_init(struct avs_dev *adev) 412{ 413 init_waitqueue_head(&adev->trace_waitq); 414 spin_lock_init(&adev->trace_lock); 415 416 adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root); 417 418 /* Initialize timer periods with recommended defaults. */ 419 adev->aging_timer_period = 10; 420 adev->fifo_full_timer_period = 10; 421 422 debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops); 423 debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops); 424 debugfs_create_file("fw_regs", 0444, adev->debugfs_root, adev, &fw_regs_fops); 425 debugfs_create_file("debug_window", 0444, adev->debugfs_root, adev, &debug_window_fops); 426 427 debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root, 428 &adev->aging_timer_period); 429 debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root, 430 &adev->fifo_full_timer_period); 431 432 debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops); 433 debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev, 434 &probe_points_disconnect_fops); 435} 436 437void avs_debugfs_exit(struct avs_dev *adev) 438{ 439 debugfs_remove_recursive(adev->debugfs_root); 440} 441