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