aboutsummaryrefslogtreecommitdiff
path: root/arch/metag/kernel/kick.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/metag/kernel/kick.c')
-rw-r--r--arch/metag/kernel/kick.c101
1 files changed, 101 insertions, 0 deletions
diff --git a/arch/metag/kernel/kick.c b/arch/metag/kernel/kick.c
new file mode 100644
index 00000000000..50fcbec98cd
--- /dev/null
+++ b/arch/metag/kernel/kick.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2009 Imagination Technologies
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * The Meta KICK interrupt mechanism is generally a useful feature, so
+ * we provide an interface for registering multiple interrupt
+ * handlers. All the registered interrupt handlers are "chained". When
+ * a KICK interrupt is received the first function in the list is
+ * called. If that interrupt handler cannot handle the KICK the next
+ * one is called, then the next until someone handles it (or we run
+ * out of functions). As soon as one function handles the interrupt no
+ * other handlers are called.
+ *
+ * The only downside of chaining interrupt handlers is that each
+ * handler must be able to detect whether the KICK was intended for it
+ * or not. For example, when the IPI handler runs and it sees that
+ * there are no IPI messages it must not signal that the KICK was
+ * handled, thereby giving the other handlers a chance to run.
+ *
+ * The reason that we provide our own interface for calling KICK
+ * handlers instead of using the generic kernel infrastructure is that
+ * the KICK handlers require access to a CPU's pTBI structure. So we
+ * pass it as an argument.
+ */
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/types.h>
+
+#include <asm/traps.h>
+
+/*
+ * All accesses/manipulations of kick_handlers_list should be
+ * performed while holding kick_handlers_lock.
+ */
+static DEFINE_SPINLOCK(kick_handlers_lock);
+static LIST_HEAD(kick_handlers_list);
+
+void kick_register_func(struct kick_irq_handler *kh)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kick_handlers_lock, flags);
+
+ list_add_tail(&kh->list, &kick_handlers_list);
+
+ spin_unlock_irqrestore(&kick_handlers_lock, flags);
+}
+EXPORT_SYMBOL(kick_register_func);
+
+void kick_unregister_func(struct kick_irq_handler *kh)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kick_handlers_lock, flags);
+
+ list_del(&kh->list);
+
+ spin_unlock_irqrestore(&kick_handlers_lock, flags);
+}
+EXPORT_SYMBOL(kick_unregister_func);
+
+TBIRES
+kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI)
+{
+ struct kick_irq_handler *kh;
+ struct list_head *lh;
+ int handled = 0;
+ TBIRES ret;
+
+ head_end(State, ~INTS_OFF_MASK);
+
+ /* If we interrupted user code handle any critical sections. */
+ if (State.Sig.SaveMask & TBICTX_PRIV_BIT)
+ restart_critical_section(State);
+
+ trace_hardirqs_off();
+
+ /*
+ * There is no need to disable interrupts here because we
+ * can't nest KICK interrupts in a KICK interrupt handler.
+ */
+ spin_lock(&kick_handlers_lock);
+
+ list_for_each(lh, &kick_handlers_list) {
+ kh = list_entry(lh, struct kick_irq_handler, list);
+
+ ret = kh->func(State, SigNum, Triggers, Inst, pTBI, &handled);
+ if (handled)
+ break;
+ }
+
+ spin_unlock(&kick_handlers_lock);
+
+ WARN_ON(!handled);
+
+ return tail_end(ret);
+}