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