diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index 0ab546558c4e2e125ba5ab3c1a294e2bd3b3e144..e6f1e79b9e8936cc52922b0ee460719d544c4847 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/wakeup_reason.h>
+#include <linux/irq.h>
 
 static LIST_HEAD(syscore_ops_list);
 static DEFINE_MUTEX(syscore_ops_lock);
diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c
index 2ca80154d75b25e2103cf6c409135ceaa12accc8..dbe079ac3b964c1f121eb0e62fa43fb94bda71b6 100644
--- a/drivers/irqchip/irq-gic.c
+++ b/drivers/irqchip/irq-gic.c
@@ -264,7 +264,7 @@ static void gic_show_resume_irq(struct gic_chip_data *gic)
 	for (i = find_first_bit(pending, gic->gic_irqs);
 		i < gic->gic_irqs;
 		i = find_next_bit(pending, gic->gic_irqs, i+1)) {
-		log_wakeup_reason(i + gic->irq_offset);
+		log_base_wakeup_reason(i + gic->irq_offset);
 	}
 }
 
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 881ed916efefe7a200723d672402cf94415a1161..bf34318ec79f5851b03df0142277e3b17d87b212 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -109,6 +109,7 @@ static inline struct msi_desc *irq_desc_get_msi_desc(struct irq_desc *desc)
  * irqchip-style controller then we call the ->handle_irq() handler,
  * and it calls __do_IRQ() if it's attached to an irqtype-style controller.
  */
+
 static inline bool generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
 {
 	return desc->handle_irq(irq, desc);
diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h
index 93a2e4ca1daf9f5d1998041e6c9a046d5dde8c92..6d585a4824f124d5319a135b06fbc8e9d01ab6c3 100644
--- a/include/linux/wakeup_reason.h
+++ b/include/linux/wakeup_reason.h
@@ -19,13 +19,71 @@
 #define _LINUX_WAKEUP_REASON_H
 
 #include <linux/types.h>
+#include <linux/completion.h>
 
 #define MAX_SUSPEND_ABORT_LEN 256
 
-void log_wakeup_reason(int irq);
+struct wakeup_irq_node {
+	/* @leaf is a linked list of all leaf nodes in the interrupts trees.
+	 */
+	struct list_head next;
+	/* @irq: IRQ number of this node.
+	 */
+	int irq;
+	struct irq_desc *desc;
+
+	/* @siblings contains the list of irq nodes at the same depth; at a
+	 * depth of zero, this is the list of base wakeup interrupts.
+	 */
+	struct list_head siblings;
+	/* @parent: only one node in a siblings list has a pointer to the
+	 * parent; that node is the head of the list of siblings.
+	 */
+	struct wakeup_irq_node *parent;
+	/* @child: any node can have one child
+	 */
+	struct wakeup_irq_node *child;
+	/* @handled: this flag is set to true when the interrupt handler (one of
+	 * handle_.*_irq in kernel/irq/handle.c) for this node gets called; it is set
+	 * to false otherwise.  We use this flag to determine whether a subtree rooted
+	 * at a node has been handled.  When all trees rooted at
+	 * base-wakeup-interrupt nodes have been handled, we stop logging
+	 * potential wakeup interrupts, and construct the list of actual
+	 * wakeups from the leaves of these trees.
+	 */
+	bool handled;
+};
+
+/* Called in the resume path, with interrupts and nonboot cpus disabled; on
+ * need for a spinlock.
+ */
+static inline void start_logging_wakeup_reasons(void)
+{
+	extern bool log_wakeups;
+	extern struct completion wakeups_completion;
+	log_wakeups = true;
+	init_completion(&wakeups_completion);
+}
+
+static inline bool logging_wakeup_reasons(void)
+{
+	extern bool log_wakeups;
+	return ACCESS_ONCE(log_wakeups);
+}
+
+void log_base_wakeup_reason(int irq);
+
 void log_suspend_abort_reason(const char *fmt, ...);
+
+bool log_possible_wakeup_reason(int irq,
+			struct irq_desc *desc,
+			bool (*handler)(unsigned int, struct irq_desc *));
+
 int check_wakeup_reason(int irq);
-const int* get_wakeup_reasons(size_t *len);
+
+const struct list_head*
+get_wakeup_reasons(unsigned long timeout, struct list_head *unfinished);
+
 void clear_wakeup_reasons(void);
 
 #endif /* _LINUX_WAKEUP_REASON_H */
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 8ab8e9390297a06ef7c4efc2a8ad502433b13879..b4f085161527d0f76b4c7971fdc03b45fb0130e1 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -14,6 +14,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/radix-tree.h>
 #include <linux/bitmap.h>
+#include <linux/wakeup_reason.h>
 
 #include "internals.h"
 
@@ -304,16 +305,25 @@ static int irq_expand_nr_irqs(unsigned int nr)
 /**
  * generic_handle_irq - Invoke the handler for a particular irq
  * @irq:	The irq number to handle
- *
+ * returns:
+ * 	negative on error
+ *	0 when the interrupt handler was not called
+ *	1 when the interrupt handler was called
  */
+
 int generic_handle_irq(unsigned int irq)
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 
 	if (!desc)
 		return -EINVAL;
-	generic_handle_irq_desc(irq, desc);
-	return 0;
+
+	if (unlikely(logging_wakeup_reasons()))
+		return log_possible_wakeup_reason(irq,
+				desc,
+				generic_handle_irq_desc);
+
+	return generic_handle_irq_desc(irq, desc);
 }
 EXPORT_SYMBOL_GPL(generic_handle_irq);
 
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 7c53fea31cba66727d0f99514aa9717e6af68392..b0a12b0c3eca7b772850418afb5bd1770a751467 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -237,6 +237,8 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
 				MAX_SUSPEND_ABORT_LEN);
 			log_suspend_abort_reason(suspend_abort);
 		}
