inc: add chained ISR helper
A simple, generic helper for chaining together ISRs for a shared IRQ.
ISRs can be efficiently added or removed from a chain, even from inside
an interrupt context. The shared IRQ is automatically enabled or
disabled as ISRs are added and removed from the chain.
The platform is responsible for declaring the appropriate
ChainedInterrupt structures, providing callbacks to enable/disable the
underlying IRQ, and calling dispatchIsr() in the corresponding IRQ
handler.
Change-Id: I5fa36dd4c4092d4932c57cd8f4ebd8b78f558739
Signed-off-by: Greg Hackmann <[email protected]>
diff --git a/firmware/inc/isr.h b/firmware/inc/isr.h
new file mode 100644
index 0000000..c1e95d6
--- /dev/null
+++ b/firmware/inc/isr.h
@@ -0,0 +1,51 @@
+#ifndef __ISR_H
+#define __ISR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <cpu.h>
+#include <list.h>
+#include <util.h>
+
+struct ChainedInterrupt {
+ link_t isrs;
+
+ void (*const enable)(struct ChainedInterrupt *);
+ void (*const disable)(struct ChainedInterrupt *);
+};
+
+struct ChainedIsr {
+ link_t node;
+ bool (*const func)(struct ChainedIsr *);
+};
+
+static inline void chainIsr(struct ChainedInterrupt *interrupt, struct ChainedIsr *isr)
+{
+ interrupt->disable(interrupt);
+ list_add_tail(&interrupt->isrs, &isr->node);
+ interrupt->enable(interrupt);
+}
+
+static inline void unchainIsr(struct ChainedInterrupt *interrupt, struct ChainedIsr *isr)
+{
+ interrupt->disable(interrupt);
+ list_delete(&isr->node);
+ if (!list_is_empty(&interrupt->isrs))
+ interrupt->enable(interrupt);
+}
+
+static inline bool dispatchIsr(struct ChainedInterrupt *interrupt)
+{
+ struct link_t *cur, *tmp;
+ bool handled = false;
+
+ list_iterate(&interrupt->isrs, cur, tmp) {
+ struct ChainedIsr *curIsr = container_of(cur, struct ChainedIsr, node);
+ handled = handled || curIsr->func(curIsr);
+ }
+
+ return handled;
+}
+
+#endif /* __ISR_H */