| /* | |
| * 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/hardirq.h> | |
| #include <linux/irq.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 pt_regs *old_regs; | |
| 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(); | |
| old_regs = set_irq_regs((struct pt_regs *)State.Sig.pCtx); | |
| irq_enter(); | |
| /* | |
| * 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); | |
| irq_exit(); | |
| set_irq_regs(old_regs); | |
| return tail_end(ret); | |
| } |