linux/arch/metag/kernel/kick.c
<<
>>
Prefs
   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