diff options
Diffstat (limited to 'include/linux/wakeup_reason.h')
-rw-r--r-- | include/linux/wakeup_reason.h | 82 |
1 files changed, 77 insertions, 5 deletions
diff --git a/include/linux/wakeup_reason.h b/include/linux/wakeup_reason.h index d84d8c301546..a015ce5c830f 100644 --- a/include/linux/wakeup_reason.h +++ b/include/linux/wakeup_reason.h @@ -18,15 +18,87 @@ #ifndef _LINUX_WAKEUP_REASON_H #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); -int check_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; +}; + +#ifdef CONFIG_DEDUCE_WAKEUP_REASONS + +/* 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; + ACCESS_ONCE(log_wakeups) = true; + init_completion(&wakeups_completion); +} + +static inline bool logging_wakeup_reasons_nosync(void) +{ + extern bool log_wakeups; + return ACCESS_ONCE(log_wakeups); +} + +static inline bool logging_wakeup_reasons(void) +{ + smp_rmb(); + return logging_wakeup_reasons_nosync(); +} + +bool log_possible_wakeup_reason(int irq, + struct irq_desc *desc, + bool (*handler)(struct irq_desc *)); -#ifdef CONFIG_SUSPEND -void log_suspend_abort_reason(const char *fmt, ...); #else -static inline void log_suspend_abort_reason(const char *fmt, ...) { } + +static inline void start_logging_wakeup_reasons(void) {} +static inline bool logging_wakeup_reasons_nosync(void) { return false; } +static inline bool logging_wakeup_reasons(void) { return false; } +static inline bool log_possible_wakeup_reason(int irq, + struct irq_desc *desc, + bool (*handler)(struct irq_desc *)) { return true; } + #endif +const struct list_head* +get_wakeup_reasons(unsigned long timeout, struct list_head *unfinished); +void log_base_wakeup_reason(int irq); +void clear_wakeup_reasons(void); +void log_suspend_abort_reason(const char *fmt, ...); +int check_wakeup_reason(int irq); + #endif /* _LINUX_WAKEUP_REASON_H */ |