rpm_master_stats.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
  4. * Copyright (c) 2023, Linaro Limited
  5. *
  6. * This driver supports what is known as "Master Stats v2" in Qualcomm
  7. * downstream kernel terms, which seems to be the only version which has
  8. * ever shipped, all the way from 2013 to 2023.
  9. */
  10. #include <linux/debugfs.h>
  11. #include <linux/io.h>
  12. #include <linux/module.h>
  13. #include <linux/of.h>
  14. #include <linux/of_address.h>
  15. #include <linux/platform_device.h>
  16. struct master_stats_data {
  17. void __iomem *base;
  18. const char *label;
  19. };
  20. struct rpm_master_stats {
  21. u32 active_cores;
  22. u32 num_shutdowns;
  23. u64 shutdown_req;
  24. u64 wakeup_idx;
  25. u64 bringup_req;
  26. u64 bringup_ack;
  27. u32 wakeup_reason; /* 0 = "rude wakeup", 1 = scheduled wakeup */
  28. u32 last_sleep_trans_dur;
  29. u32 last_wake_trans_dur;
  30. /* Per-subsystem (*not necessarily* SoC-wide) XO shutdown stats */
  31. u32 xo_count;
  32. u64 xo_last_enter;
  33. u64 last_exit;
  34. u64 xo_total_dur;
  35. } __packed;
  36. static int master_stats_show(struct seq_file *s, void *unused)
  37. {
  38. struct master_stats_data *data = s->private;
  39. struct rpm_master_stats stat;
  40. memcpy_fromio(&stat, data->base, sizeof(stat));
  41. seq_printf(s, "%s:\n", data->label);
  42. seq_printf(s, "\tLast shutdown @ %llu\n", stat.shutdown_req);
  43. seq_printf(s, "\tLast bringup req @ %llu\n", stat.bringup_req);
  44. seq_printf(s, "\tLast bringup ack @ %llu\n", stat.bringup_ack);
  45. seq_printf(s, "\tLast wakeup idx: %llu\n", stat.wakeup_idx);
  46. seq_printf(s, "\tLast XO shutdown enter @ %llu\n", stat.xo_last_enter);
  47. seq_printf(s, "\tLast XO shutdown exit @ %llu\n", stat.last_exit);
  48. seq_printf(s, "\tXO total duration: %llu\n", stat.xo_total_dur);
  49. seq_printf(s, "\tLast sleep transition duration: %u\n", stat.last_sleep_trans_dur);
  50. seq_printf(s, "\tLast wake transition duration: %u\n", stat.last_wake_trans_dur);
  51. seq_printf(s, "\tXO shutdown count: %u\n", stat.xo_count);
  52. seq_printf(s, "\tWakeup reason: 0x%x\n", stat.wakeup_reason);
  53. seq_printf(s, "\tShutdown count: %u\n", stat.num_shutdowns);
  54. seq_printf(s, "\tActive cores bitmask: 0x%x\n", stat.active_cores);
  55. return 0;
  56. }
  57. DEFINE_SHOW_ATTRIBUTE(master_stats);
  58. static int master_stats_probe(struct platform_device *pdev)
  59. {
  60. struct device *dev = &pdev->dev;
  61. struct master_stats_data *data;
  62. struct device_node *msgram_np;
  63. struct dentry *dent, *root;
  64. struct resource res;
  65. int count, i, ret;
  66. count = of_property_count_strings(dev->of_node, "qcom,master-names");
  67. if (count < 0)
  68. return count;
  69. data = devm_kcalloc(dev, count, sizeof(*data), GFP_KERNEL);
  70. if (!data)
  71. return -ENOMEM;
  72. root = debugfs_create_dir("qcom_rpm_master_stats", NULL);
  73. platform_set_drvdata(pdev, root);
  74. for (i = 0; i < count; i++) {
  75. msgram_np = of_parse_phandle(dev->of_node, "qcom,rpm-msg-ram", i);
  76. if (!msgram_np) {
  77. debugfs_remove_recursive(root);
  78. return dev_err_probe(dev, -ENODEV,
  79. "Couldn't parse MSG RAM phandle idx %d", i);
  80. }
  81. /*
  82. * Purposefully skip devm_platform helpers as we're using a
  83. * shared resource.
  84. */
  85. ret = of_address_to_resource(msgram_np, 0, &res);
  86. of_node_put(msgram_np);
  87. if (ret < 0) {
  88. debugfs_remove_recursive(root);
  89. return ret;
  90. }
  91. data[i].base = devm_ioremap(dev, res.start, resource_size(&res));
  92. if (!data[i].base) {
  93. debugfs_remove_recursive(root);
  94. return dev_err_probe(dev, -EINVAL,
  95. "Could not map the MSG RAM slice idx %d!\n", i);
  96. }
  97. ret = of_property_read_string_index(dev->of_node, "qcom,master-names", i,
  98. &data[i].label);
  99. if (ret < 0) {
  100. debugfs_remove_recursive(root);
  101. return dev_err_probe(dev, ret,
  102. "Could not read name idx %d!\n", i);
  103. }
  104. /*
  105. * Generally it's not advised to fail on debugfs errors, but this
  106. * driver's only job is exposing data therein.
  107. */
  108. dent = debugfs_create_file(data[i].label, 0444, root,
  109. &data[i], &master_stats_fops);
  110. if (IS_ERR(dent)) {
  111. debugfs_remove_recursive(root);
  112. return dev_err_probe(dev, PTR_ERR(dent),
  113. "Failed to create debugfs file %s!\n", data[i].label);
  114. }
  115. }
  116. device_set_pm_not_required(dev);
  117. return 0;
  118. }
  119. static void master_stats_remove(struct platform_device *pdev)
  120. {
  121. struct dentry *root = platform_get_drvdata(pdev);
  122. debugfs_remove_recursive(root);
  123. }
  124. static const struct of_device_id rpm_master_table[] = {
  125. { .compatible = "qcom,rpm-master-stats" },
  126. { },
  127. };
  128. /*
  129. * No MODULE_DEVICE_TABLE intentionally: that's a debugging module, to be
  130. * loaded manually only.
  131. */
  132. static struct platform_driver master_stats_driver = {
  133. .probe = master_stats_probe,
  134. .remove = master_stats_remove,
  135. .driver = {
  136. .name = "qcom_rpm_master_stats",
  137. .of_match_table = rpm_master_table,
  138. },
  139. };
  140. module_platform_driver(master_stats_driver);
  141. MODULE_DESCRIPTION("Qualcomm RPM Master Statistics driver");
  142. MODULE_LICENSE("GPL");