+
+		start_logging_wakeup_reasons();
 		syscore_resume();
 	}
 
diff --git a/kernel/power/wakeup_reason.c b/kernel/power/wakeup_reason.c
index ce8b2978b9c440815b3287bb355df23d633b1b77..0f446650f0ac29100fa1756d657387ed8ba4f054 100644
--- a/kernel/power/wakeup_reason.c
+++ b/kernel/power/wakeup_reason.c
@@ -26,37 +26,223 @@
 #include <linux/spinlock.h>
 #include <linux/notifier.h>
 #include <linux/suspend.h>
-
+#include <linux/slab.h>
 
 #define MAX_WAKEUP_REASON_IRQS 32
-static int irq_list[MAX_WAKEUP_REASON_IRQS];
-static int irqcount;
 static bool suspend_abort;
 static char abort_reason[MAX_SUSPEND_ABORT_LEN];
+
+static struct wakeup_irq_node *base_irq_nodes;
+static struct wakeup_irq_node *cur_irq_tree;
+static int cur_irq_tree_depth;
+static LIST_HEAD(wakeup_irqs);
+
+static struct kmem_cache *wakeup_irq_nodes_cache;
 static struct kobject *wakeup_reason;
-static DEFINE_SPINLOCK(resume_reason_lock);
+static spinlock_t resume_reason_lock;
+bool log_wakeups __read_mostly;
+struct completion wakeups_completion;
 
