1/* 2 * Copyright (C) 2009 Imagination Technologies 3 * 4 * This file is subject to the terms and conditions of the GNU General Public 5 * License. See the file COPYING in the main directory of this archive 6 * for more details. 7 * 8 * The Meta KICK interrupt mechanism is generally a useful feature, so 9 * we provide an interface for registering multiple interrupt 10 * handlers. All the registered interrupt handlers are "chained". When 11 * a KICK interrupt is received the first function in the list is 12 * called. If that interrupt handler cannot handle the KICK the next 13 * one is called, then the next until someone handles it (or we run 14 * out of functions). As soon as one function handles the interrupt no 15 * other handlers are called. 16 * 17 * The only downside of chaining interrupt handlers is that each 18 * handler must be able to detect whether the KICK was intended for it 19 * or not. For example, when the IPI handler runs and it sees that 20 * there are no IPI messages it must not signal that the KICK was 21 * handled, thereby giving the other handlers a chance to run. 22 * 23 * The reason that we provide our own interface for calling KICK 24 * handlers instead of using the generic kernel infrastructure is that 25 * the KICK handlers require access to a CPU's pTBI structure. So we 26 * pass it as an argument. 27 */ 28#include <linux/export.h> 29#include <linux/hardirq.h> 30#include <linux/irq.h> 31#include <linux/kernel.h> 32#include <linux/mm.h> 33#include <linux/types.h> 34 35#include <asm/traps.h> 36 37/* 38 * All accesses/manipulations of kick_handlers_list should be 39 * performed while holding kick_handlers_lock. 40 */ 41static DEFINE_SPINLOCK(kick_handlers_lock); 42static LIST_HEAD(kick_handlers_list); 43 44void kick_register_func(struct kick_irq_handler *kh) 45{ 46 unsigned long flags; 47 48 spin_lock_irqsave(&kick_handlers_lock, flags); 49 50 list_add_tail(&kh->list, &kick_handlers_list); 51 52 spin_unlock_irqrestore(&kick_handlers_lock, flags); 53} 54EXPORT_SYMBOL(kick_register_func); 55 56void kick_unregister_func(struct kick_irq_handler *kh) 57{ 58 unsigned long flags; 59 60 spin_lock_irqsave(&kick_handlers_lock, flags); 61 62 list_del(&kh->list); 63 64 spin_unlock_irqrestore(&kick_handlers_lock, flags); 65} 66EXPORT_SYMBOL(kick_unregister_func); 67 68TBIRES 69kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI) 70{ 71 struct pt_regs *old_regs; 72 struct kick_irq_handler *kh; 73 struct list_head *lh; 74 int handled = 0; 75 TBIRES ret; 76 77 head_end(State, ~INTS_OFF_MASK); 78 79 /* If we interrupted user code handle any critical sections. */ 80 if (State.Sig.SaveMask & TBICTX_PRIV_BIT) 81 restart_critical_section(State); 82 83 trace_hardirqs_off(); 84 85 old_regs = set_irq_regs((struct pt_regs *)State.Sig.pCtx); 86 irq_enter(); 87 88 /* 89 * There is no need to disable interrupts here because we 90 * can't nest KICK interrupts in a KICK interrupt handler. 91 */ 92 spin_lock(&kick_handlers_lock); 93 94 list_for_each(lh, &kick_handlers_list) { 95 kh = list_entry(lh, struct kick_irq_handler, list); 96 97 ret = kh->func(State, SigNum, Triggers, Inst, pTBI, &handled); 98 if (handled) 99 break; 100 } 101 102 spin_unlock(&kick_handlers_lock); 103 104 WARN_ON(!handled); 105 106 irq_exit(); 107 set_irq_regs(old_regs); 108 109 return tail_end(ret); 110} 111