Branch data Line data Source code
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 : :
18 : : #include <linux/wakeup_reason.h>
19 : : #include <linux/kernel.h>
20 : : #include <linux/irq.h>
21 : : #include <linux/interrupt.h>
22 : : #include <linux/io.h>
23 : : #include <linux/kobject.h>
24 : : #include <linux/sysfs.h>
25 : : #include <linux/init.h>
26 : : #include <linux/spinlock.h>
27 : : #include <linux/notifier.h>
28 : : #include <linux/suspend.h>
29 : :
30 : :
31 : : #define MAX_WAKEUP_REASON_IRQS 32
32 : : static int irq_list[MAX_WAKEUP_REASON_IRQS];
33 : : static int irqcount;
34 : : static struct kobject *wakeup_reason;
35 : : static spinlock_t resume_reason_lock;
36 : :
37 : 0 : static ssize_t reason_show(struct kobject *kobj, struct kobj_attribute *attr,
38 : : char *buf)
39 : : {
40 : : int irq_no, buf_offset = 0;
41 : : struct irq_desc *desc;
42 : : spin_lock(&resume_reason_lock);
43 [ # # ]: 0 : for (irq_no = 0; irq_no < irqcount; irq_no++) {
44 : 0 : desc = irq_to_desc(irq_list[irq_no]);
45 [ # # ][ # # ]: 0 : if (desc && desc->action && desc->action->name)
[ # # ]
46 : 0 : buf_offset += sprintf(buf + buf_offset, "%d %s\n",
47 : : irq_list[irq_no], desc->action->name);
48 : : else
49 : 0 : buf_offset += sprintf(buf + buf_offset, "%d\n",
50 : : irq_list[irq_no]);
51 : : }
52 : : spin_unlock(&resume_reason_lock);
53 : 0 : return buf_offset;
54 : : }
55 : :
56 : : static struct kobj_attribute resume_reason = __ATTR(last_resume_reason, 0666,
57 : : reason_show, NULL);
58 : :
59 : : static struct attribute *attrs[] = {
60 : : &resume_reason.attr,
61 : : NULL,
62 : : };
63 : : static struct attribute_group attr_group = {
64 : : .attrs = attrs,
65 : : };
66 : :
67 : : /*
68 : : * logs all the wake up reasons to the kernel
69 : : * stores the irqs to expose them to the userspace via sysfs
70 : : */
71 : 0 : void log_wakeup_reason(int irq)
72 : : {
73 : : struct irq_desc *desc;
74 : 0 : desc = irq_to_desc(irq);
75 [ # # ][ # # ]: 0 : if (desc && desc->action && desc->action->name)
[ # # ]
76 : 0 : printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq,
77 : : desc->action->name);
78 : : else
79 : 0 : printk(KERN_INFO "Resume caused by IRQ %d\n", irq);
80 : :
81 : : spin_lock(&resume_reason_lock);
82 [ # # ]: 0 : if (irqcount == MAX_WAKEUP_REASON_IRQS) {
83 : : spin_unlock(&resume_reason_lock);
84 : 0 : printk(KERN_WARNING "Resume caused by more than %d IRQs\n",
85 : : MAX_WAKEUP_REASON_IRQS);
86 : 0 : return;
87 : : }
88 : :
89 : 0 : irq_list[irqcount++] = irq;
90 : : spin_unlock(&resume_reason_lock);
91 : : }
92 : :
93 : : /* Detects a suspend and clears all the previous wake up reasons*/
94 : 0 : static int wakeup_reason_pm_event(struct notifier_block *notifier,
95 : : unsigned long pm_event, void *unused)
96 : : {
97 [ # # ]: 0 : switch (pm_event) {
98 : : case PM_SUSPEND_PREPARE:
99 : : spin_lock(&resume_reason_lock);
100 : 0 : irqcount = 0;
101 : : spin_unlock(&resume_reason_lock);
102 : : break;
103 : : default:
104 : : break;
105 : : }
106 : 0 : return NOTIFY_DONE;
107 : : }
108 : :
109 : : static struct notifier_block wakeup_reason_pm_notifier_block = {
110 : : .notifier_call = wakeup_reason_pm_event,
111 : : };
112 : :
113 : : /* Initializes the sysfs parameter
114 : : * registers the pm_event notifier
115 : : */
116 : 0 : int __init wakeup_reason_init(void)
117 : : {
118 : : int retval;
119 : 0 : spin_lock_init(&resume_reason_lock);
120 : 0 : retval = register_pm_notifier(&wakeup_reason_pm_notifier_block);
121 [ # # ]: 0 : if (retval)
122 : 0 : printk(KERN_WARNING "[%s] failed to register PM notifier %d\n",
123 : : __func__, retval);
124 : :
125 : 0 : wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj);
126 [ # # ]: 0 : if (!wakeup_reason) {
127 : 0 : printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n",
128 : : __func__);
129 : 0 : return 1;
130 : : }
131 : 0 : retval = sysfs_create_group(wakeup_reason, &attr_group);
132 [ # # ]: 0 : if (retval) {
133 : 0 : kobject_put(wakeup_reason);
134 : 0 : printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n",
135 : : __func__, retval);
136 : : }
137 : : return 0;
138 : : }
139 : :
140 : : late_initcall(wakeup_reason_init);
|