icc-debug.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /* Copyright (c) 2021, The Linux Foundation. All rights reserved. */
  3. #include <linux/debugfs.h>
  4. #include <linux/device.h>
  5. #include <linux/interconnect-provider.h>
  6. #include <linux/module.h>
  7. #include <linux/mutex.h>
  8. #include <linux/slab.h>
  9. #include <trace/events/power.h>
  10. #include "../internal.h"
  11. static LIST_HEAD(icc_providers);
  12. static DEFINE_MUTEX(debug_lock);
  13. static struct dentry *dentry_suspend;
  14. static bool debug_suspend;
  15. struct qcom_icc_debug_provider {
  16. struct list_head list;
  17. struct icc_provider *provider;
  18. };
  19. static int icc_print_enabled(void)
  20. {
  21. struct qcom_icc_debug_provider *dp;
  22. struct icc_provider *provider;
  23. struct icc_node *n;
  24. struct icc_req *r;
  25. u32 avg_bw, peak_bw;
  26. pr_info(" node tag avg peak\n");
  27. pr_info("--------------------------------------------------------------------\n");
  28. list_for_each_entry(dp, &icc_providers, list) {
  29. provider = dp->provider;
  30. list_for_each_entry(n, &provider->nodes, node_list) {
  31. if (!n->avg_bw && !n->peak_bw)
  32. continue;
  33. pr_info("%-42s %12u %12u\n",
  34. n->name, n->avg_bw, n->peak_bw);
  35. hlist_for_each_entry(r, &n->req_list, req_node) {
  36. if (!r->dev)
  37. continue;
  38. if (r->enabled) {
  39. avg_bw = r->avg_bw;
  40. peak_bw = r->peak_bw;
  41. } else {
  42. avg_bw = 0;
  43. peak_bw = 0;
  44. }
  45. if (avg_bw || peak_bw)
  46. pr_info(" %-27s %12u %12u %12u\n",
  47. dev_name(r->dev), r->tag, avg_bw, peak_bw);
  48. }
  49. }
  50. }
  51. return 0;
  52. }
  53. static int icc_debug_suspend_get(void *data, u64 *val)
  54. {
  55. *val = debug_suspend;
  56. return 0;
  57. }
  58. static void icc_debug_suspend_trace_probe(void *unused, const char *action,
  59. int val, bool start)
  60. {
  61. if (start && val > 0 && !strcmp("machine_suspend", action)) {
  62. pr_info("Enabled interconnect votes:\n");
  63. icc_print_enabled();
  64. }
  65. }
  66. static int icc_debug_suspend_set(void *data, u64 val)
  67. {
  68. int ret;
  69. val = !!val;
  70. if (val == debug_suspend)
  71. return 0;
  72. if (val)
  73. ret = register_trace_suspend_resume(icc_debug_suspend_trace_probe, NULL);
  74. else
  75. ret = unregister_trace_suspend_resume(icc_debug_suspend_trace_probe, NULL);
  76. if (ret) {
  77. pr_err("%s: Failed to %sregister suspend trace callback, ret=%d\n",
  78. __func__, val ? "" : "un", ret);
  79. return ret;
  80. }
  81. debug_suspend = val;
  82. return 0;
  83. }
  84. DEFINE_DEBUGFS_ATTRIBUTE(icc_debug_suspend_fops, icc_debug_suspend_get,
  85. icc_debug_suspend_set, "%llu\n");
  86. int qcom_icc_debug_register(struct icc_provider *provider)
  87. {
  88. struct qcom_icc_debug_provider *dp;
  89. dp = kzalloc(sizeof(*dp), GFP_KERNEL);
  90. if (!dp)
  91. return -ENOMEM;
  92. dp->provider = provider;
  93. mutex_lock(&debug_lock);
  94. list_add_tail(&dp->list, &icc_providers);
  95. mutex_unlock(&debug_lock);
  96. return 0;
  97. }
  98. EXPORT_SYMBOL(qcom_icc_debug_register);
  99. int qcom_icc_debug_unregister(struct icc_provider *provider)
  100. {
  101. struct qcom_icc_debug_provider *dp, *temp;
  102. mutex_lock(&debug_lock);
  103. list_for_each_entry_safe(dp, temp, &icc_providers, list) {
  104. if (dp->provider == provider) {
  105. list_del(&dp->list);
  106. kfree(dp);
  107. }
  108. }
  109. mutex_unlock(&debug_lock);
  110. return 0;
  111. }
  112. EXPORT_SYMBOL(qcom_icc_debug_unregister);
  113. static int __init qcom_icc_debug_init(void)
  114. {
  115. static struct dentry *dir;
  116. int ret;
  117. dir = debugfs_lookup("interconnect", NULL);
  118. if (IS_ERR_OR_NULL(dir)) {
  119. ret = PTR_ERR(dir);
  120. pr_err("%s: unable to find root interconnect debugfs directory, ret=%d\n",
  121. __func__, ret);
  122. return 0;
  123. }
  124. dentry_suspend = debugfs_create_file_unsafe("debug_suspend",
  125. 0644, dir, NULL,
  126. &icc_debug_suspend_fops);
  127. return 0;
  128. }
  129. module_init(qcom_icc_debug_init);
  130. static void __exit qcom_icc_debug_exit(void)
  131. {
  132. debugfs_remove(dentry_suspend);
  133. if (debug_suspend)
  134. unregister_trace_suspend_resume(icc_debug_suspend_trace_probe, NULL);
  135. }
  136. module_exit(qcom_icc_debug_exit);
  137. MODULE_DESCRIPTION("QCOM ICC debug library");
  138. MODULE_LICENSE("GPL v2");