-static ssize_t last_resume_reason_show(struct kobject *kobj, struct kobj_attribute *attr,
-		char *buf)
+static void init_wakeup_irq_node(struct wakeup_irq_node *p, int irq)
 {
-	int irq_no, buf_offset = 0;
-	struct irq_desc *desc;
-	spin_lock(&resume_reason_lock);
-	if (suspend_abort) {
-		buf_offset = sprintf(buf, "Abort: %s", abort_reason);
-	} else {
-		for (irq_no = 0; irq_no < irqcount; irq_no++) {
-			desc = irq_to_desc(irq_list[irq_no]);
-			if (desc && desc->action && desc->action->name)
-				buf_offset += sprintf(buf + buf_offset, "%d %s\n",
-						irq_list[irq_no], desc->action->name);
-			else
-				buf_offset += sprintf(buf + buf_offset, "%d\n",
-						irq_list[irq_no]);
+	p->irq = irq;
+	p->desc = irq_to_desc(irq);
+	p->child = NULL;
+	p->parent = NULL;
+	p->handled = false;
+	INIT_LIST_HEAD(&p->siblings);
+	INIT_LIST_HEAD(&p->next);
+}
+
+static struct wakeup_irq_node* alloc_irq_node(int irq)
+{
+	struct wakeup_irq_node *n;
+
+	n = kmem_cache_alloc(wakeup_irq_nodes_cache, GFP_ATOMIC);
+	if (!n) {
+		pr_warning("Failed to log chained wakeup IRQ %d\n",
+			irq);
+		return NULL;
+	}
+
+	init_wakeup_irq_node(n, irq);
+	return n;
+}
+
+static struct wakeup_irq_node *
+search_siblings(struct wakeup_irq_node *root, int irq)
+{
+	bool found = false;
+	struct wakeup_irq_node *n = NULL;
+	BUG_ON(!root);
+
+	if (root->irq == irq)
+		return root;
+
+	list_for_each_entry(n, &root->siblings, siblings) {
+		if (n->irq == irq) {
+			found = true;
+			break;
 		}
 	}
-	spin_unlock(&resume_reason_lock);
-	return buf_offset;
+
+	return found ? n : NULL;
+}
+
+static struct wakeup_irq_node *
+add_to_siblings(struct wakeup_irq_node *root, int irq)
+{
+	struct wakeup_irq_node *n;
+	if (root) {
+		n = search_siblings(root, irq);
+		if (n)
+			return n;
+	}
+	n = alloc_irq_node(irq);
+
+	if (n && root)
+		list_add(&n->siblings, &root->siblings);
+	return n;
+}
+
+static struct wakeup_irq_node* add_child(struct wakeup_irq_node *root, int irq)
+{
+	if (!root->child) {
+		root->child = alloc_irq_node(irq);
+		if (!root->child)
+			return NULL;
+		root->child->parent = root;
+		return root->child;
+	}
+
+	return add_to_siblings(root->child, irq);
+}
+
+static struct wakeup_irq_node *find_first_sibling(struct wakeup_irq_node *node)
+{
+	struct wakeup_irq_node *n;
+	if (node->parent)
+		return node;
+	list_for_each_entry(n, &node->siblings, siblings) {
+		if (n->parent)
+			return n;
+	}
+	return NULL;
+}
+
+static struct wakeup_irq_node *
+get_base_node(struct wakeup_irq_node *node, unsigned depth)
+{
+	if (!node)
+		return NULL;
+
+	while (depth) {
+		node = find_first_sibling(node);
+		BUG_ON(!node);
+		node = node->parent;
+		depth--;
+	}
+
+	return node;
+}
+
+static const struct list_head* get_wakeup_reasons_nosync(void);
+
+static void print_wakeup_sources(void)
+{
+	struct wakeup_irq_node *n;
+	const struct list_head *wakeups;
+
+	if (suspend_abort) {
+		pr_info("Abort: %s", abort_reason);
+		return;
+	}
+
+	wakeups = get_wakeup_reasons_nosync();
+	list_for_each_entry(n, wakeups, next) {
+		if (n->desc && n->desc->action && n->desc->action->name)
+			pr_info("Resume caused by IRQ %d, %s\n", n->irq,
+				n->desc->action->name);
+		else
+			pr_info("Resume caused by IRQ %d\n", n->irq);
+	}
+}
+
+static bool walk_irq_node_tree(struct wakeup_irq_node *root,
+		bool (*visit)(struct wakeup_irq_node *, void *),
+		void *cookie)
+{
+	struct wakeup_irq_node *n, *t;
+
+	if (!root)
+		return true;
+
+	list_for_each_entry_safe(n, t, &root->siblings, siblings) {
+		if (!walk_irq_node_tree(n->child, visit, cookie))
+			return false;
+		if (!visit(n, cookie))
+			return false;
+	}
+
+	if (!walk_irq_node_tree(root->child, visit, cookie))
+		return false;
+	return visit(root, cookie);
+}
+
+static bool is_node_handled(struct wakeup_irq_node *n, void *_p)
+{
+	return n->handled;
+}
+
+static bool base_irq_nodes_done(void)
+{
+	return walk_irq_node_tree(base_irq_nodes, is_node_handled, NULL);
+}
+
+struct buf_cookie {
+	char *buf;
+	int buf_offset;
+};
+
+static bool print_leaf_node(struct wakeup_irq_node *n, void *_p)
+{
+	struct buf_cookie *b = _p;
+	if (!n->child) {
+		if (n->desc && n->desc->action && n->desc->action->name)
+			b->buf_offset +=
+				snprintf(b->buf + b->buf_offset,
+					PAGE_SIZE - b->buf_offset,
+					"%d %s\n",
+					n->irq, n->desc->action->name);
+		else
+			b->buf_offset +=
+				snprintf(b->buf + b->buf_offset,
+					PAGE_SIZE - b->buf_offset,
+					"%d\n",
+					n->irq);
+	}
+	return true;
+}
+
+static ssize_t last_resume_reason_show(struct kobject *kobj,
+					struct kobj_attribute *attr,
+					char *buf)
+{
+	unsigned long flags;
+
+	struct buf_cookie b = {
+		.buf = buf,
+		.buf_offset = 0
+	};
+
+	spin_lock_irqsave(&resume_reason_lock, flags);
+	if (suspend_abort)
+		b.buf_offset = snprintf(buf, PAGE_SIZE, "Abort: %s", abort_reason);
+	else
+		walk_irq_node_tree(base_irq_nodes, print_leaf_node, &b);
+	spin_unlock_irqrestore(&resume_reason_lock, flags);
+
+	return b.buf_offset;
 }
 
 static struct kobj_attribute resume_reason = __ATTR_RO(last_resume_reason);
@@ -69,45 +255,128 @@ static struct attribute_group attr_group = {
 	.attrs = attrs,
 };
 
+static inline void stop_logging_wakeup_reasons(void)
+{
+	ACCESS_ONCE(log_wakeups) = false;
+}
+
 /*
- * logs all the wake up reasons to the kernel
- * stores the irqs to expose them to the userspace via sysfs
+ * stores the immediate wakeup irqs; these often aren't the ones seen by
+ * the drivers that registered them, due to chained interrupt controllers,
+ * and multiple-interrupt dispatch.
  */
-void log_wakeup_reason(int irq)
+void log_base_wakeup_reason(int irq)
 {
-	struct irq_desc *desc;
-	desc = irq_to_desc(irq);
-	if (desc && desc->action && desc->action->name)
-		printk(KERN_INFO "Resume caused by IRQ %d, %s\n", irq,
-				desc->action->name);
-	else
-		printk(KERN_INFO "Resume caused by IRQ %d\n", irq);
+	/* No locking is needed, since this function is called within
+	 * syscore_resume, with both nonboot CPUs and interrupts disabled.
+	 */
+	base_irq_nodes = add_to_siblings(base_irq_nodes, irq);
+	BUG_ON(!base_irq_nodes);
+}
 
-	spin_lock(&resume_reason_lock);
-	if (irqcount == MAX_WAKEUP_REASON_IRQS) {
-		spin_unlock(&resume_reason_lock);
-		printk(KERN_WARNING "Resume caused by more than %d IRQs\n",
-				MAX_WAKEUP_REASON_IRQS);
-		return;
+/* This function is called by generic_handle_irq, which may call itself
+ * recursively.  This happens with interrupts disabled.  Using
+ * log_possible_wakeup_reason, we build a tree of interrupts, tracing the call
+ * stack of generic_handle_irq, for each wakeup source containing the
+ * interrupts actually handled.
+ *
+ * Most of these "trees" would either have a single node (in the event that the
+ * wakeup source is the final interrupt), or consist of a list of two
+ * interrupts, with the wakeup source at the root, and the final dispatched
+ * interrupt at the leaf.
+ *
+ * When *all* wakeup sources have been thusly spoken for, this function will
+ * clear the log_wakeups flag, and print the wakeup reasons.
+
+   TODO: percpu
+
+ */
+
+struct wakeup_irq_node *
+log_possible_wakeup_reason_start(int irq, struct irq_desc *desc, unsigned depth)
+{
+	BUG_ON(!irqs_disabled() || !logging_wakeup_reasons());
+	BUG_ON((signed)depth < 0);
+
+	/* If suspend was aborted, the base IRQ nodes are missing, and we stop
+	 * logging interrupts immediately.
+	 */
+	if (!base_irq_nodes) {
+		stop_logging_wakeup_reasons();
+		return NULL;
 	}
 
-	irq_list[irqcount++] = irq;
-	spin_unlock(&resume_reason_lock);
+	/* We assume wakeup interrupts are handlerd only by the first core. */
+	/* TODO: relax this by having percpu versions of the irq tree */
+	if (smp_processor_id() != 0) {
+		return NULL;
+	}
+
+	if (depth == 0) {
+		cur_irq_tree_depth = 0;
+		cur_irq_tree = search_siblings(base_irq_nodes, irq);
+	}
+	else if (cur_irq_tree) {
+		if (depth > cur_irq_tree_depth) {
+			BUG_ON(depth - cur_irq_tree_depth > 1);
+			cur_irq_tree = add_child(cur_irq_tree, irq);
+			if (cur_irq_tree)
+				cur_irq_tree_depth++;
+		}
+		else {
+			cur_irq_tree = get_base_node(cur_irq_tree,
+					cur_irq_tree_depth - depth);
+			cur_irq_tree_depth = depth;
+			cur_irq_tree = add_to_siblings(cur_irq_tree, irq);
+		}
+	}
+
+	return cur_irq_tree;
 }
 
-int check_wakeup_reason(int irq)
+void log_possible_wakeup_reason_complete(struct wakeup_irq_node *n,
+					unsigned depth,
+					bool handled)
 {
-	int irq_no;
-	int ret = false;
-
-	spin_lock(&resume_reason_lock);
-	for (irq_no = 0; irq_no < irqcount; irq_no++)
-		if (irq_list[irq_no] == irq) {
-			ret = true;
-			break;
+	if (!n)
+		return;
+	n->handled = handled;
+	if (depth == 0) {
+		if (base_irq_nodes_done()) {
+			stop_logging_wakeup_reasons();
+			complete(&wakeups_completion);
+			print_wakeup_sources();
+		}
 	}
-	spin_unlock(&resume_reason_lock);
-	return ret;
+}
+
+bool log_possible_wakeup_reason(int irq,
+			struct irq_desc *desc,
+			bool (*handler)(unsigned int, struct irq_desc *))
+{
+	static DEFINE_PER_CPU(unsigned int, depth);
+
+	struct wakeup_irq_node *n;
+	bool handled;
+	unsigned d;
+
+	d = get_cpu_var(depth)++;
+	put_cpu_var(depth);
+
+	n = log_possible_wakeup_reason_start(irq, desc, d);
+
+	handled = handler(irq, desc);
+
+	d = --get_cpu_var(depth);
+	put_cpu_var(depth);
+
+	if (!handled && desc && desc->action)
+		pr_debug("%s: irq %d action %pF not handled\n", __func__,
+			irq, desc->action->handler);
+
+	log_possible_wakeup_reason_complete(n, d, handled);
+
+	return handled;
 }
 
 void log_suspend_abort_reason(const char *fmt, ...)
@@ -126,21 +395,95 @@ void log_suspend_abort_reason(const char *fmt, ...)
 	va_start(args, fmt);
 	snprintf(abort_reason, MAX_SUSPEND_ABORT_LEN, fmt, args);
 	va_end(args);
+
 	spin_unlock(&resume_reason_lock);
 }
 
-const int* get_wakeup_reasons(size_t *len)
+static bool match_node(struct wakeup_irq_node *n, void *_p)
 {
-	*len = irqcount;
-	return irq_list;
+	int irq = *((int *)_p);
+	return n->irq != irq;
 }
 
-void clear_wakeup_reasons(void)
+int check_wakeup_reason(int irq)
 {
+	bool found;
 	spin_lock(&resume_reason_lock);
-	irqcount = 0;
-	suspend_abort = false;
+	found = !walk_irq_node_tree(base_irq_nodes, match_node, &irq);
 	spin_unlock(&resume_reason_lock);
+	return found;
+}
+
+static bool build_leaf_nodes(struct wakeup_irq_node *n, void *_p)
+{
+	struct list_head *wakeups = _p;
+	if (!n->child)
+		list_add(&n->next, wakeups);
+	return true;
+}
+
+static const struct list_head* get_wakeup_reasons_nosync(void)
+{
+	BUG_ON(logging_wakeup_reasons());
+	INIT_LIST_HEAD(&wakeup_irqs);
+	walk_irq_node_tree(base_irq_nodes, build_leaf_nodes, &wakeup_irqs);
+	return &wakeup_irqs;
+}
+
+static bool build_unfinished_nodes(struct wakeup_irq_node *n, void *_p)
+{
+	struct list_head *unfinished = _p;
+	if (!n->handled) {
+		pr_warning("%s: wakeup irq %d was not handled\n",
+			   __func__, n->irq);
+		list_add(&n->next, unfinished);
+	}
+	return true;
+}
+
+const struct list_head* get_wakeup_reasons(unsigned long timeout,
+					struct list_head *unfinished)
+{
+	INIT_LIST_HEAD(unfinished);
+
+	if (logging_wakeup_reasons()) {
+		unsigned long signalled = 0;
+		if (timeout)
+			signalled = wait_for_completion_timeout(&wakeups_completion, timeout);
+		if (WARN_ON(!signalled)) {
+			stop_logging_wakeup_reasons();
+			walk_irq_node_tree(base_irq_nodes, build_unfinished_nodes, unfinished);
+			return NULL;
+		}
+		pr_info("%s: waited for %u ms\n",
+				__func__,
+				jiffies_to_msecs(timeout - signalled));
+	}
+
+	return get_wakeup_reasons_nosync();
+}
+
+static bool delete_node(struct wakeup_irq_node *n, void *unused)
+{
+	list_del(&n->siblings);
+	kmem_cache_free(wakeup_irq_nodes_cache, n);
+	return true;
+}
+
+void clear_wakeup_reasons(void)
+{
+	unsigned long flags;
+	spin_lock_irqsave(&resume_reason_lock, flags);
+
+	BUG_ON(logging_wakeup_reasons());
+	walk_irq_node_tree(base_irq_nodes, delete_node, NULL);
+	base_irq_nodes = NULL;
+	cur_irq_tree = NULL;
+	cur_irq_tree_depth = 0;
+	INIT_LIST_HEAD(&wakeup_irqs);
+	suspend_abort = false;
+
+	spin_unlock_irqrestore(&resume_reason_lock, flags);
 }
 
 /* Detects a suspend and clears all the previous wake up reasons*/
@@ -149,9 +492,15 @@ static int wakeup_reason_pm_event(struct notifier_block *notifier,
 {
 	switch (pm_event) {
 	case PM_SUSPEND_PREPARE:
-		spin_lock(&resume_reason_lock);
 		clear_wakeup_reasons();
-		spin_unlock(&resume_reason_lock);
+		break;
+	case PM_POST_SUSPEND:
+		/* log_wakeups should have been cleared by now. */
+		if (WARN_ON(logging_wakeup_reasons())) {
+			stop_logging_wakeup_reasons();
+			mb();
+			print_wakeup_sources();
+		}
 		break;
 	default:
 		break;
@@ -163,31 +512,46 @@ static struct notifier_block wakeup_reason_pm_notifier_block = {
 	.notifier_call = wakeup_reason_pm_event,
 };
 
-/* Initializes the sysfs parameter
- * registers the pm_event notifier
- */
 int __init wakeup_reason_init(void)
 {
-	int retval;
+	spin_lock_init(&resume_reason_lock);
 
-	retval = register_pm_notifier(&wakeup_reason_pm_notifier_block);
-	if (retval)
-		printk(KERN_WARNING "[%s] failed to register PM notifier %d\n",
-				__func__, retval);
+	if (register_pm_notifier(&wakeup_reason_pm_notifier_block)) {
+		pr_warning("[%s] failed to register PM notifier\n",
+			__func__);
+		goto fail;
+	}
 
 	wakeup_reason = kobject_create_and_add("wakeup_reasons", kernel_kobj);
 	if (!wakeup_reason) {
-		printk(KERN_WARNING "[%s] failed to create a sysfs kobject\n",
+		pr_warning("[%s] failed to create a sysfs kobject\n",
 				__func__);
-		return 1;
+		goto fail_unregister_pm_notifier;
 	}
-	retval = sysfs_create_group(wakeup_reason, &attr_group);
-	if (retval) {
-		kobject_put(wakeup_reason);
-		printk(KERN_WARNING "[%s] failed to create a sysfs group %d\n",
-				__func__, retval);
+
+	if (sysfs_create_group(wakeup_reason, &attr_group)) {
+		pr_warning("[%s] failed to create a sysfs group\n",
+			__func__);
+		goto fail_kobject_put;
 	}
+
+	wakeup_irq_nodes_cache =
+		kmem_cache_create("wakeup_irq_node_cache",
+					sizeof(struct wakeup_irq_node), 0,
+					0, NULL);
+	if (!wakeup_irq_nodes_cache)
+		goto fail_remove_group;
+
 	return 0;
+
+fail_remove_group:
+	sysfs_remove_group(wakeup_reason, &attr_group);
+fail_kobject_put:
+	kobject_put(wakeup_reason);
+fail_unregister_pm_notifier:
+	unregister_pm_notifier(&wakeup_reason_pm_notifier_block);
+fail:
+	return 1;
 }
 
 late_initcall(wakeup_reason_init);