]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/metag/kernel/kick.c
Merge tag 'metag-v3.9-rc1-v4' of git://git.kernel.org/pub/scm/linux/kernel/git/jhogan...
[karo-tx-linux.git] / arch / metag / kernel / kick.c
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/kernel.h>
30 #include <linux/mm.h>
31 #include <linux/types.h>
32
33 #include <asm/traps.h>
34
35 /*
36  * All accesses/manipulations of kick_handlers_list should be
37  * performed while holding kick_handlers_lock.
38  */
39 static DEFINE_SPINLOCK(kick_handlers_lock);
40 static LIST_HEAD(kick_handlers_list);
41
42 void kick_register_func(struct kick_irq_handler *kh)
43 {
44         unsigned long flags;
45
46         spin_lock_irqsave(&kick_handlers_lock, flags);
47
48         list_add_tail(&kh->list, &kick_handlers_list);
49
50         spin_unlock_irqrestore(&kick_handlers_lock, flags);
51 }
52 EXPORT_SYMBOL(kick_register_func);
53
54 void kick_unregister_func(struct kick_irq_handler *kh)
55 {
56         unsigned long flags;
57
58         spin_lock_irqsave(&kick_handlers_lock, flags);
59
60         list_del(&kh->list);
61
62         spin_unlock_irqrestore(&kick_handlers_lock, flags);
63 }
64 EXPORT_SYMBOL(kick_unregister_func);
65
66 TBIRES
67 kick_handler(TBIRES State, int SigNum, int Triggers, int Inst, PTBI pTBI)
68 {
69         struct kick_irq_handler *kh;
70         struct list_head *lh;
71         int handled = 0;
72         TBIRES ret;
73
74         head_end(State, ~INTS_OFF_MASK);
75
76         /* If we interrupted user code handle any critical sections. */
77         if (State.Sig.SaveMask & TBICTX_PRIV_BIT)
78                 restart_critical_section(State);
79
80         trace_hardirqs_off();
81
82         /*
83          * There is no need to disable interrupts here because we
84          * can't nest KICK interrupts in a KICK interrupt handler.
85          */
86         spin_lock(&kick_handlers_lock);
87
88         list_for_each(lh, &kick_handlers_list) {
89                 kh = list_entry(lh, struct kick_irq_handler, list);
90
91                 ret = kh->func(State, SigNum, Triggers, Inst, pTBI, &handled);
92                 if (handled)
93                         break;
94         }
95
96         spin_unlock(&kick_handlers_lock);
97
98         WARN_ON(!handled);
99
100         return tail_end(ret);
101 }