debugfs.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. * Generic OPP debugfs interface
  3. *
  4. * Copyright (C) 2015-2016 Viresh Kumar <viresh.kumar@linaro.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License version 2 as
  8. * published by the Free Software Foundation.
  9. */
  10. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  11. #include <linux/debugfs.h>
  12. #include <linux/device.h>
  13. #include <linux/err.h>
  14. #include <linux/init.h>
  15. #include <linux/limits.h>
  16. #include <linux/slab.h>
  17. #include "opp.h"
  18. static struct dentry *rootdir;
  19. static void opp_set_dev_name(const struct device *dev, char *name)
  20. {
  21. if (dev->parent)
  22. snprintf(name, NAME_MAX, "%s-%s", dev_name(dev->parent),
  23. dev_name(dev));
  24. else
  25. snprintf(name, NAME_MAX, "%s", dev_name(dev));
  26. }
  27. void opp_debug_remove_one(struct dev_pm_opp *opp)
  28. {
  29. debugfs_remove_recursive(opp->dentry);
  30. }
  31. static bool opp_debug_create_supplies(struct dev_pm_opp *opp,
  32. struct opp_table *opp_table,
  33. struct dentry *pdentry)
  34. {
  35. struct dentry *d;
  36. int i;
  37. for (i = 0; i < opp_table->regulator_count; i++) {
  38. char name[15];
  39. snprintf(name, sizeof(name), "supply-%d", i);
  40. /* Create per-opp directory */
  41. d = debugfs_create_dir(name, pdentry);
  42. if (!d)
  43. return false;
  44. if (!debugfs_create_ulong("u_volt_target", S_IRUGO, d,
  45. &opp->supplies[i].u_volt))
  46. return false;
  47. if (!debugfs_create_ulong("u_volt_min", S_IRUGO, d,
  48. &opp->supplies[i].u_volt_min))
  49. return false;
  50. if (!debugfs_create_ulong("u_volt_max", S_IRUGO, d,
  51. &opp->supplies[i].u_volt_max))
  52. return false;
  53. if (!debugfs_create_ulong("u_amp", S_IRUGO, d,
  54. &opp->supplies[i].u_amp))
  55. return false;
  56. }
  57. return true;
  58. }
  59. int opp_debug_create_one(struct dev_pm_opp *opp, struct opp_table *opp_table)
  60. {
  61. struct dentry *pdentry = opp_table->dentry;
  62. struct dentry *d;
  63. unsigned long id;
  64. char name[25]; /* 20 chars for 64 bit value + 5 (opp:\0) */
  65. /*
  66. * Get directory name for OPP.
  67. *
  68. * - Normally rate is unique to each OPP, use it to get unique opp-name.
  69. * - For some devices rate isn't available, use index instead.
  70. */
  71. if (likely(opp->rate))
  72. id = opp->rate;
  73. else
  74. id = _get_opp_count(opp_table);
  75. snprintf(name, sizeof(name), "opp:%lu", id);
  76. /* Create per-opp directory */
  77. d = debugfs_create_dir(name, pdentry);
  78. if (!d)
  79. return -ENOMEM;
  80. if (!debugfs_create_bool("available", S_IRUGO, d, &opp->available))
  81. return -ENOMEM;
  82. if (!debugfs_create_bool("dynamic", S_IRUGO, d, &opp->dynamic))
  83. return -ENOMEM;
  84. if (!debugfs_create_bool("turbo", S_IRUGO, d, &opp->turbo))
  85. return -ENOMEM;
  86. if (!debugfs_create_bool("suspend", S_IRUGO, d, &opp->suspend))
  87. return -ENOMEM;
  88. if (!debugfs_create_u32("performance_state", S_IRUGO, d, &opp->pstate))
  89. return -ENOMEM;
  90. if (!debugfs_create_ulong("rate_hz", S_IRUGO, d, &opp->rate))
  91. return -ENOMEM;
  92. if (!opp_debug_create_supplies(opp, opp_table, d))
  93. return -ENOMEM;
  94. if (!debugfs_create_ulong("clock_latency_ns", S_IRUGO, d,
  95. &opp->clock_latency_ns))
  96. return -ENOMEM;
  97. opp->dentry = d;
  98. return 0;
  99. }
  100. static int opp_list_debug_create_dir(struct opp_device *opp_dev,
  101. struct opp_table *opp_table)
  102. {
  103. const struct device *dev = opp_dev->dev;
  104. struct dentry *d;
  105. opp_set_dev_name(dev, opp_table->dentry_name);
  106. /* Create device specific directory */
  107. d = debugfs_create_dir(opp_table->dentry_name, rootdir);
  108. if (!d) {
  109. dev_err(dev, "%s: Failed to create debugfs dir\n", __func__);
  110. return -ENOMEM;
  111. }
  112. opp_dev->dentry = d;
  113. opp_table->dentry = d;
  114. return 0;
  115. }
  116. static int opp_list_debug_create_link(struct opp_device *opp_dev,
  117. struct opp_table *opp_table)
  118. {
  119. const struct device *dev = opp_dev->dev;
  120. char name[NAME_MAX];
  121. struct dentry *d;
  122. opp_set_dev_name(opp_dev->dev, name);
  123. /* Create device specific directory link */
  124. d = debugfs_create_symlink(name, rootdir, opp_table->dentry_name);
  125. if (!d) {
  126. dev_err(dev, "%s: Failed to create link\n", __func__);
  127. return -ENOMEM;
  128. }
  129. opp_dev->dentry = d;
  130. return 0;
  131. }
  132. /**
  133. * opp_debug_register - add a device opp node to the debugfs 'opp' directory
  134. * @opp_dev: opp-dev pointer for device
  135. * @opp_table: the device-opp being added
  136. *
  137. * Dynamically adds device specific directory in debugfs 'opp' directory. If the
  138. * device-opp is shared with other devices, then links will be created for all
  139. * devices except the first.
  140. *
  141. * Return: 0 on success, otherwise negative error.
  142. */
  143. int opp_debug_register(struct opp_device *opp_dev, struct opp_table *opp_table)
  144. {
  145. if (!rootdir) {
  146. pr_debug("%s: Uninitialized rootdir\n", __func__);
  147. return -EINVAL;
  148. }
  149. if (opp_table->dentry)
  150. return opp_list_debug_create_link(opp_dev, opp_table);
  151. return opp_list_debug_create_dir(opp_dev, opp_table);
  152. }
  153. static void opp_migrate_dentry(struct opp_device *opp_dev,
  154. struct opp_table *opp_table)
  155. {
  156. struct opp_device *new_dev;
  157. const struct device *dev;
  158. struct dentry *dentry;
  159. /* Look for next opp-dev */
  160. list_for_each_entry(new_dev, &opp_table->dev_list, node)
  161. if (new_dev != opp_dev)
  162. break;
  163. /* new_dev is guaranteed to be valid here */
  164. dev = new_dev->dev;
  165. debugfs_remove_recursive(new_dev->dentry);
  166. opp_set_dev_name(dev, opp_table->dentry_name);
  167. dentry = debugfs_rename(rootdir, opp_dev->dentry, rootdir,
  168. opp_table->dentry_name);
  169. if (!dentry) {
  170. dev_err(dev, "%s: Failed to rename link from: %s to %s\n",
  171. __func__, dev_name(opp_dev->dev), dev_name(dev));
  172. return;
  173. }
  174. new_dev->dentry = dentry;
  175. opp_table->dentry = dentry;
  176. }
  177. /**
  178. * opp_debug_unregister - remove a device opp node from debugfs opp directory
  179. * @opp_dev: opp-dev pointer for device
  180. * @opp_table: the device-opp being removed
  181. *
  182. * Dynamically removes device specific directory from debugfs 'opp' directory.
  183. */
  184. void opp_debug_unregister(struct opp_device *opp_dev,
  185. struct opp_table *opp_table)
  186. {
  187. if (opp_dev->dentry == opp_table->dentry) {
  188. /* Move the real dentry object under another device */
  189. if (!list_is_singular(&opp_table->dev_list)) {
  190. opp_migrate_dentry(opp_dev, opp_table);
  191. goto out;
  192. }
  193. opp_table->dentry = NULL;
  194. }
  195. debugfs_remove_recursive(opp_dev->dentry);
  196. out:
  197. opp_dev->dentry = NULL;
  198. }
  199. static int __init opp_debug_init(void)
  200. {
  201. /* Create /sys/kernel/debug/opp directory */
  202. rootdir = debugfs_create_dir("opp", NULL);
  203. if (!rootdir) {
  204. pr_err("%s: Failed to create root directory\n", __func__);
  205. return -ENOMEM;
  206. }
  207. return 0;
  208. }
  209. core_initcall(opp_debug_init);