debugfs.c 10 KB

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