wakeup_reason.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * kernel/power/wakeup_reason.c
  3. *
  4. * Logs the reasons which caused the kernel to resume from
  5. * the suspend mode.
  6. *
  7. * Copyright (C) 2014 Google, Inc.
  8. * This software is licensed under the terms of the GNU General Public
  9. * License version 2, as published by the Free Software Foundation, and
  10. * may be copied, distributed, and modified under those terms.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. */
  17. #include <linux/wakeup_reason.h>
  18. #include <linux/kernel.h>
  19. #include <linux/irq.h>
  20. #include <linux/interrupt.h>
  21. #include <linux/io.h>
  22. #include <linux/kobject.h>
  23. #include <linux/sysfs.h>
  24. #include <linux/init.h>
  25. #include <linux/spinlock.h>
  26. #include <linux/notifier.h>
  27. #include <linux/suspend.h>
  28. #define MAX_WAKEUP_REASON_IRQS 32
  29. static int irq_list[MAX_WAKEUP_REASON_IRQS];
  30. static int irqcount;
  31. static bool suspend_abort;
  32. static char abort_reason[MAX_SUSPEND_ABORT_LEN];
  33. static struct kobject *wakeup_reason;
  34. static DEFINE_SPINLOCK(resume_reason_lock);
  35. static ktime_t last_monotime; /* monotonic time before last suspend */
  36. static ktime_t curr_monotime; /* monotonic time after last suspend */
  37. static ktime_t last_stime; /* monotonic boottime offset before last suspend */
  38. static ktime_t curr_stime; /* monotonic boottime offset after last suspend */
  39. static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr,
  40. char *buf)
  41. {
  42. int irq_no, buf_offset = 0;
  43. struct irq_desc *desc;
  44. spin_lock(&resume_reason_lock);
  45. if (suspend_abort) {
  46. buf_offset = sprintf(buf, "Abort: %s", abort_reason);
  47. } else {
  48. for (irq_no = 0; irq_no < irqcount; irq_no++) {
  49. desc = irq_to_desc(irq_list[irq_no]);
  50. if (desc && desc->action && desc->action->name)
  51. buf_offset += sprintf(buf + buf_offset, "%d %s\n",
  52. irq_list[irq_no], desc->action->name);
  53. else
  54. buf_offset += sprintf(buf + buf_offset, "%d\n",
  55. irq_list[irq_no]);
  56. }
  57. }
  58. spin_unlock(&resume_reason_lock);
  59. return buf_offset;
  60. }
  61. static ssize_t last_suspend_time_show(struct kobject *kobj,
  62. struct kobj_attribute *attr, char *buf)
  63. {
  64. struct timespec sleep_time;
  65. struct timespec total_time;
  66. struct timespec suspend_resume_time;
  67. /*
  68. * total_time is calculated from monotonic bootoffsets because
  69. * unlike CLOCK_MONOTONIC it include the time spent in suspend state.
  70. */
  71. total_time = ktime_to_timespec(ktime_sub(curr_stime, last_stime));
  72. /*
  73. * suspend_resume_time is calculated as monotonic (CLOCK_MONOTONIC)
  74. * time interval before entering suspend and post suspend.
  75. */
  76. suspend_resume_time = ktime_to_timespec(ktime_sub(curr_monotime, last_monotime));
  77. /* sleep_time = total_time - suspend_resume_time */
  78. sleep_time = timespec_sub(total_time, suspend_resume_time);
  79. /* Export suspend_resume_time and sleep_time in pair here. */
  80. return sprintf(buf, "%lu.%09lu %lu.%09lu\n",
  81. suspend_resume_time.tv_sec, suspend_resume_time.tv_nsec,
  82. sleep_time.tv_sec, sleep_time.tv_nsec);
  83. }
  84. static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason);
  85. static struct kobj_attribute suspend_time = __ATTR_RO(last_suspend_time);
  86. static struct attribute *attrs[] = {
  87. &resume_reason.attr,
  88. &suspend_time.attr,
  89. NULL,
  90. };
  91. static struct attribute_group attr_group = {
  92. .attrs = attrs,
  93. };
  94. /*
  95. * logs all the wake up reasons to the kernel
  96. * stores the irqs to expose them to the userspace via sysfs
  97. */
  98. void log_wakeup_reason(int irq)
  99. {
  100. struct irq_desc *desc;
  101. desc = irq_to_desc(irq);
  102. if (desc && desc->action && desc->action->name)
  103. printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq,
  104. desc->action->name);
  105. else
  106. printk(KERN_INFO "Resume caused by IRQ %d\n", irq);
  107. spin_lock(&resume_reason_lock);
  108. if (irqcount == MAX_WAKEUP_REASON_IRQS) {
  109. spin_unlock(&resume_reason_lock);
  110. printk(KERN_WARNING "Resume caused by more than %d IRQs\n",
  111. MAX_WAKEUP_REASON_IRQS);
  112. return;
  113. }
  114. irq_list[irqcount++] = irq;
  115. spin_unlock(&resume_reason_lock);
  116. }
  117. int check_wakeup_reason(int irq)
  118. {
  119. int irq_no;
  120. int ret = false;
  121. spin_lock(&resume_reason_lock);
  122. for (irq_no = 0; irq_no < irqcount; irq_no++)
  123. if (irq_list[irq_no] == irq) {
  124. ret = true;
  125. break;
  126. }
  127. spin_unlock(&resume_reason_lock);
  128. return ret;
  129. }
  130. void log_suspend_abort_reason(const char *fmt, ...)
  131. {
  132. va_list args;
  133. spin_lock(&resume_reason_lock);
  134. //Suspend abort reason has already been logged.
  135. if (suspend_abort) {
  136. spin_unlock(&resume_reason_lock);
  137. return;
  138. }
  139. suspend_abort = true;
  140. va_start(args, fmt);
  141. snprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args);
  142. va_end(args);
  143. spin_unlock(&resume_reason_lock);
  144. }
  145. /* Detects a suspend and clears all the previous wake up reasons*/
  146. static int wakeup_reason_pm_event(struct notifier_block *notifier,
  147. unsigned long pm_event, void *unused)
  148. {
  149. switch (pm_event) {
  150. case PM_SUSPEND_PREPARE:
  151. spin_lock(&resume_reason_lock);
  152. irqcount = 0;
  153. suspend_abort = false;
  154. spin_unlock(&resume_reason_lock);
  155. /* monotonic time since boot */
  156. last_monotime = ktime_get();
  157. /* monotonic time since boot including the time spent in suspend */
  158. last_stime = ktime_get_boottime();
  159. break;
  160. case PM_POST_SUSPEND:
  161. /* monotonic time since boot */
  162. curr_monotime = ktime_get();
  163. /* monotonic time since boot including the time spent in suspend */
  164. curr_stime = ktime_get_boottime();
  165. break;
  166. default:
  167. break;
  168. }
  169. return NOTIFY_DONE;
  170. }
  171. static struct notifier_block wakeup_reason_pm_notifier_block = {
  172. .notifier_call = wakeup_reason_pm_event,
  173. };
  174. /* Initializes the sysfs parameter
  175. * registers the pm_event notifier
  176. */
  177. int __init wakeup_reason_init(void)
  178. {
  179. int retval;
  180. retval = register_pm_notifier(&wakeup_reason_pm_notifier_block);
  181. if (retval)
  182. printk(KERN_WARNING "[%s] failed to register PM notifier %d\n",
  183. __func__, retval);
  184. wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj);
  185. if (!wakeup_reason) {
  186. printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n",
  187. __func__);
  188. return 1;
  189. }
  190. retval = sysfs_create_group(wakeup_reason, &attr_group);
  191. if (retval) {
  192. kobject_put(wakeup_reason);
  193. printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n",
  194. __func__, retval);
  195. }
  196. return 0;
  197. }
  198. late_initcall(wakeup_reason_init);