/* * 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); }