wakeup_reason.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 irq_count;
  31. static struct kobject *wakeup_reason;
  32. static spinlock_t resume_reason_lock;
  33. static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr,
  34. char *buf)
  35. {
  36. int irq_no, buf_offset = 0;
  37. struct irq_desc *desc;
  38. spin_lock(&resume_reason_lock);
  39. for (irq_no = 0; irq_no < irq_count; irq_no++) {
  40. desc = irq_to_desc(irq_list[irq_no]);
  41. if (desc && desc->action && desc->action->name)
  42. buf_offset += sprintf(buf + buf_offset, "%d %s\n",
  43. irq_list[irq_no], desc->action->name);
  44. else
  45. buf_offset += sprintf(buf + buf_offset, "%d\n",
  46. irq_list[irq_no]);
  47. }
  48. spin_unlock(&resume_reason_lock);
  49. return buf_offset;
  50. }
  51. static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason);
  52. static struct attribute *attrs[] = {
  53. &resume_reason.attr,
  54. NULL,
  55. };
  56. static struct attribute_group attr_group = {
  57. .attrs = attrs,
  58. };
  59. /*
  60. * logs all the wake up reasons to the kernel
  61. * stores the irqs to expose them to the userspace via sysfs
  62. */
  63. void log_wakeup_reason(int irq)
  64. {
  65. struct irq_desc *desc;
  66. desc = irq_to_desc(irq);
  67. if (desc && desc->action && desc->action->name)
  68. printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq,
  69. desc->action->name);
  70. else
  71. printk(KERN_INFO "Resume caused by IRQ %d\n", irq);
  72. spin_lock(&resume_reason_lock);
  73. if (irq_count == MAX_WAKEUP_REASON_IRQS) {
  74. spin_unlock(&resume_reason_lock);
  75. printk(KERN_WARNING "Resume caused by more than %d IRQs\n",
  76. MAX_WAKEUP_REASON_IRQS);
  77. return;
  78. }
  79. irq_list[irq_count++] = irq;
  80. spin_unlock(&resume_reason_lock);
  81. }
  82. /* Detects a suspend and clears all the previous wake up reasons*/
  83. static int wakeup_reason_pm_event(struct notifier_block *notifier,
  84. unsigned long pm_event, void *unused)
  85. {
  86. switch (pm_event) {
  87. case PM_SUSPEND_PREPARE:
  88. spin_lock(&resume_reason_lock);
  89. irq_count = 0;
  90. spin_unlock(&resume_reason_lock);
  91. break;
  92. default:
  93. break;
  94. }
  95. return NOTIFY_DONE;
  96. }
  97. static struct notifier_block wakeup_reason_pm_notifier_block = {
  98. .notifier_call = wakeup_reason_pm_event,
  99. };
  100. /* Initializes the sysfs parameter
  101. * registers the pm_event notifier
  102. */
  103. int __init wakeup_reason_init(void)
  104. {
  105. int retval;
  106. spin_lock_init(&resume_reason_lock);
  107. retval = register_pm_notifier(&wakeup_reason_pm_notifier_block);
  108. if (retval)
  109. printk(KERN_WARNING "[%s] failed to register PM notifier %d\n",
  110. __func__, retval);
  111. wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj);
  112. if (!wakeup_reason) {
  113. printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n",
  114. __func__);
  115. return 1;
  116. }
  117. retval = sysfs_create_group(wakeup_reason, &attr_group);
  118. if (retval) {
  119. kobject_put(wakeup_reason);
  120. printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n",
  121. __func__, retval);
  122. }
  123. return 0;
  124. }
  125. late_initcall(wakeup_reason_init);
  126. #ifdef CONFIG_ARCH_MSM
  127. #include <linux/debugfs.h>
  128. #define NR_TOTAL_IRQS 1024
  129. struct wakeup_reason_stats {
  130. unsigned int wakeup_count;
  131. };
  132. static struct wakeup_reason_stats wakeup_reason_stats[NR_TOTAL_IRQS] = {{0,},};
  133. void update_wakeup_reason_stats(int irq)
  134. {
  135. pr_info("IRQ %d [%ps]\n", irq, __builtin_return_address(0));
  136. wakeup_reason_stats[irq].wakeup_count++;
  137. }
  138. #ifdef CONFIG_DEBUG_FS
  139. static int wakeup_reason_stats_show(struct seq_file *s, void *unused)
  140. {
  141. int i;
  142. pr_info("nr_irqs: %d\n", nr_irqs);
  143. seq_puts(s, "irq\twakeup_count\tname\n");
  144. for (i = 0; i < nr_irqs; i++) {
  145. if (wakeup_reason_stats[i].wakeup_count > 0) {
  146. struct irq_desc *desc = irq_to_desc(i);
  147. const char *irq_name = NULL;
  148. if (desc && desc->action && desc->action->name)
  149. irq_name = desc->action->name;
  150. seq_printf(s, "%d\t%u\t%s\n", i,
  151. wakeup_reason_stats[i].wakeup_count, irq_name);
  152. }
  153. }
  154. return 0;
  155. }
  156. static int wakeup_reason_stats_open(struct inode *inode, struct file *file)
  157. {
  158. return single_open(file, wakeup_reason_stats_show, NULL);
  159. }
  160. static const struct file_operations wakeup_reason_stats_ops = {
  161. .open = wakeup_reason_stats_open,
  162. .read = seq_read,
  163. .llseek = seq_lseek,
  164. .release = single_release,
  165. };
  166. static int __init wakeup_reason_debugfs_init(void)
  167. {
  168. debugfs_create_file("wakeup_reason_stats", S_IFREG | S_IRUGO,
  169. NULL, NULL, &wakeup_reason_stats_ops);
  170. return 0;
  171. }
  172. late_initcall(wakeup_reason_debugfs_init);
  173. #endif /* CONFIG_DEBUG_FS */
  174. #endif /* CONFIG_ARCH_MSM */