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