sec_pm_log.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * sec_pm_log.c - SAMSUNG Power/Thermal logging.
  4. *
  5. */
  6. #include <linux/module.h>
  7. #include <linux/device.h>
  8. #include <linux/err.h>
  9. #include <linux/slab.h>
  10. #include <linux/uaccess.h>
  11. #include <linux/proc_fs.h>
  12. #include <linux/time.h>
  13. #include <linux/rtc.h>
  14. #include <linux/suspend.h>
  15. #include <linux/sched/clock.h>
  16. #include <linux/sec_pm_log.h>
  17. /* log for pm team */
  18. #define PROC_PM_DIR "sec_pm_log"
  19. static struct proc_dir_entry *procfs_pm_dir;
  20. /* power/thermal log */
  21. #define POWER_LOG_NAME "power_log"
  22. #define THERMAL_LOG_NAME "thermal_log"
  23. #define PM_LOG_BUF_SIZE SZ_128K
  24. #define MAX_STR_LEN 256
  25. static bool proc_init_done __read_mostly;
  26. /* monitoring log */
  27. static struct delayed_work power_log_work;
  28. static DEFINE_MUTEX(power_log_lock);
  29. /* proc log implementation */
  30. #define SEC_PM_LOG(name) \
  31. static char name##_log_buf[PM_LOG_BUF_SIZE]; \
  32. static unsigned int name##_buf_pos; \
  33. static unsigned int name##_buf_full; \
  34. \
  35. void ss_##name##_print(const char *fmt, ...) \
  36. { \
  37. char buf[MAX_STR_LEN] = {0, }; \
  38. unsigned int len = 0; \
  39. va_list args; \
  40. u64 time; \
  41. unsigned long nsec; \
  42. \
  43. if (!proc_init_done) \
  44. return; \
  45. \
  46. /* timestamp */ \
  47. time = local_clock(); \
  48. nsec = do_div(time, NSEC_PER_SEC); \
  49. len = snprintf(buf, sizeof(buf), "[%6lu.%06ld] ", \
  50. (unsigned long)time, nsec / NSEC_PER_USEC); \
  51. \
  52. /* append log */ \
  53. va_start(args, fmt); \
  54. len += vsnprintf(buf + len, MAX_STR_LEN - len, fmt, args); \
  55. va_end(args); \
  56. \
  57. /* buffer full, reset position */ \
  58. if (name##_buf_pos + len + 1 > PM_LOG_BUF_SIZE) { \
  59. name##_buf_pos = 0; \
  60. name##_buf_full++; \
  61. } \
  62. \
  63. name##_buf_pos += scnprintf(&name##_log_buf[name##_buf_pos], \
  64. len + 1, "%s", buf); \
  65. } \
  66. EXPORT_SYMBOL(ss_##name##_print); \
  67. \
  68. static ssize_t ss_##name##_log_read(struct file *file, char __user *buf, \
  69. size_t len, loff_t *offset) \
  70. { \
  71. loff_t pos = *offset; \
  72. ssize_t count = 0; \
  73. size_t size = (name##_buf_full > 0) ? PM_LOG_BUF_SIZE : (size_t)name##_buf_pos; \
  74. \
  75. pr_debug("%s: pos(%d), full(%d), size(%d)\n", __func__, \
  76. name##_buf_pos, name##_buf_full, size); \
  77. \
  78. if (pos >= size) \
  79. return 0; \
  80. \
  81. count = min(len, size); \
  82. \
  83. if ((pos + count) > size) \
  84. count = size - pos; \
  85. \
  86. if (copy_to_user(buf, name##_log_buf + pos, count)) \
  87. return -EFAULT; \
  88. \
  89. *offset += count; \
  90. \
  91. pr_info("%s: done\n", __func__); \
  92. \
  93. return count; \
  94. } \
  95. \
  96. static const struct proc_ops proc_ss_##name##_log_ops = { \
  97. .proc_read = ss_##name##_log_read, \
  98. };
  99. /* all-in-one define */
  100. SEC_PM_LOG(power);
  101. SEC_PM_LOG(thermal);
  102. /* monitoring log implementation */
  103. #define POLLING_DELAY (HZ * 5)
  104. static void __ref power_log_print(struct work_struct *work)
  105. {
  106. char power_log[MAX_STR_LEN];
  107. mutex_lock(&power_log_lock);
  108. pm_get_active_wakeup_sources(power_log, MAX_STR_LEN);
  109. mutex_unlock(&power_log_lock);
  110. ss_power_print("pmon: %s\n", power_log);
  111. schedule_delayed_work(&power_log_work, POLLING_DELAY);
  112. }
  113. void sec_pm_proc_log_init(void)
  114. {
  115. struct proc_dir_entry *entry;
  116. procfs_pm_dir = proc_mkdir(PROC_PM_DIR, NULL);
  117. if (unlikely(!procfs_pm_dir)) {
  118. pr_err("%s: failed to make %s\n", __func__, PROC_PM_DIR);
  119. return;
  120. }
  121. /* proc power */
  122. entry = proc_create(POWER_LOG_NAME, 0444,
  123. procfs_pm_dir, &proc_ss_power_log_ops);
  124. if (unlikely(!entry)) {
  125. pr_err("%s: proc power log fail\n", __func__);
  126. } else {
  127. proc_set_size(entry, PM_LOG_BUF_SIZE);
  128. ss_power_print("%s done\n", __func__);
  129. proc_init_done = true;
  130. }
  131. /* proc thermal */
  132. entry = proc_create(THERMAL_LOG_NAME, 0444,
  133. procfs_pm_dir,
  134. &proc_ss_thermal_log_ops);
  135. if (unlikely(!entry)) {
  136. pr_err("%s: proc thermal log fail\n", __func__);
  137. } else {
  138. proc_set_size(entry, PM_LOG_BUF_SIZE);
  139. ss_thermal_print("%s done\n", __func__);
  140. proc_init_done = true;
  141. }
  142. if (!proc_init_done)
  143. remove_proc_subtree(PROC_PM_DIR, NULL);
  144. else
  145. pr_info("%s: proc init done\n", __func__);
  146. return;
  147. }
  148. static int sec_pm_log_notify(struct notifier_block *nb,
  149. unsigned long mode, void *_unused)
  150. {
  151. struct timespec64 ts;
  152. struct rtc_time tm;
  153. ktime_get_real_ts64(&ts);
  154. rtc_time64_to_tm(ts.tv_sec, &tm);
  155. switch (mode) {
  156. case PM_SUSPEND_PREPARE:
  157. ss_power_print("soc: suspend(%d-%02d-%02d %02d:%02d:%02d.%03lu)\n",
  158. tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
  159. tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
  160. cancel_delayed_work(&power_log_work);
  161. break;
  162. case PM_POST_SUSPEND:
  163. schedule_delayed_work(&power_log_work, 0);
  164. ss_power_print("soc: resume(%d-%02d-%02d %02d:%02d:%02d.%03lu)\n",
  165. tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
  166. tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
  167. break;
  168. default:
  169. break;
  170. }
  171. return 0;
  172. }
  173. static struct notifier_block sec_pm_log_nb = {
  174. .notifier_call = sec_pm_log_notify,
  175. };
  176. static int __init sec_pm_log_init(void)
  177. {
  178. int ret = 0;
  179. pr_info("%s\n", __func__);
  180. /* proc log */
  181. sec_pm_proc_log_init();
  182. /* monitoring log */
  183. INIT_DELAYED_WORK(&power_log_work, power_log_print);
  184. schedule_delayed_work(&power_log_work, 0);
  185. pr_info("%s: monitor init done\n", __func__);
  186. /* pm notifier */
  187. ret = register_pm_notifier(&sec_pm_log_nb);
  188. if (ret)
  189. pr_err("sec_pm_log: pm notifier register fail(%d).\n", ret);
  190. return 0;
  191. }
  192. static void __exit sec_pm_log_exit(void)
  193. {
  194. remove_proc_subtree(POWER_LOG_NAME, procfs_pm_dir);
  195. remove_proc_subtree(THERMAL_LOG_NAME, procfs_pm_dir);
  196. remove_proc_subtree(PROC_PM_DIR, NULL);
  197. }
  198. module_init(sec_pm_log_init);
  199. module_exit(sec_pm_log_exit);
  200. MODULE_ALIAS("platform:sec_pm_log");
  201. MODULE_DESCRIPTION("Samsung Power/Thermal Log driver");
  202. MODULE_LICENSE("GPL v2");